# CLAUDE.md

## Projet

Dashboard OBD-II natif Android pour smartwatch 480x480 (2.6"). Kotlin + XML layouts + ViewBinding. Pas de Jetpack Compose.

## Build & deploy

Après **chaque modification de code**, tu DOIS build et installer l'app sur un émulateur Android :

```bash
export JAVA_HOME="/opt/homebrew/opt/openjdk@17"
export ANDROID_HOME="$HOME/Library/Android/sdk"

# Build
./gradlew assembleDebug

# Install
$HOME/Library/Android/sdk/platform-tools/adb install -r app/build/outputs/apk/debug/app-debug.apk

# Relancer l'app après installation
$HOME/Library/Android/sdk/platform-tools/adb shell am start -n com.erwan.revwatch/.MainActivity
```

### Émulateur

L'émulateur doit être configuré en **480x480** avec un DPI de **200** :

```bash
adb shell wm size 480x480
adb shell wm density 200
```

Si aucun émulateur n'existe, en créer un :

```bash
$HOME/Library/Android/sdk/cmdline-tools/latest/bin/avdmanager create avd \
  --name "Watch480" \
  --package "system-images;android-34;google_apis;arm64-v8a" \
  --device "pixel" \
  --force

# Puis le lancer :
$HOME/Library/Android/sdk/emulator/emulator -avd Watch480 &

# Puis configurer la taille :
adb wait-for-device
adb shell wm size 480x480
adb shell wm density 200
```

## Structure du code

```
app/src/main/java/com/example/revwatch/
├── model/
│   ├── BridgeConfig.kt        # Config globale de l'app
│   ├── DashPage.kt            # Modèle grille (CellConfig + DataBinding + DashPage) avec sérialisation
│   ├── WidgetType.kt          # Enum des 6 types de widgets (oil_temp, boost, rpm, speed, coolant, voltage)
│   ├── GaugeStyle.kt          # Enum des 6 styles de gauge (ring, arc, bar, digital, dual, segment)
│   ├── GaugeRange.kt          # Range min/max + seuils warning/danger
│   ├── GaugeLimits.kt         # Limites converties en unités d'affichage
│   ├── UnitOption.kt          # Options d'unités avec conversion (°C/°F, kPa/PSI/bar, km/h/mph)
│   ├── MetricSnapshot.kt      # Snapshot des données OBD (kotlinx.serialization)
│   ├── ConnectionMode.kt      # USB, Bluetooth, WiFi, Test
│   └── VehicleProfile.kt      # Profils véhicule Ford/Honda
├── obd/
│   ├── ElmTransport.kt        # Interface transport ELM327
│   ├── Elm327Client.kt        # Client ELM327 (init, envoi commandes AT/OBD)
│   ├── ObdPoller.kt           # Polling périodique des PIDs selon le profil
│   ├── ConnectionFactory.kt   # Factory pour créer le transport (USB/BT/WiFi)
│   ├── UsbElmTransport.kt     # Transport USB
│   ├── BluetoothElmTransport.kt # Transport Bluetooth
│   └── WifiElmTransport.kt    # Transport WiFi
├── prefs/
│   └── AppPrefs.kt            # SharedPreferences (load/save config, pages, DPI, screen control)
├── ui/
│   ├── GaugeView.kt           # Vue Canvas custom — 6 styles de gauge, animations, zones couleur
│   ├── GridPagerAdapter.kt    # Adapter ViewPager2 horizontal — grille de widgets par page
│   ├── VerticalPagerAdapter.kt # Adapter ViewPager2 vertical — 3 pages : gauges, apps, réglages
│   ├── BrandSplashOverlay.kt  # Overlay intro/outro animé avec nom de marque
│   └── NestedScrollableHost.kt # Wrapper pour touch events entre VP2 imbriqués
├── util/
│   └── Extensions.kt          # Extensions Kotlin partagées (Context.dp())
├── BaseActivity.kt            # Classe abstraite — immersive mode, DPI custom, lifecycle partagé
├── DashboardActivity.kt       # Écran principal (pager vertical, edit mode, splash overlay)
├── GaugeConfigActivity.kt     # Config d'un widget (4 steps : type, style, unité, range/limites)
├── SetupActivity.kt           # Assistant initial (connexion, test, véhicule)
├── ObdBridgeService.kt        # Service foreground (OBD + broadcast + power receiver)
├── ScreenAdminReceiver.kt     # Device admin pour lockNow() (extinction écran)
└── MainActivity.kt            # Routeur (→ setup ou dashboard)
```

## Conventions

- **Langue UI** : français
- **Layouts** : XML dans `res/layout/`, ViewBinding (pas de `findViewById` dans les activities qui utilisent binding)
- **Pas de Compose** : tout est XML + Canvas custom
- **BaseActivity** : toutes les activités visibles héritent de `BaseActivity` (immersive mode, DPI custom, lifecycle). `MainActivity` est l'exception (routeur instantané).
- **Extensions** : `Context.dp()` dans `util/Extensions.kt` — pas de fonctions utilitaires dupliquées dans les fichiers
- **Haptic feedback** : `HapticFeedbackConstants.CONTEXT_CLICK` sur chaque interaction
- **Animations gauge** : `ValueAnimator` avec `DecelerateInterpolator`, payload-based partial updates dans RecyclerView
- **Sérialisation pages** : format custom texte dans SharedPreferences (pas de JSON pour les pages)
- **Écran 480x480** : tout le design est optimisé pour petit écran carré
- ** Style de design ** : watchOS like (Apple Watch)

## Points d'attention

- `GridPagerAdapter` utilise des **payload-based updates** (`PAYLOAD_SNAPSHOT`) pour éviter de recréer les vues à chaque snapshot OBD — ne pas utiliser `notifyDataSetChanged()` pour les mises à jour de valeurs
- `GaugeView.hasBeenSet` évite l'animation depuis zéro lors du premier affichage
- Les `DashPage` se sérialisent avec `§` comme séparateur de pages et `|` pour les cellules
- Le mode édition est activé par long press sur la page de gauges
- Ne pas re-créer inutilement de composants/designs: si on créé un bouton quelque part, utiliser le style déjà créé; si on créé une modale quelque part, utiliser le style déjà créé.
