@ahmed-mili/capacitor-thunder-bg-service
v0.1.31
Published
Capacitor 7 plugin - Android foreground service with notification, location tracking, and background tasks.
Downloads
65
Maintainers
Readme
@ahmed-mili/capacitor-thunder-bg-service
Plugin Capacitor 7 pour Android qui fournit un service foreground avec notifications, localisation, et gestion de tâches en arrière-plan. Fonctionne même quand l'app est fermée.
✨ Fonctionnalités principales
- UI 100% dynamique depuis l'app : Injection complète de layouts, textes et boutons depuis votre application
- Boutons cliquables dans la notification : Boutons interactifs reliés à vos BroadcastReceiver
- Persistance d'état : L'UI et l'état persistent même après fermeture/réouverture de l'app
- Tâches en arrière-plan : Exécution de code Java même si l'app est fermée
- Localisation : Suivi GPS en arrière-plan
- Aucune UI/logique par défaut : Le plugin n'affiche que ce que vous envoyez depuis l'app
📋 Table des matières
- Installation
- Configuration
- Utilisation de base
- Notifications personnalisées
- UI Dynamique 100% App-Driven
- Tâches en arrière-plan
- Localisation
- Utilisation depuis Java natif
- API complète
- Architecture
- Exemples
- Dépannage
🚀 Installation
1. Installer le package
npm install @ahmed-mili/capacitor-thunder-bg-service2. Synchroniser avec Capacitor
npx cap sync android3. Permissions Android
Le plugin nécessite les permissions suivantes (déjà incluses dans le plugin) :
FOREGROUND_SERVICEFOREGROUND_SERVICE_LOCATIONPOST_NOTIFICATIONSACCESS_FINE_LOCATIONACCESS_COARSE_LOCATIONINTERNETWAKE_LOCK
4. Configuration des ressources Android (Requis)
Le plugin ne contient aucune UI par défaut. Vous devez créer vos propres layouts dans votre app.
Layout de notification (android/app/src/main/res/layout/notification_online.xml - exemple) :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
<TextView
android:id="@+id/txtDriverStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="En ligne"
android:textStyle="bold"
android:textSize="16sp" />
<TextView
android:id="@+id/txtWaiting"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="En attente"
android:textSize="14sp" />
<Button
android:id="@+id/btnAction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Action" />
</LinearLayout>Icône de notification (android/app/src/main/res/drawable/ic_notification.xml) :
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</vector>Important : Créez vos layouts dans android/app/src/main/res/layout/ de votre app. Le plugin utilisera uniquement les layouts que vous spécifiez via customLayout.
5. Configuration minimale
Aucune autre configuration n'est requise. Le plugin est prêt à l'emploi.
⚙️ Configuration
Permissions Runtime (Android 6+)
Vous devez demander les permissions à l'utilisateur :
import { Permissions } from '@capacitor/core';
async function requestPermissions() {
const permissions = await Permissions.request([
Permissions.ANDROID.POST_NOTIFICATIONS,
Permissions.ANDROID.ACCESS_FINE_LOCATION,
Permissions.ANDROID.ACCESS_COARSE_LOCATION,
]);
return permissions;
}🎯 Utilisation de base
1. Démarrer le service
Important : Vous devez toujours fournir customLayout car le plugin n'a pas de UI par défaut.
import { ThunderBgService } from '@ahmed-mili/capacitor-thunder-bg-service';
// Démarrer avec un layout personnalisé
await ThunderBgService.start({
customLayout: 'notification_online', // REQUIS : nom de votre layout XML
titleViewId: 'txtDriverStatus', // ID du TextView pour le titre
subtitleViewId: 'txtWaiting', // ID du TextView pour le sous-titre
notificationTitle: 'Online',
notificationSubtitle: 'Service actif',
enableLocation: true,
soundsEnabled: false,
});2. Mettre à jour la notification
// Mettre à jour le contenu de la notification
await ThunderBgService.update({
notificationTitle: 'En cours',
notificationSubtitle: 'Traitement des données',
});3. Arrêter le service
// Arrêter le service et toutes les tâches
await ThunderBgService.stop();4. Exemple complet dans un service Angular
import { Injectable } from '@angular/core';
import { ThunderBgService } from '@ahmed-mili/capacitor-thunder-bg-service';
@Injectable({
providedIn: 'root'
})
export class BackgroundService {
async startService() {
try {
await ThunderBgService.start({
notificationTitle: 'Online',
notificationSubtitle: 'Service actif',
enableLocation: true,
customLayout: 'notification_online', // REQUIS : layout personnalisé
titleViewId: 'txtTitle',
subtitleViewId: 'txtSubtitle',
});
console.log('Service démarré');
} catch (error) {
console.error('Erreur:', error);
}
}
async updateStatus(status: string) {
await ThunderBgService.update({
notificationSubtitle: status,
});
}
async stopService() {
await ThunderBgService.stop();
}
}🎨 Notifications personnalisées
1. Layout personnalisé (Requis)
⚠️ Important : Le plugin n'a pas de layout par défaut. Vous devez créer votre propre layout XML dans votre application.
Le plugin peut utiliser un layout de fallback si aucun customLayout n'est fourni, mais il est fortement recommandé de toujours fournir un customLayout pour un contrôle total de l'UI.
2. Créer votre propre layout
Créez un fichier XML dans android/app/src/main/res/layout/ :
<!-- notification_custom.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
<TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title"
android:textStyle="bold"
android:textSize="16sp"
android:textColor="#000000" />
<TextView
android:id="@+id/txtSubtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Subtitle"
android:textSize="14sp"
android:textColor="#444444" />
<TextView
android:id="@+id/txtTimer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="00:00:00"
android:textSize="13sp"
android:textColor="#1B5E20" />
</LinearLayout>3. Utiliser le layout personnalisé
// Démarrer avec un layout personnalisé
await ThunderBgService.start({
notificationTitle: 'Online',
notificationSubtitle: 'En attente',
enableLocation: true,
customLayout: 'notification_custom', // Nom du layout (sans .xml)
titleViewId: 'txtTitle', // ID du TextView pour le titre
subtitleViewId: 'txtSubtitle', // ID du TextView pour le sous-titre
timerViewId: 'txtTimer', // ID du TextView pour le timer
});4. Changer de layout dynamiquement
// Changer de layout sans redémarrer le service
await ThunderBgService.update({
notificationTitle: 'Nouveau titre',
notificationSubtitle: 'Nouveau sous-titre',
customLayout: 'notification_other', // Nouveau layout
titleViewId: 'txtOtherTitle', // Nouveaux IDs
subtitleViewId: 'txtOtherSubtitle',
timerViewId: 'txtOtherTimer',
});5. Exemple : Layouts multiples selon l'état
enum AppState {
OFFLINE = 'offline',
ONLINE = 'online',
ON_RIDE = 'on_ride',
ARRIVED = 'arrived',
}
class NotificationManager {
async switchToState(state: AppState) {
const configs = {
[AppState.OFFLINE]: {
title: 'Hors ligne',
subtitle: 'Service arrêté',
layout: 'notification_offline',
titleId: 'txtStatus',
subtitleId: 'txtMessage',
},
[AppState.ONLINE]: {
title: 'En ligne',
subtitle: 'En attente de courses',
layout: 'notification_online',
titleId: 'txtDriverStatus',
subtitleId: 'txtWaiting',
},
[AppState.ON_RIDE]: {
title: 'En course',
subtitle: 'Direction destination',
layout: 'notification_riding',
titleId: 'txtRideStatus',
subtitleId: 'txtDestination',
},
[AppState.ARRIVED]: {
title: 'Arrivé',
subtitle: 'À destination',
layout: 'notification_arrived',
titleId: 'txtArrivalStatus',
subtitleId: 'txtLocation',
},
};
const config = configs[state];
await ThunderBgService.update({
notificationTitle: config.title,
notificationSubtitle: config.subtitle,
customLayout: config.layout,
titleViewId: config.titleId,
subtitleViewId: config.subtitleId,
});
}
}🎨 UI Dynamique 100% App-Driven
Le plugin ne contient aucune UI ou logique par défaut. Tout doit être fourni depuis votre application.
1. Injection dynamique de textes (viewData)
Utilisez viewData pour mettre à jour n'importe quel TextView de votre layout :
await ThunderBgService.update({
customLayout: 'notification_online',
viewData: {
txtDriverStatus: 'En ligne',
txtWaiting: 'En attente de courses',
txtTimer: '00:05:23',
// Ajoutez autant de TextViews que vous voulez
},
});Important : Les IDs dans viewData doivent correspondre exactement aux IDs de votre XML (sans @+id/).
2. Boutons cliquables dans la notification (buttons)
Créez des boutons interactifs reliés à vos BroadcastReceiver :
await ThunderBgService.update({
customLayout: 'notification_stepper',
buttons: [
{
viewId: 'btnPrev', // ID du Button/TextView dans votre XML
action: 'com.yourapp.ACTION_STEPPER_PREV', // Action broadcast
},
{
viewId: 'btnNext',
action: 'com.yourapp.ACTION_STEPPER_NEXT',
},
{
viewId: 'btnDone',
action: 'com.yourapp.ACTION_ONLINE',
extras: { // Optionnel : données supplémentaires
step: '3',
status: 'completed',
},
},
],
});⚠️ Important : Les boutons doivent être re-fournis à chaque appel à update() car les instances de RemoteViews sont recréées. Si vous omettez buttons dans un update(), les boutons perdront leurs bindings de clics.
3. Configuration du BroadcastReceiver
Dans votre AndroidManifest.xml :
<receiver
android:name=".NotifActionReceiver"
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="com.yourapp.ACTION_STEPPER_PREV"/>
<action android:name="com.yourapp.ACTION_STEPPER_NEXT"/>
<action android:name="com.yourapp.ACTION_ONLINE"/>
<!-- Ajoutez toutes vos actions -->
</intent-filter>
</receiver>Dans votre NotifActionReceiver.java :
public class NotifActionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ("com.yourapp.ACTION_STEPPER_PREV".equals(action)) {
// Votre logique
} else if ("com.yourapp.ACTION_STEPPER_NEXT".equals(action)) {
// Votre logique
}
}
}4. Exemple complet : Page stepper avec boutons
Layout XML (notification_stepper.xml) :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
<TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Étapes"
android:textStyle="bold" />
<TextView
android:id="@+id/txtCurrentStep"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Étape actuelle: 1/3" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnPrev"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Précédent" />
<Button
android:id="@+id/btnNext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Suivant" />
</LinearLayout>
</LinearLayout>TypeScript :
// Démarrer avec page stepper
await ThunderBgService.start({
customLayout: 'notification_stepper',
titleViewId: 'txtTitle',
subtitleViewId: 'txtCurrentStep',
viewData: {
txtTitle: 'Étapes',
txtCurrentStep: 'Étape actuelle: 1/3',
},
buttons: [
{ viewId: 'btnPrev', action: 'com.yourapp.ACTION_STEPPER_PREV' },
{ viewId: 'btnNext', action: 'com.yourapp.ACTION_STEPPER_NEXT' },
],
});
// Mettre à jour dynamiquement
await ThunderBgService.update({
viewData: {
txtCurrentStep: 'Étape actuelle: 2/3',
},
});Java Helper (dans votre app) :
public class NotificationDynamicHelper {
private int currentStep = 1;
public void stepNext() {
currentStep = Math.min(3, currentStep + 1);
updateStepperUI();
}
private void updateStepperUI() {
Intent extras = new Intent();
extras.putExtra(FgConstants.EXTRA_CUSTOM_LAYOUT, "notification_stepper");
extras.putExtra(FgConstants.EXTRA_TITLE_VIEW_ID, "txtTitle");
extras.putExtra(FgConstants.EXTRA_SUBTITLE_VIEW_ID, "txtCurrentStep");
JSONObject viewData = new JSONObject();
viewData.put("txtTitle", "Étapes");
viewData.put("txtCurrentStep", "Étape actuelle: " + currentStep + "/3");
extras.putExtra(FgConstants.EXTRA_VIEW_DATA_JSON, viewData.toString());
JSONArray buttons = new JSONArray();
buttons.put(new JSONObject().put("viewId", "btnPrev").put("action", "com.yourapp.ACTION_STEPPER_PREV"));
buttons.put(new JSONObject().put("viewId", "btnNext").put("action", "com.yourapp.ACTION_STEPPER_NEXT"));
extras.putExtra(FgConstants.EXTRA_BUTTONS_JSON, buttons.toString());
ForegroundTaskService.startAction(context, FgConstants.ACTION_UPDATE, extras);
}
}5. Persistance automatique
Le plugin sauvegarde automatiquement :
- Le layout actuel (
customLayout) - Les IDs de vues (
titleViewId,subtitleViewId,timerViewId) - Les données dynamiques (
viewData) - Les boutons (
buttons)
Quand vous fermez et rouvrez l'app, l'UI est automatiquement restaurée à l'état précédent.
6. Notes importantes
- Aucun layout par défaut : Vous devez toujours fournir
customLayout - IDs exacts : Les IDs dans
viewDataetbuttonsdoivent correspondre exactement à votre XML - Boutons cliquables : Utilisez
ButtonouTextViewavecandroid:clickable="true" - Receiver exporté :
android:exported="true"est obligatoire sur Android 12+
🔄 Tâches en arrière-plan
1. Concept
Les tâches en arrière-plan sont des fonctions Java qui s'exécutent périodiquement même si l'app est fermée. Elles tournent dans le service foreground.
2. Créer une tâche
Créez une classe Java dans votre app qui implémente BackgroundTask :
package com.yourpackage;
import android.content.Context;
import android.util.Log;
import com.webify.thunderbgservice.tasks.BackgroundTask;
import com.webify.thunderbgservice.tasks.TaskResultStorage;
import com.webify.thunderbgservice.tasks.TaskEventEmitter;
import org.json.JSONObject;
public class MySyncTask implements BackgroundTask {
private static final String TAG = "MySyncTask";
private int syncCount = 0;
@Override
public void execute(Context context, String taskId) {
syncCount++;
Log.i(TAG, "Sync #" + syncCount);
// 1. Stocker des données pour JS
TaskResultStorage.saveResult(context, taskId, "syncCount", String.valueOf(syncCount));
TaskResultStorage.saveResult(context, taskId, "lastSync", System.currentTimeMillis() + "");
// 2. Émettre un événement (si l'app est active)
TaskEventEmitter.emit(context, taskId, "Sync completed: " + syncCount);
// 3. Votre logique métier ici
performSync(context);
}
@Override
public void onRegistered(Context context, String taskId) {
Log.i(TAG, "Task registered: " + taskId);
// Initialisation si nécessaire
}
@Override
public void onUnregistered(Context context, String taskId) {
Log.i(TAG, "Task unregistered: " + taskId);
// Nettoyage si nécessaire
}
private void performSync(Context context) {
// Votre code de synchronisation
// Ex: appeler une API, sauvegarder des données, etc.
}
}3. Enregistrer une tâche depuis TypeScript
import { ThunderBgService } from '@ahmed-mili/capacitor-thunder-bg-service';
// Démarrer le service d'abord
await ThunderBgService.start({
notificationTitle: 'Online',
notificationSubtitle: 'Service actif',
enableLocation: true,
});
// Enregistrer une tâche
await ThunderBgService.registerTask({
taskId: 'syncTask',
taskClass: 'com.yourpackage.MySyncTask', // Nom complet de la classe
intervalMs: 10000, // Toutes les 10 secondes (minimum 1000ms)
});4. Écouter les événements de la tâche
// Écouter les événements (seulement si l'app est active)
ThunderBgService.addListener('taskEvent', (data) => {
console.log('Événement de la tâche:', data.taskId);
console.log('Données:', data.data);
console.log('Timestamp:', data.timestamp);
// Traiter les données
if (data.taskId === 'syncTask') {
handleSyncEvent(data.data);
}
});5. Récupérer les résultats stockés
// Récupérer les résultats même si l'app était fermée
async function getTaskResults() {
const { result } = await ThunderBgService.getTaskResult('syncTask');
if (result) {
console.log('Sync count:', result.syncCount);
console.log('Last sync:', result.lastSync);
console.log('Timestamp:', result.timestamp);
}
}
// Polling périodique
setInterval(async () => {
const { result } = await ThunderBgService.getTaskResult('syncTask');
if (result) {
updateUI(result);
}
}, 5000);6. Désenregistrer une tâche
// Désenregistrer une tâche
await ThunderBgService.unregisterTask('syncTask');7. Exemples de tâches
Exemple 1: Vérification réseau
public class NetworkCheckTask implements BackgroundTask {
@Override
public void execute(Context context, String taskId) {
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
boolean isConnected = info != null && info.isConnected();
TaskResultStorage.saveResult(context, taskId, "connected", String.valueOf(isConnected));
if (!isConnected) {
TaskEventEmitter.emit(context, taskId, "Network disconnected");
}
}
}Exemple 2: Mise à jour de localisation
public class LocationUpdateTask implements BackgroundTask {
@Override
public void execute(Context context, String taskId) {
// Récupérer la localisation
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
JSONObject data = new JSONObject();
data.put("latitude", location.getLatitude());
data.put("longitude", location.getLongitude());
data.put("timestamp", System.currentTimeMillis());
TaskResultStorage.saveResult(context, taskId, data);
TaskEventEmitter.emit(context, taskId, data);
}
}
}Exemple 3: Synchronisation de données
public class DataSyncTask implements BackgroundTask {
@Override
public void execute(Context context, String taskId) {
try {
// Appeler votre API
String response = callApi(context);
// Sauvegarder la réponse
TaskResultStorage.saveResult(context, taskId, "lastResponse", response);
TaskResultStorage.saveResult(context, taskId, "syncStatus", "success");
TaskEventEmitter.emit(context, taskId, "Sync successful");
} catch (Exception e) {
TaskResultStorage.saveResult(context, taskId, "syncStatus", "error");
TaskResultStorage.saveResult(context, taskId, "error", e.getMessage());
}
}
private String callApi(Context context) {
// Votre code HTTP ici
return "";
}
}📍 Localisation
1. Activation automatique
La localisation est activée automatiquement si enableLocation: true :
await ThunderBgService.start({
notificationTitle: 'Online',
notificationSubtitle: 'Service actif',
enableLocation: true, // Active la localisation
customLayout: 'notification_online', // REQUIS : layout personnalisé
titleViewId: 'txtTitle',
subtitleViewId: 'txtSubtitle',
});2. Utilisation dans une tâche
Vous pouvez accéder à la localisation depuis vos tâches :
public class LocationTask implements BackgroundTask {
@Override
public void execute(Context context, String taskId) {
FusedLocationProviderClient fused =
LocationServices.getFusedLocationProviderClient(context);
fused.getLastLocation().addOnSuccessListener(location -> {
if (location != null) {
JSONObject data = new JSONObject();
data.put("lat", location.getLatitude());
data.put("lng", location.getLongitude());
TaskResultStorage.saveResult(context, taskId, data);
}
});
}
}☕ Utilisation depuis Java natif
1. Import
import com.webify.thunderbgservice.core.ThunderBgServiceHelper;2. Démarrer le service
// Méthode simple
ThunderBgServiceHelper.startService(
context,
"Online",
"En attente",
true // enableLocation
);
// Avec tous les paramètres
ThunderBgServiceHelper.startService(
context,
"Online",
"En attente",
true, // enableLocation
false, // soundsEnabled
"notification_custom", // customLayout
"txtTitle", // titleViewId
"txtSubtitle", // subtitleViewId
"txtTimer" // timerViewId
);3. Mettre à jour la notification
// Simple
ThunderBgServiceHelper.updateNotification(
context,
"Nouveau titre",
"Nouveau sous-titre"
);
// Avec changement de layout
ThunderBgServiceHelper.updateNotification(
context,
"Titre",
"Sous-titre",
"notification_other", // customLayout
"txtOtherTitle", // titleViewId
"txtOtherSubtitle", // subtitleViewId
"txtOtherTimer" // timerViewId
);4. Arrêter le service
ThunderBgServiceHelper.stopService(context);5. Enregistrer une tâche
// Avec une instance
MyTask task = new MyTask();
ThunderBgServiceHelper.registerTask(context, "myTask", task, 5000);
// Par nom de classe
ThunderBgServiceHelper.registerTask(
context,
"myTask",
"com.yourpackage.MyTask",
5000
);6. Désenregistrer une tâche
ThunderBgServiceHelper.unregisterTask(context, "myTask");7. Vérifier si une tâche est enregistrée
boolean isRegistered = ThunderBgServiceHelper.isTaskRegistered("myTask");8. Récupérer les résultats d'une tâche
JSONObject result = ThunderBgServiceHelper.getTaskResult(context, "myTask");
if (result != null) {
String data = result.getString("data");
}9. Émettre un événement
ThunderBgServiceHelper.emitTaskEvent(context, "myTask", "Données");10. Vérifier l'existence d'un layout
boolean exists = ThunderBgServiceHelper.layoutExists(context, "notification_custom");11. Exemple dans une Activity
package com.yourpackage;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
import com.webify.thunderbgservice.core.ThunderBgServiceHelper;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button startBtn = findViewById(R.id.btnStart);
startBtn.setOnClickListener(v -> {
ThunderBgServiceHelper.startService(
this,
"Online",
"Service démarré",
true
);
});
Button stopBtn = findViewById(R.id.btnStop);
stopBtn.setOnClickListener(v -> {
ThunderBgServiceHelper.stopService(this);
});
}
}📚 API complète
TypeScript/JavaScript
start(options: StartOptions): Promise<{started: boolean}>
Démarre le service foreground.
Options:
notificationTitle?: string- Titre de la notification (optionnel)notificationSubtitle?: string- Sous-titre (optionnel)enableLocation?: boolean- Activer la localisation (défaut: true)soundsEnabled?: boolean- Activer les sons (défaut: false)customLayout?: string- Requis : Nom du layout personnalisé (sans .xml)titleViewId?: string- ID du TextView pour le titresubtitleViewId?: string- ID du TextView pour le sous-titretimerViewId?: string- ID du TextView pour le timerviewData?: { [viewIdName: string]: string }- Nouveau : Objet pour injecter des textes dans n'importe quel TextViewbuttons?: Array<{ viewId: string; action: string; extras?: object }>- Nouveau : Tableau de boutons cliquables
stop(): Promise<{stopped: boolean}>
Arrête le service et toutes les tâches.
update(options: Partial<StartOptions>): Promise<{updated: boolean}>
Met à jour la notification. Tous les paramètres sont optionnels.
registerTask(options: RegisterTaskOptions): Promise<{registered: boolean}>
Enregistre une tâche en arrière-plan.
Options:
taskId: string- ID unique de la tâchetaskClass: string- Nom complet de la classe JavaintervalMs: number- Intervalle en millisecondes (minimum 1000)
unregisterTask(taskId: string): Promise<{unregistered: boolean}>
Désenregistre une tâche.
getTaskResult(taskId: string): Promise<{result: any | null}>
Récupère les résultats stockés d'une tâche.
addListener(event: 'taskEvent', listener: Function): Promise<{remove: () => void}>
Écoute les événements émis par les tâches.
removeAllListeners(): Promise<void>
Supprime tous les listeners.
Java natif
Voir la section "Utilisation depuis Java natif" ci-dessus pour la liste complète des méthodes.
🏗️ Architecture
Structure du package
com.webify.thunderbgservice/
├── core/ # Fichiers principaux
│ ├── FgConstants.java # Constantes
│ ├── ForegroundTaskService.java # Service Android
│ ├── ThunderBgServicePlugin.java # Plugin Capacitor
│ └── ThunderBgServiceHelper.java # Helper publique
├── tasks/ # Tâches en arrière-plan
│ ├── BackgroundTask.java # Interface
│ ├── BackgroundTaskManager.java # Gestionnaire
│ ├── TaskEventEmitter.java # Émission d'événements
│ └── TaskResultStorage.java # Stockage de résultats
└── helpers/ # Utilitaires
├── NotificationHelper.java # Gestion notifications
└── LocationHelper.java # Gestion localisationFlux de données
┌─────────────┐
│ App JS │
│ (TypeScript)│
└──────┬──────┘
│
│ Capacitor Bridge
│
┌──────▼──────────────────────┐
│ ThunderBgServicePlugin │
│ (Capacitor Plugin) │
└──────┬──────────────────────┘
│
│ Intents
│
┌──────▼──────────────────────┐
│ ForegroundTaskService │
│ (Android Service) │
└──────┬──────────────────────┘
│
├──► NotificationHelper
├──► LocationHelper
└──► BackgroundTaskManager
│
└──► BackgroundTask (votre code)💡 Exemples
Exemple 1: Service de livraison
class DeliveryService {
async startDelivery() {
// Démarrer le service
await ThunderBgService.start({
notificationTitle: 'Livraison en cours',
notificationSubtitle: 'En route vers le client',
enableLocation: true,
customLayout: 'notification_delivery',
titleViewId: 'txtDeliveryStatus',
subtitleViewId: 'txtClientAddress',
timerViewId: 'txtElapsedTime',
});
// Enregistrer une tâche de mise à jour de localisation
await ThunderBgService.registerTask({
taskId: 'locationUpdate',
taskClass: 'com.yourpackage.DeliveryLocationTask',
intervalMs: 5000,
});
}
async updateDeliveryStatus(status: string, address: string) {
await ThunderBgService.update({
notificationTitle: status,
notificationSubtitle: address,
});
}
async stopDelivery() {
await ThunderBgService.unregisterTask('locationUpdate');
await ThunderBgService.stop();
}
}Exemple 2: Application de fitness
class FitnessTracker {
async startWorkout() {
await ThunderBgService.start({
notificationTitle: 'Entraînement en cours',
notificationSubtitle: 'Course',
enableLocation: true,
customLayout: 'notification_workout',
titleViewId: 'txtWorkoutType',
subtitleViewId: 'txtDistance',
timerViewId: 'txtDuration',
});
// Enregistrer une tâche de tracking
await ThunderBgService.registerTask({
taskId: 'fitnessTracking',
taskClass: 'com.yourpackage.FitnessTrackingTask',
intervalMs: 2000,
});
}
async updateWorkoutData(distance: string) {
await ThunderBgService.update({
notificationSubtitle: `Distance: ${distance} km`,
});
}
}Exemple 3: Application de monitoring
class MonitoringService {
async startMonitoring() {
await ThunderBgService.start({
notificationTitle: 'Monitoring actif',
notificationSubtitle: 'Surveillance en cours',
enableLocation: false,
});
// Tâche de vérification système
await ThunderBgService.registerTask({
taskId: 'systemCheck',
taskClass: 'com.yourpackage.SystemCheckTask',
intervalMs: 30000,
});
// Écouter les événements
ThunderBgService.addListener('taskEvent', (data) => {
if (data.taskId === 'systemCheck') {
this.handleSystemAlert(data.data);
}
});
}
private handleSystemAlert(data: any) {
// Traiter les alertes système
console.log('Alert:', data);
}
async getSystemStatus() {
const { result } = await ThunderBgService.getTaskResult('systemCheck');
return result;
}
}🔧 Dépannage
Le service s'arrête quand l'app est fermée
✅ Solution: Le service est conçu pour persister. Vérifiez :
- Les permissions sont accordées
- Le service est bien démarré avec
start() - Aucune restriction de batterie n'est active
La notification ne s'affiche pas
✅ Solution:
- Vérifiez que la permission
POST_NOTIFICATIONSest accordée (Android 13+) - Vérifiez que le canal de notification existe
- Vérifiez que le service est bien démarré
Les tâches ne s'exécutent pas
✅ Solution:
- Vérifiez que le service est démarré avant d'enregistrer les tâches
- Vérifiez que la classe Java existe et implémente
BackgroundTask - Vérifiez les logs Android (logcat) pour les erreurs
Les événements ne sont pas reçus
✅ Solution:
- Les événements ne sont émis que si l'app est active
- Utilisez
getTaskResult()pour récupérer les données si l'app était fermée - Vérifiez que le listener est bien enregistré
Le layout personnalisé ne s'affiche pas
✅ Solution:
- Vérifiez que le fichier XML existe dans
res/layout/ - Vérifiez que les IDs des TextViews sont corrects
- Vérifiez les logs pour voir les IDs résolus
- Important : Le plugin ne contient pas de layout par défaut. Vous devez toujours fournir
customLayout
Les boutons dans la notification ne fonctionnent pas
✅ Solution:
- Vérifiez que le
BroadcastReceiverest déclaré dansAndroidManifest.xmlavecandroid:exported="true" - Vérifiez que les actions dans
buttonscorrespondent exactement aux actions déclarées dans le Receiver - Vérifiez que les IDs de boutons dans
buttonscorrespondent exactement aux IDs dans votre XML (sans@+id/) - Utilisez
ButtonouTextViewavecandroid:clickable="true"dans votre layout - Vérifiez les logs Logcat (filtre
ThunderBG) pour voir si les boutons sont bindés : cherchez"Button bound: viewId=..."ou"⚠️ NO RECEIVER FOUND" - Vérifiez les logs de votre Receiver pour voir si les intents sont reçus : ajoutez
Log.d("Receiver", "Received: " + intent.getAction())
Erreur de compilation Java
✅ Solution:
- Vérifiez que tous les imports sont corrects
- Utilisez
com.webify.thunderbgservice.core.*pour les classes core - Utilisez
com.webify.thunderbgservice.tasks.*pour les tâches
📖 Ressources supplémentaires
Documentation
- 📘 Guide de démarrage rapide - Commencez en 5 minutes
- 📚 Référence API complète - Toutes les méthodes détaillées
- 💡 Cas d'usage pratiques - Exemples réels d'utilisation
- 🏗️ Architecture et organisation - Structure du package
Exemples
- 📁 Index des exemples - Guide de navigation
- 📝 Exemples TypeScript/JavaScript
- ☕ Exemples Java natif
- 🔄 Tâches en arrière-plan
- 📡 Communication JS/Java
Guides
📝 Notes importantes
- UI 100% app-driven: Le plugin ne contient aucune UI/logique par défaut. Vous devez fournir
customLayout,viewDataetbuttonsdepuis votre app. - Layout requis: Vous devez toujours fournir
customLayoutlors du démarrage. Créez vos layouts dansres/layout/de votre app. - Persistance automatique: L'UI et l'état (layout, viewData, buttons) sont automatiquement sauvegardés et restaurés après fermeture/réouverture de l'app.
- Batterie: Les intervalles courts peuvent drainer la batterie. Utilisez des intervalles >= 5000ms pour les tâches.
- Permissions: Toujours demander les permissions runtime avant d'utiliser le service.
- Tâches: Les tâches doivent être en Java natif, pas en JavaScript.
- Boutons cliquables: Utilisez
ButtonouTextViewavecandroid:clickable="true"dans vos layouts. Déclarez leBroadcastReceiveravecandroid:exported="true"dans le Manifest.
🆘 Support
Pour toute question ou problème, consultez les exemples dans le dossier examples/ ou ouvrez une issue sur le repository.
Version: 0.1.0
Capacitor: ^7.0.0
Android: API 21+
