Skip to content

chore(live-notifications): update events, local update API, asset management, typed config#765

Draft
mahmoud-elmorabea wants to merge 19 commits into
feature/live-notificationsfrom
live-notifications-ios-parity
Draft

chore(live-notifications): update events, local update API, asset management, typed config#765
mahmoud-elmorabea wants to merge 19 commits into
feature/live-notificationsfrom
live-notifications-ios-parity

Conversation

@mahmoud-elmorabea

Copy link
Copy Markdown
Contributor

Brings the Android live-notifications feature to parity with iOS and the CDP track contract: adds update lifecycle events (reported only for on-device-initiated changes), a local updateLiveNotification API, iOS-equivalent asset management (registered assets + http(s) image URLs) with a branding large-icon fallback, a typed enable/asset-registration config API, native single-slot logo rendering for LiveScore, and aligns the lifecycle event name with the CDP contract.

🤖 Generated with Claude Code

mahmoud-elmorabea and others added 11 commits June 18, 2026 21:18
Replace the bespoke /v1/live_activities REST client with two CDP track
events per the SDK→CDP contract:

- "Live Notification"        eventType=start  (local start via public API)
- "Live Notification"        eventType=end    (user dismissal)
- "Live Notification Token"  registrationType=push_to_start (per enabled type)

All contract fields ride under `properties` (the Segment track envelope is
fixed; the edge reads them there). `deviceId` is the FCM token and doubles as
`pushToStartToken`; `instanceUUID` is the client-generated activity id.
Events are gated on an identified user (auth-only). The instance-token flow is
dropped — Android has no per-instance push token.

The dismiss receiver now carries instanceUUID + activityType on the delete
PendingIntent and reads the FCM token to emit `end`. Retry/flush is handled by
the data pipeline, so the HTTP client, backoff and instance registration are
removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…le UI

Add a public ModuleMessagingPushFCM.registerLiveNotificationToken(activityType)
that registers a single type's push-to-start token on demand, on top of the
automatic registration of every enabled type at login / token change.

Harden the auto-registration: registerAll now retries (serialized via a mutex)
so it survives the brief window where the pipeline's identified state lags the
UserChangedEvent that triggered it — previously a racy pass during identify
left several types unregistered with no retry.

Sample (java_layout): add a "Register Push-to-Start Token" section to the live
notification demo — a dropdown of all supported types + an on-demand register
button with a result snackbar. The login flow already enables (and thus
auto-registers) all supported types via setLiveNotificationTypes.

Verified on emulator: login registers all 7 enabled types; the on-demand button
emits one register_push_to_start for the selected type.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…CDP events

The edge/action-destination contract renamed the live-activity type property to
`notificationType` (live activity → live notification rename). The SDK was still
emitting `properties.activityType`, so the edge resolved an empty type and
dropped it. Switch the wire key to `notificationType` for all three ops
(start, end, push_to_start); the Android-facing API parameter names keep the
`activityType` domain term.

Verified on emulator: push_to_start now sends
`properties.notificationType=io.customer.liveactivities.*`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…has no content

A push whose template fields never reached the render `data` (content not
flattened at the envelope top level, or empty) was still posting a notification
— the templates emit placeholder text ("Order #", " · $"), so the result looked
empty rather than being rejected.

Templates now return null when they have no usable content, and the handler logs
an error and skips posting instead of showing a blank notification. Envelope/
ordering handler tests get a minimal statusMessage so they still post; added
explicit guard tests (template returns null + handler doesn't notify).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…drop on-demand register API

- Rename MessagingPushModuleConfig.Builder.setLiveNotificationTypes →
  enableLiveNotificationTypes: the method gates the whole feature, so "enable"
  reads clearer than a plain setter.
- Remove the public ModuleMessagingPushFCM.registerLiveNotificationToken API and
  the registrar's on-demand register(): push-to-start registration is already
  fully automatic (every enabled type registers when the user/token changes), so
  a manual single-type API was redundant. Enabling a type is the single path.
- Drop the sample's "Register Push-to-Start Token" dropdown/button section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pshot

The snapshot version is branch-named and mutable (<branch>-SNAPSHOT), so Gradle's
changing-module cache can shadow a freshly published snapshot with a stale copy
from a previous commit. This made sample release builds compile against an old
SDK whenever a public API the sample uses was renamed/added (e.g. setLiveNotificationTypes
-> enableLiveNotificationTypes). Force --refresh-dependencies so the sample
resolves the snapshot published from the current commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er extras, revert CI workflow

- Registrar: registration is a CDP track event, so the data pipeline owns
  batching/retry/delivery — drop the SDK-side retry loop (coroutine scope, mutex,
  attempt/backoff). Emit synchronously on token/user change, deduped per
  token|userId signature.
- Stop clearing stored registration signatures on device-token delete: the
  routine delete+re-register token cycle on identify was re-sending every
  registration on each app launch. Dedup by signature already re-registers when
  the token or user actually changes.
- Make LiveNotificationDismissReceiver.EXTRA_* constants internal — they're only
  set/read by the SDK and don't belong in the public API.
- Revert the snapshot-release.yml --refresh-dependencies change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…key to notification_type

Inbound push handling, to match how the backend delivers live notifications:

- extractData now unwraps template fields nested under a `payload` object
  (merged over any flattened top-level fields), so a push that delivers content
  as `payload={...}` renders instead of being dropped as "no usable content".
  Local-start's flattened shape still works.
- Rename the inbound envelope type key `activity_type` -> `notification_type`
  (internal LiveNotificationHandler.NOTIFICATION_TYPE_KEY; no public API change),
  plus the sample's push-simulation bundle and custom-render callback key.

Verified on a physical device: a deliveryTracking push with `notification_type`
+ nested `payload` renders on the live channel (title "Order #…", progress).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…I, asset management, typed config

- Emit `update` lifecycle events (start/update/end); report only on-device-initiated changes
- Add updateLiveNotification API mirroring local start
- Match iOS asset-management behavior + load image URLs; branding logoAssetKey large-icon fallback
- Typed enableLiveNotificationTypes(enum) + enableCustomLiveNotificationTypes + registerLiveNotificationAsset
- LiveScore renders a team/league logo natively via the single large-icon slot
- Align lifecycle event name with the CDP contract ("Live Notification Event")

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

Sample app builds 📱

Below you will find the list of the latest versions of the sample apps. It's recommended to always download the latest builds of the sample apps to accurately test the pull request.


@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.72008% with 169 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.65%. Comparing base (050f431) to head (e702472).

Files with missing lines Patch % Lines
...agingpush/livenotification/LiveNotificationData.kt 59.09% 35 Missing and 1 partial ⚠️
...o/customer/messagingpush/ModuleMessagingPushFCM.kt 0.00% 24 Missing ⚠️
...ngpush/livenotification/template/TemplateAssets.kt 17.85% 18 Missing and 5 partials ⚠️
...omer/messagingpush/Api36LiveNotificationBuilder.kt 0.00% 11 Missing ⚠️
...ivenotification/LiveNotificationDismissReceiver.kt 0.00% 10 Missing ⚠️
...ngpush/livenotification/LiveNotificationManager.kt 81.13% 6 Missing and 4 partials ⚠️
...ush/livenotification/template/LiveScoreTemplate.kt 52.38% 1 Missing and 9 partials ⚠️
...push/livenotification/LiveNotificationRegistrar.kt 42.85% 7 Missing and 1 partial ⚠️
.../customer/messagingpush/LiveNotificationHandler.kt 80.64% 3 Missing and 3 partials ⚠️
...ngpush/livenotification/LiveNotificationPayload.kt 44.44% 1 Missing and 4 partials ⚠️
... and 11 more
Additional details and impacted files
@@                       Coverage Diff                        @@
##             feature/live-notifications     #765      +/-   ##
================================================================
+ Coverage                         66.80%   67.65%   +0.85%     
- Complexity                         1041     1087      +46     
================================================================
  Files                               177      180       +3     
  Lines                              5711     5911     +200     
  Branches                            786      829      +43     
================================================================
+ Hits                               3815     3999     +184     
+ Misses                             1607     1585      -22     
- Partials                            289      327      +38     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
  • java_layout: live-notifications-ios-parity (1782903921)

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

📏 SDK Binary Size Comparison Report

Module Last Recorded Size Current Size Change in Size
core 34.07 KB 34.07 KB ✅ No Change
datapipelines 43.29 KB 43.29 KB ✅ No Change
messagingpush 70.20 KB 75.79 KB ⬆️ +5.59KB
messaginginapp 121.84 KB 121.84 KB ✅ No Change
tracking-migration 22.89 KB 22.89 KB ✅ No Change

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
  • kotlin_compose: live-notifications-ios-parity (1782903919)

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

Build available to test
Version: live-notifications-ios-parity-SNAPSHOT
Repository: https://central.sonatype.com/repository/maven-snapshots/

…/update API

- Regenerate messagingpush.api for the new public surface (typed enable + custom
  types, registerLiveNotificationAsset, updateLiveNotification, LiveNotificationAsset,
  branding logoAssetKey, LiveNotificationType enum)
- java_layout demo start/update API buttons now use the typed LiveNotificationData overloads

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
  • kotlin_compose: live-notifications-ios-parity (1782908055)

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
  • java_layout: live-notifications-ios-parity (1782908045)

- Host can programmatically end a live notification it started; only the
  activityId is needed — the SDK records the activity type at render time and
  uses it for the (payload-less) `end` event
- Store activityId -> type in LiveNotificationStore (cleared on end + TTL trim);
  share the notification-id derivation with the handler
- java_layout demo gets an "End via API" button; regenerate messagingpush.api

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mahmoud-elmorabea mahmoud-elmorabea changed the title feat(live-notifications): iOS parity — update events, local update API, asset management, typed config feat(live-notifications): update events, local update API, asset management, typed config Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
  • java_layout: live-notifications-ios-parity (1782910628)

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
  • kotlin_compose: live-notifications-ios-parity (1782910631)

- On Event.ResetEvent (clearIdentify/logout), cancel every SDK-tracked live
  notification and clear its stored state, then clear registrations so the next
  identified user re-registers
- No `end` event is emitted (user is being de-identified); mirrors iOS
  endAllActivities-on-reset behavior
- Enumerate tracked activities via LiveNotificationStore (activityId -> type)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
  • java_layout: live-notifications-ios-parity (1782928096)

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
  • kotlin_compose: live-notifications-ios-parity (1782928092)

@mahmoud-elmorabea mahmoud-elmorabea changed the base branch from live-notifications-cdp-track-contract to feature/live-notifications July 2, 2026 11:09
@mahmoud-elmorabea mahmoud-elmorabea changed the title feat(live-notifications): update events, local update API, asset management, typed config chore(live-notifications): update events, local update API, asset management, typed config Jul 2, 2026
…h iOS parity

Rework live notifications to the finalized cross-platform contract and match the
iOS SDK's presentation-role field model, wire envelope, and end behavior.

Field model & templates
- Presentation-role fields (header/title/subtitle/status/statusMessage/…) in
  LiveNotificationFields; drop SDK-composed strings (no "Order for <id>")
- statusColor sent as a styling value; staleMessage rendered as body when present
- Rework all 5 templates (LiveScore, DeliveryTracking, CountdownTimer,
  FlightStatus, AuctionBid) to read the new flat payload fields
- Required-field errors logged; optional fields hidden gracefully

Wire envelope
- LiveNotificationData splits attributes() / contentState() for the local API
- Lifecycle client emits attributes + contentState (no payload envelope);
  attributesType is not sent (iOS-only)

Behavior
- End no longer force-cancels when no dismissal_date: setOngoing(false) +
  setAutoCancel(true) so the ended notification stays dismissible (iOS parity)
- Backend-initiated (push) end is not echoed as a lifecycle event; user
  dismissal reports end via LiveNotificationDismissReceiver

Tooling & tests
- Regenerate messagingpush public-API dump
- Update templates/data/lifecycle/manager tests + sample demo screen (232 tests)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
  • java_layout: live-notifications-ios-parity (1783037638)

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
  • kotlin_compose: live-notifications-ios-parity (1783037644)

…UUIDv4

The backend expects ULIDs for live notification instance IDs. Replace the
locally-minted UUIDv4 with a dependency-free ULID generator: 48-bit ms
timestamp + 80-bit SecureRandom randomness, Crockford Base32, 26-char uppercase
canonical form. Mirrors the iOS SDK's ULID minting so both platforms share one
id format.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
  • java_layout: live-notifications-ios-parity (1783343602)

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
  • kotlin_compose: live-notifications-ios-parity (1783343610)

…entify race)

On the session where the user identifies, the registrar's UserChangedEvent
subscriber could run before the data pipeline's isUserIdentified flag settled
(analytics.userId() is a cache updated asynchronously after identify), so every
registration was dropped with no retry — the device+user went unregistered until
the next app launch.

The registrar is now the authoritative identity gate: it registers only while
identified, deriving identity from UserChangedEvent.userId (carried synchronously)
instead of the laggy pipeline flag. The client no longer re-checks isUserIdentified
for the registration event (lifecycle events keep their gate). No delays/polling —
attribution stays correct because the analytics store reducer is applied before the
registration track is enqueued. Matches the in-app module's UserChangedEvent handling.

Adds LiveNotificationRegistrarTest and updates the client gate tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
  • java_layout: live-notifications-ios-parity (1783371387)

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
  • kotlin_compose: live-notifications-ios-parity (1783371394)

…iOS + backend

Aligns the Android live-notifications contract with the finalized iOS SDK and the
Customer.io backend push payload:

- Route on `cioInstanceId` instead of `activity_id` (the inbound detection gate and
  local-start writer), matching the backend envelope and iOS attributes key.
- `end` renders the final content-state and leaves it posted for the user to dismiss;
  the SDK never auto-removes it (dropped the `dismissal_date` scheduling machinery),
  matching iOS leaving an ended Live Activity on screen.
- CDP lifecycle/token events report the instance id under `cioInstanceId`.
- Field-model, envelope, timestamp (epoch seconds) and template rendering alignment,
  including nested delivery-tracking progress.

Full :messagingpush:testDebugUnitTest suite passes; apiCheck passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
  • java_layout: Build failed. See CI job logs to determine the issue and try re-building.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
  • kotlin_compose: live-notifications-ios-parity (1783545513)

…eliveryTracking ctor

- Reorder PROP_CIO_INSTANCE_ID import in LiveNotificationLifecycleClientTest to satisfy
  ktlint import-ordering (the CDP-key rename moved its lexicographic position).
- Update the java_layout sample's DeliveryTracking construction to the new nested
  Progress(current, total) constructor (replaced the flat stepCurrent/stepTotal args),
  fixing the compileReleaseJavaWithJavac break.

ktlint clean on the changed file; :samples:java_layout:compileReleaseJavaWithJavac and
:messagingpush test sources compile.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
  • java_layout: live-notifications-ios-parity (1783550124)

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
  • kotlin_compose: live-notifications-ios-parity (1783550133)

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.

1 participant