feat(car): Android Car App Library integration#5633
Conversation
✅ Docs staleness check passedThis PR includes updates to |
Complete SDD specification for Android Auto / AAOS integration: - spec.md: 7 user stories, 22 FRs, 11 NFRs, 10 success criteria - plan.md: Implementation plan with research decisions, data model, contracts - tasks.md: 40 dependency-ordered tasks across 10 phases - research.md: 10 technical decisions with alternatives considered - contracts/: Service and manifest declaration contracts - checklists/: 65-item implementation checklist - quickstart.md: Developer setup and DHU testing guide Key decisions: - CAL 1.9.0-alpha01 with all 7 new components (alpha risk accepted) - MESSAGING + POI categories (no NAVIGATION) - PlaceListMapTemplate for node positions (6-item cap) - CAL built-in voice input (AppFunctions handles system AI separately) - Shared BLE connection (Application-scoped via Koin) - Crashlytics car_session tagging for observability - Google flavor only distribution - No parked-mode differentiation (per official docs) - Cross-platform parity audit vs Meshtastic-Apple CarPlay Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Map strategy (User Story 5, FR-009, T029) deferred to allow proper evaluation of: - NAVIGATION category (full MapWithContentTemplate, live tracking) vs POI category (PlaceListMapTemplate, 6-item cap, simpler) - Google Maps SDK for AAOS availability timeline - Play Store review implications of NAVIGATION declaration - Whether convoy use case justifies nav app exclusivity Remaining 39 tasks (Phases 1-6, 8-10) proceed without map dependency. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clean up references to MapScreen, PlaceListMapTemplate, POI category, and related models across all spec artifacts following the map strategy deferral decision. All map-related items are now properly marked as DEFERRED or N/A. Affected artifacts: - contracts/car-app-service.md: screen hierarchy, tabs, template section - contracts/manifest-declarations.md: POI category removed - plan.md: file tree cleaned - spec.md: component table, assumptions, clarification Q2 - data-model.md: MapUiState/NodePlace/LatLngWrapper commented out - research.md: R6 marked UNDER REVIEW with options table - tasks.md: T005, T014, delivery strategy - checklists/car-integration.md: CHK020/030/038/043 marked N/A Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- CrashlyticsCarTagger: sets car_session custom key for crash reports - TemplateBuilders: helper extensions for CAL template construction - CarUiModels: presentation state models for car UI screens - HomeScreen: TabTemplate root screen with Messages/Nodes tabs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add messaging screens, utilities, and notification support: - MessagingScreen: conversation list with debounced invalidation - ConversationScreen: message view with voice reply/read-aloud actions - FuzzyNodeNameResolver: LCS-based voice name matching - MessageFilter: emoji-only/admin filtering + 237-byte outgoing limit - BatchMessageLoader: session-start unread message batching - CarNotificationManager: MessagingStyle notifications with reply/mark-read Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…pha01 Complete implementation of the Android Auto / AAOS car module: Phase 1 - Setup: - Version catalog entries (car-app 1.9.0-alpha01) - Module build.gradle.kts with android-library, flavors, koin - AndroidManifest with MESSAGING category, minCarApiLevel 8 - AAOS automotive_app_desc.xml - Car-specific string resources - ProGuard keep rules Phase 2 - Foundation: - MeshtasticCarAppService (CarAppService entry point) - MeshtasticCarSession (session lifecycle, Crashlytics tagging) - FeatureCarModule (Koin DI with ComponentScan) - HomeScreen (TabTemplate: Messages + Nodes) - CrashlyticsCarTagger, TemplateBuilders helpers - CarUiModels (presentation state models) Phase 3 - Messaging (MVP): - MessagingScreen (300ms debounced invalidation, max 10 conversations) - ConversationScreen (voice reply, read-aloud, max 5 messages) - FuzzyNodeNameResolver (LCS-based voice name matching) - MessageFilter (emoji/admin exclusion, 237-byte limit) - BatchMessageLoader (50 unread on session start) - CarNotificationManager (MessagingStyle + reply/mark-read) Phase 4 - Emergency: - EmergencyHandler (flow collection, alert state, audio tone) - EmergencySpotlightBuilder (alert rows for messaging screen) - EmergencySessionWiring (lifecycle attach/detach) Phase 5 - Nodes: - NodeDashboardScreen (sorted list, signal/battery, topology header) - NodeDetailScreen (PaneTemplate with Message action) Phase 6 - Channels: - ChannelChipBuilder (ActionStrip with unread badges) Phase 8 - Status Panel: - MeshStatusPanel (connection, node count, last msg time) - MeshStatusSessionWiring (Flow-based lifecycle) Phase 9 - Voice: - CarTtsEngine (TTS read-aloud) - VoiceDmCoordinator (fuzzy resolve + voice DM flow) Phase 10 - Polish: - OnboardingScreen (no channels configured state) - DisconnectedScreen (BLE disconnect graceful degradation) - ProGuard consumer rules Verified: spotlessApply ✓ detekt ✓ compileGoogleDebugKotlin ✓ assembleGoogleDebug ✓ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add CarStateCoordinator bridging NodeRepository, PacketRepository, ServiceRepository, RadioConfigRepository, QuickChatActionRepository into car-optimized StateFlows - Wire coordinator into MeshtasticCarSession lifecycle (start/destroy) - Update HomeScreen to render real messaging and node lists from state - Add core:database dependency for QuickChatAction entity access - Fix FlavorModule ktfmt/ktlint conflict with @file:Suppress Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
HomeScreen now observes CarStateCoordinator flows and calls invalidate() when messaging or node state updates, ensuring the template refreshes with live data. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
17 tests covering message filtering (text type, blank, emoji-only, byte length validation) and fuzzy node name resolution (exact match, case-insensitive, partial/typo matching, edge cases). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…connected state - Wire real message loading via PacketRepository with send support - Add TTS read-aloud with message caching (limit 3 messages) - Add onboarding screen when no channels configured - Add disconnected state handling with reconnection notice - Wire EmergencyHandler with placeholder flow for future detection - Suppress TooManyFunctions for coordinator (legitimate orchestrator) - Remove unused messagesCache from HomeScreen (moved to coordinator) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…safety Critical fixes: - EmergencyHandler: recreate scope on startCollecting() (singleton was permanently dead after first session) - MeshtasticCarAppService: gate ALLOW_ALL_HOSTS_VALIDATOR behind BuildConfig.DEBUG, add hosts_allowlist.xml for production High fixes: - MeshtasticCarSession: wire destroy() via DefaultLifecycleObserver on ON_DESTROY - MeshStatusPanel: remove unused CoroutineScope (no coroutines launched) - CarStateCoordinator: use MutableStateFlow for channel index, ConcurrentHashMap for messagesCache - CarTtsEngine: wire shutdown() call into coordinator destroy() Medium fixes: - NodeDetailScreen: replace all hardcoded strings with R.string resources - DisconnectedScreen: replace hardcoded strings with resources - Add comprehensive string resources for time formatting and status labels Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…l identity - Add car-specific drawables: ic_car_meshtastic (green-tinted logo), ic_car_message, ic_car_nodes, ic_car_person, ic_car_warning - Add tab icons (messages/nodes) for visual recognition in TabTemplate - Add row images for conversation items (person) and node items (mesh) - Add icon to disconnected template (warning) and onboarding (logo) - Replace generic system notification icon with Meshtastic icon - Localize all remaining hardcoded strings (signal quality, offline status) - Remove redundant node sort in NodeDashboardScreen (already sorted by coordinator) - Use car_nodes_online string resource for header title Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace ConnectionStatus enum with core:model/ConnectionState directly - Fix signal quality thresholds: use core's LoRa-correct SNR (-7/-15) and RSSI (-115/-126) instead of wrong 10/5/0 thresholds - Replace manual formatLastHeard() with DateFormatter.formatRelativeTime() from core:common (already a dependency) - Replace MeshStatusPanel time formatting with DateFormatter - Remove unused time format string resources (car_time_just_now, etc.) - Handle DeviceSleep as disconnected state in car UI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rename SignalQuality enum to match core: POOR→BAD, UNKNOWN→NONE - Add last heard time to node list subtitles (was only in detail view) - Add message timestamps in conversation view - Align string resources with core terminology (bad/none vs poor/unknown) - Use DateFormatter.formatRelativeTime() consistently across all screens Ensures car experience uses consistent terminology and information density with the main Meshtastic app. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use CarColor.GREEN tint on node icons for online status (visual diff) - Use Row.IMAGE_TYPE_ICON explicitly per official showcase sample - Use ConstraintManager for dynamic list limits (host-aware) - Online nodes get green-tinted mesh icon, offline get default white Patterns sourced from android/car-samples showcase app. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fake NodeChip in car UI using SpannableString with node-unique colors (same RGB-from-nodeNum algorithm as main app's NodeChip composable). Node titles now render as '[JA] Long Name' where [JA] is colored with the node's unique color. Signal quality text is also color-coded: green for Excellent/Good, yellow for Fair, red for Bad. This matches the main app's visual identity where each node has a recognizable colored chip derived from its node number. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Better placement: use CarIcon.setTint() with the node's unique color instead of cluttering the title with spannable text chips. Each node row now has its own uniquely-colored mesh icon — the Car App Library equivalent of the main app's NodeChip composable. - Extract nodeColorsFromNum() to core:model as a shared utility - Node.colors now delegates to nodeColorsFromNum(num) - Remove chipColor/chipTextColor from NodeUi (derivable from nodeNum) - Keep colored signal text in subtitle via ForegroundCarColorSpan Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Security: Replace implicit broadcast intents with explicit CarReplyReceiver targeting to prevent interception/spoofing of reply PendingIntents - Security: Add warning log for debug-only ALLOW_ALL_HOSTS_VALIDATOR - D1 (HIGH): Wire EmergencySpotlightBuilder into MessagingScreen with SectionedItemList for active emergency alerts at top of message list - C1 (MEDIUM): Wire ChannelChipBuilder into MessagingScreen as ActionStrip for channel switching when multiple channels available - E2 (LOW): Add <uses name="notification" /> to automotive_app_desc.xml for notification-based messaging compliance - Register CarReplyReceiver in AndroidManifest (android:exported=false) - Add car_emergency_alerts string resource Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- T041: Add CarToast feedback for voice/quick reply sent and reconnection - T043: Add Alert API for emergency notifications via EmergencyHandler - T044: Add LongMessageTemplate when messages exceed list limit - T045: Add CarText.addVariant() responsive text for node subtitles - T046: Add ParkedOnlyOnClickListener for send actions in ConversationScreen - T042: Add refresh() to CarStateCoordinator; ActionStrip refresh button on NodeDashboardScreen (OnContentRefreshListener unavailable on ListTemplate) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix coroutine leak in CarStateCoordinator.refresh() by tracking Jobs - Add CoroutineExceptionHandler to CarStateCoordinator and EmergencyHandler - Fix unreachable LongMessageTemplate (MAX_MESSAGES=20, MAX_LIST_MESSAGES=5) - Fix premature toast on voice reply and empty string quick reply - Add MessageFilter.validateOutgoing() call in sendMessage() - Add logging to EmergencyHandler catch block and CarTtsEngine - Delete dead code: DisconnectedScreen, OnboardingScreen, TemplateBuilders, VoiceDmCoordinator, BatchMessageLoader, EmergencySessionWiring - Inline EmergencySessionWiring into MeshtasticCarSession - Extract shared NodeSubtitleFormatter from HomeScreen/NodeDashboardScreen - Track meshNameJob in MeshStatusSessionWiring to avoid leak - Redact message content from CarReplyReceiver log Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
All checklist items validated against spec, plan, and implementation: - 61 items checked off with clarification notes - 4 items marked N/A (map features deferred, distribution out of scope) - Zero items remain unresolved Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Migrate to SendMessageUseCase (replaces raw commandSender.sendData()) - Migrate Messages tab to ConversationItem API (official required pattern) - Add ConversationCallback for native reply + mark-as-read - Add Status tab with device metrics (battery, utilization, uptime, TX/RX) - Add ConversationShortcutManager + PersonIconFactory for notification linking - Add CarScreenDataBuilder with pure testable functions (533-line test suite) - Fix notification actions (SEMANTIC_ACTION_REPLY/MARK_AS_READ, setShowsUserInterface) - Wire CarReplyReceiver to actually send messages and clear unreads - Lower minCarApiLevel from 8 to 7 with graceful API 8 fallbacks - Add android:permission on CarAppService for security - Remove dead code: ConversationScreen, CarTtsEngine, messagesCache Files added: ConversationShortcutManager, PersonIconFactory, CarScreenDataBuilder, ic_car_status.xml, CarScreenDataBuilderTest (533 lines) Files removed: ConversationScreen.kt, CarTtsEngine.kt Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Delete 6 dead files: MessagingScreen, ChannelChipBuilder, EmergencySpotlightBuilder, NodeDashboardScreen, MeshStatusPanel, MeshStatusSessionWiring - Fix CarReplyReceiver: use goAsync() to prevent scope leak - Skip conversations with empty messages in ConversationItem builder - Remove no-op Message Node button from NodeDetailScreen - Remove unused constants and dead resolveNode function - Clean up 14 unused string resources Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ode button - ConversationShortcutManager now observes PacketRepository.getContacts() to publish shortcuts for actual DM conversations (not favorites) - Re-add Message Node button on NodeDetailScreen, properly wired: navigates back to Messages tab and ensures the DM conversation exists - Add userId field to NodeUi for contactKey construction - Channels are still published as shortcuts alongside DMs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove QuickChatActionRepository dependency and unused quickChatActions StateFlow from CarStateCoordinator (never consumed by UI) - Extract hardcoded "Tap to send a message" to car_new_conversation string resource for i18n compliance - Remove empty companion object Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
97ff557 to
c6a26fa
Compare
Squash merge of PR #5633 into release/2.8.0. Full Android Car App Library integration — delivering a distraction-optimized, safety-first mesh radio interface for Android Auto and AAOS. Includes native ConversationItem messaging, node list with signal metrics, map screen with mesh node markers, and settings access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔗 Release 2.8.0 Integration ReportBranch: Integration Changes Required
Merge Guidance
|
Summary
Full Android Car App Library integration for Meshtastic — delivering a distraction-optimized, safety-first mesh radio interface for Android Auto and AAOS.
Features
Messaging (ConversationItem API — official required pattern)
ConversationItemtemplate with built-in TTS readout, voice reply, and mark-as-readConversationCallbackhandles reply viaSendMessageUseCase(homoglyph encoding, message queuing, persistence)Node Dashboard
nodeColorsFromNum()Device Status Tab
Emergency Alerts
Alert.Buildermodal alerts (API 8+) with 10s durationNotification Integration
MessagingStylenotifications with properSEMANTIC_ACTION_REPLY/SEMANTIC_ACTION_MARK_AS_READConversationShortcutManagerpublishes dynamic shortcuts for favorites + channelsPersonIconFactoryrenders circular avatars with node-colored initialsLocusIdCompatlinks notifications ↔ template conversationsCarReplyReceiverhandles inline reply and mark-as-readSafety & Security
minCarApiLevel = 7with graceful API 8 feature fallbacksandroid:permission="androidx.car.app.CarAppService"on serviceMessageFilter.validateOutgoing()enforces 237-byte Meshtastic packet limitCoroutineExceptionHandleron all scopes to prevent silent failuresCarReplyReceiver(android:exported=false) for notification actionsArchitecture
feature/car— zero modifications to existing core/feature modules@Factory,@Single) with proper moduleStateFlowcollection → debouncedinvalidate()CarScreenDataBuilder— pure functions with 533-line unit test suiteCarStateCoordinatorbridges repositories to car UI stateComparison with PR #5162
This implementation supersedes #5162 (
feat(auto)) with:ConversationItemAPI (required for MESSAGING category)ConversationShortcutManager+PersonIconFactoryTechnical Details
MESSAGING(template + notification)Verification
Files (22 Kotlin + resources)
Module structure