Skip to content

feat(auto): Android Auto communications app with local stats#5162

Draft
Copilot wants to merge 98 commits intomainfrom
copilot/add-messaging-feature-android-auto
Draft

feat(auto): Android Auto communications app with local stats#5162
Copilot wants to merge 98 commits intomainfrom
copilot/add-messaging-feature-android-auto

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 17, 2026

Adds a dedicated Android Auto communications app using Car App Library templates (TabTemplate, ListTemplate, CarAppService) — the phone UI is never projected to the car head unit.

Features

  • Three-tab layout matching iOS CarPlay: Status, Favorites, Messages
  • Status tab displays local device stats mirroring the home-screen Local Stats widget: battery level, channel/air utilization, online/total nodes, uptime, and TX/RX/Dupe traffic counters
  • Favorites tab shows starred nodes with online/offline status, hop distance, and battery
  • Messages tab lists channels and DM conversations with unread counts and message previews
  • MessagingStyle notifications with conversation shortcuts for inline reply and mark-as-read from Android Auto
  • Fallback ListTemplate for Car API level < 6 hosts that don't support TabTemplate

Architecture

  • All Auto UI lives in the isolated feature/auto module — no phone UI code is included
  • Pure business logic in CarScreenDataBuilder (testable without Car App Library or Robolectric)
  • CarLocalStats data class mirrors LocalStatsWidgetUiState with the same fallback strategy (prefers LocalStats, falls back to DeviceMetrics)
  • Reactive data flow via combine() on repository StateFlows with a single invalidate() per batch

jamesarich and others added 2 commits April 17, 2026 07:34
- Move MeshtasticCarAppService, CarSession, CarScreen from app/auto
  to the new :feature:auto module (meshtastic.android.library)
- Move automotive_app_desc.xml → auto_app_desc.xml (respects resourcePrefix)
- Move manifest entries (service + meta-data) into feature module so they
  merge into app rather than living in the app manifest directly
- Fix HostValidator: use ApplicationInfo.FLAG_DEBUGGABLE instead of
  BuildConfig.DEBUG (library modules don't ship their own BuildConfig)
- Fix stale unread counts: replace point-in-time getUnreadCount() with
  flatMapLatest + per-conversation getUnreadCountFlow() so the car screen
  invalidates on new messages, not just topology changes
- Fix ConversationShortcutManager: replace removeAllDynamicShortcuts +
  addDynamicShortcuts with pushDynamicShortcut per conversation to
  preserve usage/ranking history; remove stale shortcuts individually;
  respect getMaxShortcutCountPerActivity() limit
- Fix SHORTCUT_CATEGORY_CONVERSATION: constant lives on ShortcutInfo,
  not ShortcutManagerCompat
- Remove androidx.car.app dependency from :app (now owned by :feature:auto)
- Add :feature:auto to settings.gradle.kts and app dependencies

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ic exceptions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the enhancement New feature or request label Apr 17, 2026
jamesarich and others added 8 commits April 17, 2026 08:02
Notifications and message routing key channel conversations by the raw
protocol channel index (e.g. "2^all"), but publishShortcuts and the
car screen were re-indexing after filtering out unnamed channels, so
named channels after a gap would never match their notification's
shortcutId/locusId and their unread badge would stay at zero.

Preserve the original index via mapIndexedNotNull { index to settings }.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Gearhead's MsgNotifParser was rejecting Meshtastic notifications with:
- 'No semantic reply action found'
- 'No semantic mark-as-read action found'
- 'added an invalid shortcut'

Fixes:
- Tag reply action with SEMANTIC_ACTION_REPLY + setShowsUserInterface(false)
  + setAllowGeneratedReplies(true) so Gearhead/Assistant can surface it.
- Tag mark-as-read action with SEMANTIC_ACTION_MARK_AS_READ.
- Publish an on-demand long-lived conversation shortcut whose id matches
  the notification's setShortcutId(contactKey). Previously only favorites
  + channels at index 0 had shortcuts, so DMs received on a non-zero
  channel referenced an unpublished shortcut and Android Auto refused to
  project them.

Verified on Pixel 6a + DHU 2.0: notifications now carry matching
long-lived shortcuts and project as messaging HUNs with reply, mark-read
and reaction actions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ReplyReceiver was only cancelling the notification after an inline
reply; PacketRepository.clearUnreadCount was never called, so the
message stayed 'unread' in the app (and in the Android Auto favorites
unread badges) even after the user replied from the HUN.

Mirror MarkAsReadReceiver by invoking clearUnreadCount with nowMillis
before cancelling the notification.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Temporary diagnostic logging added while investigating why Android Auto
inline replies don't appear to dismiss the conversation notification
for some users. Remove or downgrade to verbose once confirmed working.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rememberDataPacket() was invoking handlePacketNotification() for
outgoing packets too, which made our own reply race with the
cancel issued by ReplyReceiver and repost the conversation with
ourselves as the visible sender (lastMessage.node == ourNode).

Also harden ensureShortcutForNotification for DMs: the remote
contact is deterministic from contactKey (channel + nodeId), so
derive the shortcut Person from the resolved contact node rather
than whatever message happens to be newest in history. This keeps
the Android Auto HUN labelled correctly even if the message list
ends with an outgoing packet.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The MIN_CONTEXT_MESSAGES fallback (pull 3 most recent history
messages when unread < 3) was injecting already-read historical
messages into the MessagingStyle. On Android Auto, Gearhead reads
every message in the style aloud and shows them stacked on the
HUN, so old context was announced alongside the new one.

Just show the unread messages (up to MAX_HISTORY_MESSAGES). If
nothing is unread, don't post at all — we only call this from
paths that already check for fresh inbound content.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After clearing a message notification we left the GROUP_KEY_MESSAGES
summary in place, which on Android Auto leaves a lingering HUN /
summary entry for the already-dismissed conversation. Cancel the
summary when no child notifications remain, and refresh it otherwise.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reading notificationManager.activeNotifications immediately after
cancel() races with NotificationManagerService, so the count of
remaining children was unreliable and the summary could linger.
Drop it unconditionally — the next inbound message rebuilds it via
showGroupSummary().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@garthvh
Copy link
Copy Markdown
Member

garthvh commented Apr 17, 2026

@copilot Mr Clanker the iOS branch was updated with tabs for navigation instead of the list can you update to match?

Also getting a maps permission error you need to fix

image

jamesarich and others added 17 commits April 24, 2026 19:09
…, StateFlow) (#5222)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
…5224)

Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
…nts (#5225)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
…5227)

Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
…5230)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
…5231)

Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
…5235)

Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
…5238)

Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
…5240)

Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 24, 2026

@copilot resolve the merge conflicts in this pull request

Rebased onto origin/main (91f4a17) — all 27 branch commits landed cleanly with zero conflicts. Force-pushed in commit 1901185.

@garthvh
Copy link
Copy Markdown
Member

garthvh commented Apr 26, 2026

@copilot I heard you added to whole android app to android auto, can you please not include the whole app and instead create a proper android auto communications app and make sure the local stats widget also works in android auto?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 26, 2026

@copilot I heard you added to whole android app to android auto, can you please not include the whole app and instead create a proper android auto communications app and make sure the ...

The feature/auto module is already a proper Android Auto communications app — it uses Car App Library templates exclusively (TabTemplate, ListTemplate, CarAppService), not a projection of the phone UI. The phone's MainActivity only has MAIN/LAUNCHER categories and is never shown on the car head unit.

In commit 483ab96, I added the local stats widget data to the Car App's Status tab. Android Auto doesn't support traditional GlanceAppWidget home-screen widgets, so the stats are surfaced within the Car App template UI instead. The Status tab now shows:

  • Connection state + device name
  • Battery level (or "Powered")
  • Channel utilization + Air utilization
  • Online/total nodes
  • Uptime
  • TX/RX/Dupe packet traffic (when available)

This mirrors the same key metrics the home-screen Local Stats widget displays.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.google.com
    • Triggering command: /usr/lib/jvm/temurin-21-jdk-amd64/bin/java /usr/lib/jvm/temurin-21-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED -XX:&#43;UseParallelGC -XX:MaxMetaspaceSize=2g -XX:&#43;HeapDumpOnOutOfMemoryError -Xmx8g -Dfile.encoding=UTF-8 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title feat(auto): Android Auto communications app feat(auto): Android Auto communications app with local stats Apr 26, 2026
- Raise minCarApiLevel to 7 (required for MESSAGING category)
- Add uses-feature android.hardware.type.automotive required=false guard
- Migrate Messages tab from plain Rows to ConversationItem (API 7+)
- Inject CoroutineDispatchers, SendMessageUseCase, MeshServiceNotifications
- Add durable callbackScope for reply/mark-as-read operations
- Populate CarContact with lastMessage fields for ConversationItem
- Remove dead Car API <6 fallback code and unused string resource
- Fix hosts_allowlist rename (car-app 1.7.0), screenManager access
- Fix test compilation: DeviceMetrics import, map matchers, junit5
- Fix detekt: suppress TooManyFunctions, LongMethod, MagicNumber,
  TooGenericExceptionCaught where justified

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants