Application Android + WearOS pour la collecte multi-capteurs haute frequence depuis un smartphone et une montre connectee, avec streaming temps reel, gestion de scenarios et export Google Drive.
WearableSensor/
├── app/ # Application telephone (minSdk 23, targetSdk 35)
├── wear/ # Application montre WearOS (minSdk 30, targetSdk 34)
└── shared/ # Module partage (capteurs, communication, modeles)
Les deux appareils communiquent via la Wear Data Layer API (MessageClient + DataClient).
┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ MONTRE (WearOS) │ │ TELEPHONE (Android) │
│ │ │ │
│ ┌───────────────────────────────┐ │ │ ┌───────────────────────────────┐ │
│ │ Capteurs Hardware │ │ │ │ Capteurs Hardware │ │
│ │ Accelerometre, Gyroscope, │ │ │ │ Accelerometre, Gyroscope, │ │
│ │ Magnetometre, Pression... │ │ │ │ Lumiere, Proximite... │ │
│ └──────────┬────────────────────┘ │ │ └──────────┬────────────────────┘ │
│ │ │ │ │ │
│ ┌──────────┴────────────────────┐ │ │ ┌──────────┴────────────────────┐ │
│ │ Capteurs Virtuels │ │ │ │ Capteurs Virtuels │ │
│ │ Microphone, GPS │ │ │ │ Microphone, GPS │ │
│ └──────────┬────────────────────┘ │ │ └──────────┬────────────────────┘ │
│ │ │ │ │ │
│ ┌──────────┴────────────────────┐ │ │ ┌──────────┴────────────────────┐ │
│ │ Health Services │ │ │ │ │ │
│ │ MeasureClient (HR BPM) │ │ │ │ SensorDataFlow │ │
│ └──────────┬────────────────────┘ │ │ │ (SharedFlow) │ │
│ │ │ │ └──────────┬────────────────────┘ │
│ ┌──────────┴────────────────────┐ │ │ │ │
│ │ SensorDataFlow │ │ │ ┌──────────┴────────────────────┐ │
│ │ (SharedFlow) │ │ │ │ SensorDataProcess │ │
│ └──────────┬────────────────────┘ │ │ │ Queue → .gz files │ │
│ │ │ │ └──────────┬────────────────────┘ │
│ ┌──────────┴────────────────────┐ │ │ │ │
│ │ SensorDataProcess │ │ │ ┌──────────┴────────────────────┐ │
│ │ Queue → .gz files │ │ │ │ PhoneDataProcessingWorker │ │
│ └──────────┬────────────────────┘ │ │ │ .gz → phone_*.csv │ │
│ │ │ │ └──────────┬────────────────────┘ │
│ ▼ │ │ │ │
│ ┌───────────────────────────────┐ │ │ │ │
│ │ WearDataProcessingWorker │ │ │ │ │
│ │ Envoi .gz par .gz │ │ │ │ │
│ └──────────┬────────────────────┘ │ │ │ │
│ │ │ │ │ │
└─────────────┼───────────────────────┘ └─────────────┼──────────────────────┘
│ │
│ Wear Data Layer API │
│ (Bluetooth / Wi-Fi / Cloud) │
│ │
▼ │
┌─────────────────────────────────────────────────────────────────────────────────┐
│ HybridCommunicationHelper │
│ │
│ ┌─────────────────────────────┐ ┌────────────────────────────────────────┐ │
│ │ MessageClient │ │ DataClient │ │
│ │ │ │ │ │
│ │ • Commandes (start/stop/ │ │ • Fichiers .gz (1 Asset par fichier) │ │
│ │ pause/resume) │ │ • ~50-200 KB par transfert │ │
│ │ • Config capteurs │ │ • Streaming anti-OOM │ │
│ │ • Stream temps reel 10Hz │ │ • Legacy : chunks 256 KB │ │
│ │ • Statut du service │ │ │ │
│ │ • Liste des capteurs │ │ │ │
│ └─────────────────────────────┘ └────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ PhoneHandlerUtil (reception) │
│ │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ processGzFile() │ │
│ │ decompress .gz → parse JSON → CustomCSV.appendData(batch, WATCH) │ │
│ │ → watch_*.csv (pipeline isole) │ │
│ └──────────┬────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ scheduleFinalizeUpload() │ │
│ │ Timer 5s apres dernier .gz → CustomCSV.finalizeAndUpload(WATCH) │ │
│ └──────────┬────────────────────────────────────────────────────────────────┘ │
│ │ │
└─────────────┼───────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ CustomCSV (pipelines isoles) │
│ │
│ ┌─────────────────────────────┐ ┌────────────────────────────────────────┐ │
│ │ Pipeline PHONE │ │ Pipeline WATCH │ │
│ │ Lock propre │ │ Lock propre │ │
│ │ phone_P001_session1.csv │ │ watch_P001_session1.csv │ │
│ │ data_source = "phone" │ │ data_source = "watch" │ │
│ └──────────┬──────────────────┘ └──────────┬─────────────────────────────┘ │
│ │ │ │
│ └─────────────┬─────────────────────┘ │
│ │ │
│ UploadFileWorker │
│ │ │
└───────────────────────────┼─────────────────────────────────────────────────────┘
│
▼
┌───────────────┐
│ Google Drive │
│ │
│ /WearableSensor_Data/
│ ├── participant/
│ │ ├── phone_*.csv
│ │ └── watch_*.csv
│ └── wearable_backup.json
└───────────────┘
Flux de donnees :
COLLECTE ENVOI RECEPTION STOCKAGE
──────── ─────── ────────── ─────────
Capteurs .gz files /sensor_file/ CSV append
│ │ │ │
▼ ▼ ▼ ▼
SharedFlow ──→ Queue ──→ Worker ──→ DataClient Asset ──→ processGzFile ──→ watch_*.csv
(tryEmit) (50K) (par fichier) (~200KB) (decompress+parse) (session)
│
TELEPHONE (collecte locale) ▼
─────────────────────── Google Drive
SharedFlow ──→ Queue ──→ .gz ──→ PhoneDataProcessingWorker ──→ phone_*.csv
(tryEmit) (50K) (flush) (decompress + appendData) (session)
STREAMING TEMPS REEL
────────────────────
onSensorChanged ──→ sendRawMessage ──→ MessageClient ──→ Broadcast ──→ UI Graph
(watch, 10Hz) (compact JSON) (PATH_STREAM) (FloatArray) (Compose)
Paths de communication :
/PATH_STREAM_SENSOR_START Phone → Watch Demarre le streaming d'un capteur
/PATH_STREAM_SENSOR_STOP Phone → Watch Arrete le streaming
/PATH_STREAM_SENSOR_DATA Watch → Phone Donnees temps reel (10 Hz)
/PATH_SEND_WATCH_SENSOR_LIST Watch → Phone Liste des capteurs de la montre
/PATH_ASK_WATCH_SENSOR_LIST Phone → Watch Demande la liste des capteurs
/PATH_SEND_WATCH_CONFIG Phone → Watch Configuration (settings + sensors)
/PATH_GET_WATCH_SERVICE_STATUS Watch → Phone Statut du service de collecte
/sensor_file/{name} Watch → Phone Fichier .gz via DataClient Asset
- Collecte simultanee de multiples capteurs hardware (accelerometre, gyroscope, magnetometre, pression, lumiere, etc.)
- Capteurs virtuels : microphone (dB SPL), GPS (lat/lon/alt/precision)
- Health Services : frequence cardiaque via MeasureClient (WearOS)
- SensorDirectChannel : mode haute performance zero-copy pour les capteurs compatibles
- Support des capteurs proprietaires constructeur (Samsung, Google, etc.) avec detection dynamique
- Capteurs uncalibrated (6 axes : valeurs + biais)
- Pipelines CSV separes : chaque appareil (phone/watch) ecrit dans son propre fichier CSV
- Colonne
data_sourceajoutee automatiquement ("phone" ou "watch") - Nommage :
phone_P001_session1_*.csvetwatch_P001_session1_*.csv - Locks independants par pipeline : pas de contention inter-device
- Finalisation independante : l'arret de la montre ne bloque pas le telephone
- Creation de gestes avec photo/video/GIF associe
- Scenarios configurables : ordre aleatoire, repetitions, durees personnalisees
- Lecture de scenarios avec enchainement automatique des gestes
- Assignment drag-and-drop des gestes aux scenarios
- Visualisation en temps reel des donnees capteurs de la montre sur le telephone
- Bus reactif SharedFlow pour la distribution zero-allocation des donnees
- Throttling a ~10 Hz pour le streaming watch-to-phone
- Queue en memoire (50K items) avec flush periodique vers fichiers gzip
- Envoi fichier par fichier (streaming anti-OOM) : chaque .gz (~200 KB) est envoye individuellement
- CSV incremental avec append (1 fichier par session par device)
- Backpressure a 70% de la capacite de la queue
- Header rewriting en streaming (pas de chargement complet en RAM)
- Pic memoire multi-device : ~30 MB max
- Historique des fichiers de collecte (local + Google Drive)
- Badges device source (Phone/Watch) sur chaque fichier
- Stats : total fichiers, Drive, local, par device
- Selection multiple avec long-press et export ZIP
- Upload vers Drive des fichiers locaux (bouton CloudUpload)
- Telechargement depuis Drive des fichiers distants
- Suppression avec choix : local seul, Drive seul, ou les deux
- Suppression multiple avec les memes options
- Fusion de fichiers CSV avec union des colonnes
- Selection des capteurs par appareil (telephone + montre)
- Reglages thread : priorite, frequence d'echantillonnage, delai
- Installation de l'APK montre via ADB depuis le telephone
- Sauvegarde/restauration des parametres via Google Drive
| Composant | Technologie |
|---|---|
| UI | Jetpack Compose + Material 3 |
| Navigation | Navigation Compose (bottom tabs + nested graphs) |
| State | ViewModel + StateFlow + MutableState |
| Base de donnees | Room (KSP) |
| Background | WorkManager |
| Communication | Wear Data Layer API (MessageClient + DataClient) |
| Sante | Health Services Client 1.1.0-alpha03 |
| Cloud | Google Drive API v3 + OAuth 2.0 |
| Graphiques | MPAndroidChart |
| Images | Coil (PNG, GIF) |
| JSON | Gson + Moshi (streaming) |
| Coroutines | kotlinx-coroutines 1.8.1 |
- Android Studio Hedgehog+ (AGP 8.5.1)
- JDK 17
- Kotlin 1.9.0
- Un compte Google Cloud avec :
- Google Drive API activee
- OAuth 2.0 Client IDs (Android + Web) configures dans la console
Note : Aucun fichier
google-services.jsonn'est necessaire. L'authentification Google Drive utilise OAuth 2.0 directement via le SDK Google Sign-In, sans Firebase.
git clone <repo-url>
cd WearableSensorL'application utilise Google Drive API v3 via Google Sign-In (pas Firebase). Aucun fichier google-services.json n'est necessaire.
- Aller sur Google Cloud Console
- Creer un nouveau projet (ex:
wearable-sensor) - Dans le menu lateral : APIs & Services > Library
- Chercher et activer Google Drive API
- Aller dans APIs & Services > OAuth consent screen
- Choisir External (ou Internal si compte Workspace)
- Remplir les champs obligatoires :
- Nom de l'application :
WearableCollector - Email d'assistance : votre email
- Domaines autorises : laisser vide
- Nom de l'application :
- Ajouter le scope :
https://www.googleapis.com/auth/drive.file - Ajouter votre email en tant que Test user (obligatoire tant que l'app n'est pas publiee)
- Sauvegarder
Important : Tant que l'ecran de consentement est en mode "Testing", seuls les Test users configures peuvent se connecter. Ajoutez tous les comptes Google qui utiliseront l'app.
Aller dans APIs & Services > Credentials > Create Credentials > OAuth client ID
Client 1 — Android (obligatoire) :
| Champ | Valeur |
|---|---|
| Type | Android |
| Package name | com.deladem.wearablecollector |
| SHA-1 fingerprint | Voir ci-dessous |
Pour obtenir le SHA-1 :
# Debug keystore
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android
# Release keystore
keytool -list -v -keystore /chemin/vers/votre/keystore.jksCreer un client Android par SHA-1 (un pour debug, un pour release si differents).
Client 2 — Web application (obligatoire) :
| Champ | Valeur |
|---|---|
| Type | Web application |
| Name | WearableCollector Web |
| Authorized redirect URIs | Laisser vide |
Le client Web est necessaire pour que le flow OAuth Android fonctionne. Il n'y a pas besoin de copier le client ID dans le code.
- Aucun fichier a telecharger ni a placer dans le projet
- La configuration se fait uniquement dans la Google Cloud Console
- L'app detecte automatiquement les credentials via le package name + SHA-1
- Dans l'app : Parametres > Google Drive > Se connecter pour tester
| Probleme | Solution |
|---|---|
| "Sign in failed" | Verifier que le SHA-1 correspond au keystore utilise (debug vs release) |
| "Access denied" | Ajouter le compte Google en Test user dans l'ecran de consentement |
| "Drive upload failed - user may not be signed in" | Se reconnecter dans Parametres > Google Drive |
| Pas de popup de connexion | Verifier que le client Android est cree avec le bon package name |
| "Error 403: access_denied" | L'app est en mode Testing et le compte n'est pas dans les Test users |
Creer gradle.properties a la racine :
storePassword=votre_mot_de_passe
keyAlias=votre_alias
keyPassword=votre_mot_de_passe# Debug
./gradlew assembleDebug
# Release
./gradlew assembleReleaseL'application telephone inclut un guide d'installation de l'APK montre via ADB (Parametres > Guide d'installation).
| Permission | Usage |
|---|---|
| BODY_SENSORS | Capteurs corporels |
| RECORD_AUDIO | Microphone (niveau sonore dB) |
| ACCESS_FINE_LOCATION | GPS |
| ACTIVITY_RECOGNITION | Capteurs d'activite (step detector, etc.) |
| FOREGROUND_SERVICE | Services de collecte |
| INTERNET | Google Drive, communication |
| POST_NOTIFICATIONS | Notifications de collecte |
| Permission | Usage |
|---|---|
| BODY_SENSORS_BACKGROUND | Capteurs en arriere-plan |
| HIGH_SAMPLING_RATE_SENSORS | Echantillonnage haute frequence |
| ACTIVITY_RECOGNITION | Reconnaissance d'activite |
Delimiter : ; (point-virgule), valeurs entre guillemets doubles.
Colonnes principales :
data_source: source de l'appareil ("phone"ou"watch")take_id: identifiant unique de la sessionparticipant_id,session_id: identification du participantdevice_brand,device_model,device_types: appareil sourcedeviceId: identifiant unique de l'appareilsensor_name,sensor_type,sensor_vendor: metadata capteurgestureCode,scenarioCode: contexte de collectez_timestamp: horodatage en millisecondesvalue-1avalue-N: valeurs du capteur
phone_P001_session1_1711612800000.csv # Donnees du telephone
watch_P001_session1_1711612800000.csv # Donnees de la montre
upload_phone_P001_*.csv # En cours d'upload
P001_merged_1711612800000.csv # Fusion de fichiers
P001_1711612800000.zip # Export ZIP
.gz: batches compresses (~2000 records, ~50-200 KB).csv: fichiers de session (1 par device par session).zip: export groupe de fichiers selectionnes
WearableSensor_Data/
├── participant_001/
│ ├── phone_P001_session1_*.csv
│ └── watch_P001_session1_*.csv
├── participant_002/
│ └── ...
└── wearable_backup.json
| Ecran | Description |
|---|---|
| Collecte | Lecture de scenarios ou collecte continue |
| Historique | Fichiers collectes avec badges device, selection multiple, export ZIP, upload Drive |
| Parametres > Telephone | Liste des capteurs du telephone |
| Parametres > Montre | Configuration des capteurs de la montre |
| Parametres > Google Drive | Connexion, sauvegarde, synchronisation |
| Parametres > Threads | Priorite, frequence, delai d'echantillonnage |
| Gestes | CRUD gestes avec media (photo/video/GIF) |
| Scenarios | CRUD scenarios avec assignation de gestes |
Projet prive.