Skip to content

notifications: group Android notification history by sender - #1973

Open
TheFallen058 wants to merge 8 commits into
coredevices:mainfrom
TheFallen058:feature/notification-history-grouping
Open

TheFallen058 wants to merge 8 commits into
coredevices:mainfrom
TheFallen058:feature/notification-history-grouping

Conversation

@TheFallen058

@TheFallen058 TheFallen058 commented Aug 31, 2026

Copy link
Copy Markdown

Summary

  • Add optional sender-based grouping to notification history.
  • Support Never, 1 Day, 1 Week, and All grouping ranges.
  • Display grouped messages newest-first with existing notification actions.
  • Keep old, unsupported, and iOS notifications ungrouped.
  • Apply grouping only to history, not incoming notification popups.
  • Skip corrupt history records without hiding subsequent notifications.
  • Group Android titles formatted Conversation: Sender by conversation when sender metadata is unavailable.

Testing

  • Full unit suite: 332/332 passed
  • QEMU Gabbro firmware build passed
  • Pebble Time 2 (obelix@pvt) dual-slot release build passed
  • Sideloaded and manually verified on a retail Pebble Time 2 + Galaxy s25
  • clang-format and gitlint passed

Limitations

  • Grouping prefers Android sender metadata when available.
  • Otherwise, titles formatted Conversation: Sender use Conversation as the grouping key.
  • This title-prefix fallback can over-group unrelated Android notifications sharing the same prefix.
  • Grouping keys are not separated by application.
  • Dismissing an entire group is intentionally unsupported.

Compatibility

  • Android notifications are grouped using sender metadata or a Conversation: Sender title.
  • iOS/ANCS notifications remain ungrouped and retain existing history and dismissal behavior.

AI assistance

GPT-5.6 Sol assisted with implementation. Relevant commits contain
Co-authored-by trailers. The resulting behavior was manually tested on hardware.

Example:
Before:
image
After:
image
image
image

@TheFallen058

TheFallen058 commented Aug 31, 2026

Copy link
Copy Markdown
Author

Related work

Related to #1970. This implementation additionally provides configurable age ranges, sender-based grouping, transcript view, iOS compatibility, storage optimizations, and unit coverage.

@gmarull
gmarull requested a review from ericmigi August 31, 2026 19:43
@TheFallen058
TheFallen058 force-pushed the feature/notification-history-grouping branch from 96979e8 to 7969ad8 Compare September 11, 2026 07:43
@gmarull

gmarull commented Sep 17, 2026

Copy link
Copy Markdown
Member

@ericmigi

TheFallen058 and others added 3 commits September 17, 2026 10:08
Avoid deserializing notification payloads older than the grouping range.

Co-authored-by: GPT-5.6 Sol <noreply@openai.com>
Signed-off-by: Aliaksandr Karnilovich <thefallen058@gmail.com>
Persist Never, one-day, one-week, and all-history grouping options.

Co-authored-by: GPT-5.6 Sol <noreply@openai.com>
Signed-off-by: Aliaksandr Karnilovich <thefallen058@gmail.com>
Track notification IDs by sender while preserving chronological ordering and removal behavior.

Co-authored-by: GPT-5.6 Sol <noreply@openai.com>
Signed-off-by: Aliaksandr Karnilovich <thefallen058@gmail.com>
@gmarull
gmarull force-pushed the feature/notification-history-grouping branch from ad2c5a7 to ffd2f5e Compare September 17, 2026 08:11
TheFallen058 and others added 5 commits September 17, 2026 10:29
Display collapsed sender rows and open grouped notifications in a newest-first transcript.

Co-authored-by: GPT-5.6 Sol <noreply@openai.com>
Signed-off-by: Aliaksandr Karnilovich <thefallen058@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Localize grouped titles, align setting labels, and use checked sender allocation.

Co-authored-by: GPT-5.6 Sol <noreply@openai.com>
Signed-off-by: Aliaksandr Karnilovich <thefallen058@gmail.com>
Keep age-aware iteration moving so malformed records do not hide later notifications.

Co-authored-by: GPT-5.6 Sol <noreply@openai.com>
Signed-off-by: Aliaksandr Karnilovich <thefallen058@gmail.com>
Only switch to grouped history controls when a collapsed sender group exists,
so iOS and single-message histories retain navigation and dismiss-all behavior.

Co-authored-by: GPT-5.6 Sol <noreply@openai.com>
Signed-off-by: Aliaksandr Karnilovich <thefallen058@gmail.com>
Use the text before a title's sender separator as a fallback conversation key
when the Android companion does not provide sender metadata.

Co-authored-by: GPT-5.6 Sol <noreply@openai.com>
Signed-off-by: Aliaksandr Karnilovich <thefallen058@gmail.com>
@gmarull
gmarull force-pushed the feature/notification-history-grouping branch from ffd2f5e to 7986806 Compare September 17, 2026 08:31
@gmarull

gmarull commented Sep 17, 2026

Copy link
Copy Markdown
Member

Title-prefix fallback over-groups unrelated notifications

prv_conversation_range_from_title() (src/fw/apps/system/notifications_history.c) keys a notification on whatever precedes the first ": " in its title when no AttributeIdSender is present. That is the right shape for Group: Sender chat titles, but it also collapses titles that merely share a prefix: Re: Invoice + Re: Lunch become a Re (2) group, Error: disk full + Error: login become Error (2), and the group header/preview then hides the actual senders.

The firmware-side options are not great:

  • Restricting the fallback to LayoutIdCommNotification, or requiring AttributeIdSender, would disable grouping entirely on Android today: the Core companion always sends Layout.GenericNotification with the raw EXTRA_TITLE and never sets Sender (libpebble3/.../notification/LibPebbleNotification.kt, processor/BasicNotificationProcessor.kt).
  • Any narrower title rule is just another heuristic.

So I'd leave the heuristic in as-is for now, and fix the data at the source.

Proposed CoreApp fix

Populate AttributeIdSender on Android notifications from the notification extras, which already carry the real conversation identity:

  1. NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(notification) — the companion already calls this in NotificationImage.kt for image batches. Use conversationTitle when isGroupConversation is set, otherwise the newest Message.person?.name.
  2. Fall back to notification.people() (first Person.name) for apps that set android.people.list but no messaging style.
  3. Emit it via the existing DSL: attributes { sender { it } } next to title { } in LibPebbleNotification.toTimelineNotification().

On the watch AttributeIdSender is not rendered by the notification layout (only calendar_layout.c and timeline_actions.c read it), so this is UI-neutral, and the sender path in prv_group_sender_for_item() already takes precedence over the title fallback. Once the companion ships it, the title heuristic can be removed in a follow-up.

@gmarull

gmarull commented Sep 17, 2026

Copy link
Copy Markdown
Member

@sjp4 please check mobile suggested changes

@TheFallen058

TheFallen058 commented Sep 17, 2026

Copy link
Copy Markdown
Author

To @gmarull
One adjustment to the 'sender' rule, though,
otherwise it regresses group chats.

The title fallback keys on the text before the first ": ", so PG | Elite: Aloha
and PG | Elite: Yeuhen both group under the conversation PG | Elite (tests to that exist). When
AttributeIdSender is present the firmware takes it verbatim and never splits it
(test_notifications_history__sender_attribute_is_not_split), so whatever the
companion puts there becomes the grouping key outright.

That matters because isGroupConversation() is false on the
extractMessagingStyleFromNotification() path unless the app explicitly set the
android.isGroupConversation extra — the conversationTitle != null compat fallback
is gated on mBuilder != null, which extraction never sets. So for apps that set a
conversation title but never call setGroupConversation(true), the rule falls through
to Message.person?.name and a group chat fragments into one row per participant.
Worse than the Re (2) over-grouping it's meant to fix.

Suggested flow for the sender value:

  1. conversationTitle whenever it is non-null (not gated on isGroupConversation).
  2. Otherwise the newest incoming Message.person?.name.
  3. No notification.people() fallback — in a group chat android.people.list holds
    every participant, so the first name is arbitrary and unstable across notifications.

@ericmigi

Copy link
Copy Markdown
Collaborator

I have the same question as in #1970 (comment)

Please share some more screenshots of how it would look when entering from a regular notification rather than opening the notification app itself.

sjp4 pushed a commit to coredevices/mobileapp that referenced this pull request Sep 17, 2026
The watch groups notification history by AttributeIdSender, but Android
notifications only ever carried the raw EXTRA_TITLE, so the firmware
falls back to a title-prefix heuristic that also merges unrelated
"Re: ..." / "Error: ..." titles into one group.

Derive the sender from the MessagingStyle extras: the conversation title
whenever one is set, otherwise the Person of the newest incoming message.
The title is not gated on isGroupConversation because on the extraction
path the flag is only ever the raw android.isGroupConversation extra,
which apps that set a title but never call setGroupConversation(true)
leave false; gating on it would fragment those group chats into one
row per participant. A group chat without a title sends no sender so
the watch keeps its title fallback, and android.people.list is not used
because its order is app-defined and unstable across notifications.

The watch does not render Sender on notifications, so this only affects
history grouping.

Related: coredevices/PebbleOS#1973

Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants