Application Wear OS 5 qui suit la navigation initiée par OsmAnd+ 5.4.9 sous Android 16.
| Version du document | 1.0 |
| Cible | Montre Wear OS 5 (minSdk 34), autonome (standalone) |
| Source de navigation | OsmAnd+ 5.4.9 sur le téléphone Android 16 |
| Diagrammes | puml/ — sources PlantUML versionnées |
OsmAnd+ assure le calcul d'itinéraire, le guidage et la voix sur le téléphone. En usage vélo, moto ou randonnée, le téléphone reste dans la poche : WatchOsm reprend sur le poignet l'information que la montre ne montre pas aujourd'hui — la prochaine manœuvre et les grandeurs de trajet — sans remplacer OsmAnd ni dupliquer son moteur de navigation.
Objectifs v1
Non-objectifs v1 (volontairement exclus)
OsmAnd ne publie aucune API officielle de flux de navigation vers une montre. Quatre ponts ont été évalués :
| Mécanisme | Avantages | Inconvénients | Décision | |
|---|---|---|---|---|
| A | NotificationListenerService sur la montre, alimenté par les notifications OsmAnd relayées (bridging Wear OS) |
Zéro app téléphone, zéro réseau, aucun couplet à maintenir, fonctionne avec OsmAnd+ tel quel | Dépend du format du texte de notification (non contractuel) ; permission manuelle ; bridging limité aux extras textuels | Retenu pour v1 |
| B | App compagnon téléphone : listener local + Wearable.DataLayer vers la montre |
Source plus fiable (notifications complètes côté téléphone) | Deux modules à maintenir, installation à deux endroits | Phase 2 |
| C | API AIDL OsmAnd (net.osmand.aidlapi) via app compagnon |
Contrôle direct : pauseNavigation, stopNavigation, callbacks |
Fonctionne uniquement côté téléphone, ne fournit pas le flux de consignes complet | Phase 2 (contrôle) |
| D | Attendre l'app de montre officielle OsmAnd (PR osmandapp/OsmAnd#26227, draft en sept. 2026) |
Rien à développer | Indéterminé, non publié, pas de calendrier | Suivi, hors périmètre |
Option A retenue : elle donne une application Wear OS autonome, testable sans téléphone OsmAnd modifié, et l'heuristique de parsing est isolée dans une classe unique si le format OsmAnd évolue.
OsmAnd publie une notification permanente (ongoing) pendant la navigation,
contenant la consigne de manœuvre, l'ETA, la distance restante et la vitesse.
Sur Wear OS, les notifications du téléphone sont relayées automatiquement
(bridging Bluetooth) dans le NotificationManager local de la montre, où un
NotificationListenerService peut les intercepter — y compris leurs extras
textuels (EXTRA_TITLE, EXTRA_TEXT, EXTRA_BIG_TEXT…). La catégorie
Notification.CATEGORY_NAVIGATION, quand OsmAnd la pose, sert de signalement
fort dans le parseur.
Application monomodule en trois couches strictes, sans framework de DI ni de base de données : il n'y a qu'un flux d'état à transporter.
┌────────────────────────── couche présentation ──────────────────────────┐
│ MainActivity → AppNavHost (SwipeDismissableNavHost) │
│ ├─ HomeScreen (état de l'écoute, raccourci navigation) │
│ └─ NavigationScreen (manœuvre, métriques, keep-screen-on) │
│ ↑ StateFlow<NavigationState> │
│ NavigationViewModel (relais de durée de vie) │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────── couche données ─────────────────────────┐
│ OsmAndNotificationListener (NotificationListenerService) │
│ → OsmAndNotificationParser (texte → NavigationState, défensif) │
│ → NavigationStateRepository (source de vérité, StateFlow) │
└──────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────── couche domaine ───────────────────────────┐
│ NavigationState · TurnInstruction · Maneuver (enum) · Formatters │
│ (Kotlin pur, testable sans Android) │
└──────────────────────────────────────────────────────────────────────────┘
OsmAnd (téléphone) → notification ongoing → bridging Wear OS →
NotificationManager montre → OsmAndNotificationListener →
OsmAndNotificationParser → NavigationStateRepository (StateFlow) →
NavigationViewModel → écrans Compose.
Règles clés :
AppContainer de
WatchOsmApplication) : il est partagé entre le service (production) et
les ViewModels (consommation).activeNotificationKey) empêche une
notification annexe d'OsmAnd (enregistrement de trace, alerte) d'effacer
l'état de navigation lorsqu'elle est retirée.Le texte d'OsmAnd n'est pas un contrat. Le parseur (OsmAndNotificationParser)
applique, dans l'ordre :
navigation — sinon une notification d'enregistrement
de trace (« 12,5 km ») serait prise pour une navigation active.Deux écrans seulement, en SwipeDismissableNavHost (le retour latéral système
fonctionne nativement) :
isActive = true ouvre automatiquement l'écran navigation ;NavigationState(
isActive, isPaused,
instruction: TurnInstruction?(maneuver: Maneuver, distanceMeters, streetName, rawText),
remainingDistanceMeters, remainingTimeMinutes, arrivalTimeLabel,
speedKmh, speedLimitKmh, destination, updatedAtMillis
)
Maneuver : enum de 11 valeurs couvrant le vocabulaire OsmAnd (EN/FR).Formatters produit les formes françaises (« 5,4 km », « 1 h 05 »).WatchOsm/
├── build.gradle.kts / settings.gradle.kts / gradle/libs.versions.toml
├── documentation/
│ ├── architecture.md ← ce document
│ └── puml/
│ ├── usecases.puml cas d'utilisation
│ ├── composants.puml composants
│ ├── classes.puml classes
│ ├── sequence_navigation.puml séquence notification → écran
│ ├── etats_navigation.puml machine à états
│ └── deploiement.puml déploiement téléphone + montre
└── wear/ module unique v1
└── src/main/java/fr/barteam/watchosm/
├── WatchOsmApplication.kt conteneur (AppContainer)
├── domain/ NavigationState, Maneuver, Formatters
├── data/ listener, parseur, repository
└── ui/ MainActivity, AppNavHost, ViewModel,
écrans, ManeuverArrow, thème
| Risque | Impact | Atténuation |
|---|---|---|
| OsmAnd change le format de sa notification | Consignes non reconnues | Parseur isolé + tests unitaires sur formats connus + filet catégorie navigation |
Le NotificationListenerService de la montre ne reçoit pas les notifications relayées (comportement variable selon la version Wear OS et le fabricant) |
Aucune donnée | Vérification hardware à faire en premier ; solution de repli = phase 2 (app compagnon téléphone + Wearable.DataLayer) |
| Bridging Wear OS tronque les extras | Données partielles | Champs nullables, rendu dégradé, jamais de crash |
| Permission d'écoute non accordée | Aucune donnée | Écran d'accueil explicite + bouton d'ouverture des réglages système |
| Écran qui s'endort pendant le guidage | Inutilisable à vélo/moto | FLAG_KEEP_SCREEN_ON sur l'écran navigation tant que isActive |
| Notification d'enregistrement prise pour une navigation | Faux positif | Garde-fou d'acceptation (marqueur ou catégorie) + clé de notification active |
| Batterie de la montre | Autonomie | Aucun GPS, aucun réseau, aucun réveil périodique : seul un NotificationListenerService est lié |
wear/src/test/.../OsmAndNotificationParserTest)
: 11 cas — formats FR/EN, rond-point, demi-tour, vitesse ≠ distance,
notification d'enregistrement rejetée, pause, catégorie navigation, arrivée,
légère-gauche, durée composée. Commande : ./gradlew :wear:testDebugUnitTest.
java -jar plantuml.jar -checkonly documentation/puml/*.puml../gradlew :wear:lint.| Phase | Contenu |
|---|---|
| 2 | Module phone/ : notification listener local (source plus riche) + API AIDL OsmAnd (pauseNavigation, stopNavigation, resumeNavigation) + pont Wearable.DataLayer (MessageClient) vers la montre |
| 3 | Tile de navigation (aperçu permanent), complication de manœuvre, support Always-On Display / mode ambiant natif, profilage batterie |
| 4 | Suivi de l'app de montre officielle OsmAnd (PR osmandapp/OsmAnd#26227) : bascule sur le protocole natif si publié |
| Diagramme | Fichier | Objectif |
|---|---|---|
| Cas d'utilisation | puml/usecases.puml |
Périmètre fonctionnel et acteurs |
| Composants | puml/composants.puml |
Découpage en couches et flux |
| Classes | puml/classes.puml |
Structure statique |
| Séquence | puml/sequence_navigation.puml |
Début/pause/fin d'une navigation |
| États | puml/etats_navigation.puml |
Cycle de vie de NavigationState |
| Déploiement | puml/deploiement.puml |
Téléphone, relais Bluetooth, montre |
Les diagrammes sont versionnés en sources PlantUML ; la rendu PNG/SVG
s'obtient avec java -jar plantuml.jar -tsvg documentation/puml/*.puml
(PlantUML n'étant pas installé sur la machine de développement actuelle).