feat: SDK Automatic Push Behavior Integration (rel/4.5.0)#517
feat: SDK Automatic Push Behavior Integration (rel/4.5.0)#517glenn-klaviyo wants to merge 12 commits into
Conversation
…king [MAGE-767] (#493) * feat(core): add getManifestBoolean and automatic push tracking key Add a Config.getManifestBoolean accessor (interface + KlaviyoConfig override + Context extension mirroring getManifestInt) and the Constants.AUTOMATIC_PUSH_TRACKING manifest key ("com.klaviyo.automatic_push_tracking"). The key lives in core because push token telemetry must read it and core cannot depend on push-fcm. BaseTest mocks getManifestBoolean to return the supplied default so existing suites run with the flag off. The config test covers both the Tiramisu and pre-Tiramisu getApplicationInfo paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(push): route taps through trampoline for automatic open tracking Extend KlaviyoTrampolineActivity (from #448) so that, when a host opts in via the com.klaviyo.automatic_push_tracking manifest flag, body / deep_link / open_app taps route through the trampoline, which calls Klaviyo.handlePush itself — removing the need for handlePush in host Activities. - forDestination intent factory (setClassName test seam) shared by body and button intents; deep link carried as intent data. - handleTrampolineIntent dispatches on intent contents: browser URL (unchanged, #448) else deep-link-via-VIEW (when no DeepLinkHandler) / launch fallback. - onNewIntent override + Xiaomi-safe finish posted after dispatch. - Manifest: singleTask + taskAffinity="" + excludeFromRecents. - KlaviyoNotification reads the flag once per build and routes body/button intents accordingly; flag off is byte-identical to today, open_url always routes through the trampoline regardless of the flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: assert extras parity on trampoline routing and strengthen config test - KlaviyoNotificationTest: when automatic tracking is on, assert the body and action-button intents routed through KlaviyoTrampolineActivity still carry the Klaviyo extras (and the notification tag for buttons), matching the flag-off path. - KlaviyoConfigTest: back the metadata Bundle with a real map so getManifestBoolean is verified against genuine present/absent semantics rather than a stubbed echo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * revert(push): drop Xiaomi runOnUiThread finish workaround Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(push): clarify deep link is kept for handler when unresolvable Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(push): sanitize trampoline logs and preserve CLEAR_TOP on forward - Drop URL/deep-link interpolation from dispatch logs (keep branch label) - Carry CLEAR_TOP onto the forwarded destination for back-stack parity -Claude Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(core): don't log full intent on unresolved startActivity Intent.toString() includes data/extras which can carry payload-derived URLs (deep links, browser URLs) with tokens. Log action+component only. -Claude Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(fixtures): stub Intent.component getter in MockIntent The sanitized startActivityIfResolved log reads intent.component; the mockkConstructor mock threw on the unstubbed getter, breaking DeepLinkingTest. -Claude Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(push): read auto-tracking flag from build-time context - Read the manifest flag via context.getManifestBoolean in buildNotification so opt-in works even if FCM builds a notification before Klaviyo.initialize (Registry.config returns the default until init). Mirrors getManifestInt usage. - MockIntent: mirror Android by exposing a ComponentName via component after setClassName, instead of null. -Claude Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(push): log deep-link launcher fallback at warning not error Degraded-but-handled fallback per the logging-severity convention. -Claude Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(push): trim verbose comments per review feedback - Condense intent-logging, manifest-flag, and auto-tracking comments - Drop redundant re-export cross-reference in Constants - Add lead-in framing for the opt-in manifest snippet - Document the trampoline activity's invisible/transient manifest attrs -Claude * docs(push): describe trampoline manifest attrs individually Replace the open-tracking-focused block comment with a neutral summary plus a per-attribute reference, since the trampoline also routes deep links and URLs. -Claude * docs(push): drop manifest snippet from auto-tracking const KDoc Integration how-to belongs in the dev docs, not the KDoc; sibling METADATA_* consts carry none. Keep a one-line description since the opt-in behavior is non-obvious. -Claude * fix(push): warn when trampoline finds no host launch intent Restores diagnostic parity with the non-trampoline path: when automatic push tracking is on and the host has no launcher activity, makeLaunchIntent returns null and startDestination silently no-ops. Log a warning so the missing launcher is still debuggable. No behavior change otherwise — the open is still tracked and nothing crashes. -Claude --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…68) (#495) * feat(push): dedup auto-tracked push opens via in-memory delivery guard Adds an in-memory guard so a single notification tap tracks one $opened_push even when a host opts into the trampoline but leaves a manual Klaviyo.handlePush call in place. The guard keys on the per-delivery tm field of the _k tracking payload, falling back to an SDK-generated per-notification UUID stamped on the trampoline intent when tm is absent (local notifications, previews, and other non-campaign sends). Both keys ride forward onto the host's destination intent, so the trampoline's handlePush and the host's manual call resolve to the same key and collapse to one event/dismissal/deep -link dispatch, while distinct notifications never collide. The guard engages only for intents the trampoline marks as auto-tracked, so manual-only integrations behave exactly as before. open_url is left unmarked: it always trampolines regardless of the flag and the host never receives a forwarded open_url tap, so there is nothing to deduplicate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(push): DRY up BoundedIdSet concurrency test harness Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(push): surface worker failures in BoundedIdSet concurrency harness Count down in finally and assert collected worker throwables after await, so a failing worker reports its real cause instead of a misleading timeout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(push): flatten handlePush with early-return guards; tighten dedup docs Address review feedback: replace the safeApply/takeIf/elvis chain with early-return guards and a single State lookup, keeping safeApply on each side effect so the SDK never crashes the host. Reword the dedup guard comment, trim the constant/property docs to point at the logic, and drop the raw delivery id from the duplicate-open log. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(push): dedupe all Klaviyo opens by delivery id Address review: drop the auto-tracked marker gate and dedupe every Klaviyo push open on its delivery id, so the trampoline call and a host's manual handlePush collapse to one open regardless of ordering or marker propagation. Reorder so the isKlaviyoNotificationIntent guard runs first. Removes NOTIFICATION_AUTO_TRACKED_EXTRA and isAutoTrackedOpen; the generated NOTIFICATION_UID_EXTRA remains as the tm-less fallback key. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(core): guard BoundedIdSet with a private lock Lock on a private monitor instead of the instance (@synchronized), so external holders can't contend with or deadlock the internal locking. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(push): stamp a per-notification dedup id on all tap intents Extend the tm-less fallback dedup key to every notification, not just trampoline ones: generate one NOTIFICATION_UID_EXTRA per notification in buildNotification and stamp it on the body and every action-button intent (auto-tracking or not). handlePush now dedupes any notification's opens by tm, else this id — so a manual handlePush called twice on the same tm-less notification collapses to one open. Body and buttons of a notification share the id (distinct notifications differ), so distinct targets no longer split. Moves the stamp out of the trampoline factory. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…follow-up) (#500) * refactor(push): extract push-open handling from Klaviyo into KlaviyoPushOpenHandler Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(push): move handlePush tests into KlaviyoPushOpenHandlerTest Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(push): address CodeRabbit feedback on KlaviyoPushOpenHandlerTest - Add coverage for the notification-dismissal branch - Replace !! with requireNotNull for _k extra lookups - Assert warning log level rather than exact message text - Drop redundant Registry.unregister<Config>() in cleanup -Claude --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(push): automatic push token registration (MAGE-769) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(push): namespace automatic push tracking manifest keys under com.klaviyo.push. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(push): broaden fetcher error handling to honor must-not-throw contract Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(push): address review nits — method name, provider params, test DRY Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(push): dedupe registerMockPushTokenFetcher into BaseTest -Claude --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…(MAGE-765) (#506) * feat(push): add X-Klaviyo-Sdk-Features header to push-token-register (MAGE-765) Reports when a host has explicitly disabled automatic push token forwarding while automatic push tracking is on, so the backend can track opt-out adoption (MAGE-766). Scoped to PushTokenApiRequest only; omitted entirely when tracking is off or forwarding is left at its default-on state, since only the opt-out case needs reporting. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(push): always send both SDK feature flags in push-token header (MAGE-765) Previously the X-Klaviyo-Sdk-Features header was omitted unless automatic push tracking was on and forwarding was disabled, carrying only the forwarding key. Now it's always attached with both auto_push_tracking and auto_push_token_forwarding so all four flag combinations are visible for analysis, not just the opt-out case. auto_push_token_forwarding now mirrors the disable flag directly (1 unless explicitly disabled), independent of the tracking flag. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(push): only include SDK feature flags whose manifest key is set (MAGE-765) Previously both auto_push_tracking and auto_push_token_forwarding were always present in X-Klaviyo-Sdk-Features, falling back to a default value when the host hadn't set the corresponding manifest key. Now each attribute is included only if its manifest key is actually declared, so the header reflects what the host explicitly configured rather than an assumed default. Adds Config.hasManifestKey(key) (mirroring getManifestBoolean/Int) to distinguish "key absent" from "key present and false", which getManifestBoolean's single default-value return couldn't do. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(core): resolve duplicate DISABLE_AUTOMATIC_TOKEN_FORWARDING after merge The merge from feat/auto-push-tracking (PR #503) landed a second declaration of this constant without a git conflict, since both additions fell in non-overlapping diff hunks. Keeps PR #503's PUSH_PREFIX-based definition, which is also the one Klaviyo.kt actually uses to gate automatic push token registration; our header logic already references the constant by name, so no other change is needed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * refactor(core): extract scoped SdkFeatures catalog for X-Klaviyo-Sdk-Features (MAGE-765) Replaces the inline header-building in PushTokenApiRequest with a reusable, scoped feature catalog in core, mirroring the iOS design (klaviyo-swift-sdk#640) for cross-platform parity. - SdkFeatureScope enumerates the request surfaces a feature is reported on; SdkFeatureKey is a central catalog pairing each feature's wire name with its manifest key, scope, and raw->reported value mapping. - SdkFeatures.headerValue(scope) serializes only in-scope features whose manifest key is present, so a feature can never leak onto a request outside its scope. Adding a future (e.g. profile) feature header is a new key + scope, reusing the same serialization. Pure refactor: the emitted header is byte-identical for every manifest combination. Existing PushTokenApiRequestTest/EventApiRequestTest pass unchanged as the regression guard; SdkFeaturesTest covers the catalog. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* docs(readme): document automatic push integration (MAGE-771) Restructure the Push Notifications section to lead with the zero-boilerplate automatic integration (Option A) while preserving the manual path (Option B). - Add a "Which option?" callout and an Option A section covering the com.klaviyo.push.automatic_push_tracking manifest flag, automatic open tracking via the trampoline, and token registration at initialize() and on each foreground, plus the disable_automatic_token_forwarding opt-out. - Correct the onNewToken wording: it only fires on token generation/rotation, so it can miss a pre-existing token or when FCM auto-init is disabled. - Update the table of contents with Option A / Option B anchors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(readme): trim Option A push notes (MAGE-771) Remove the token de-dup detail, the initialize() reminder, the optional DeepLinkHandler subsection, and the MAGE-708 broadcast-reception aside from the automatic integration section. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(readme): address PR review feedback (MAGE-771) - Soften "token rotations are always caught" to "picked up on those events". - Scope the disable_automatic_token_forwarding claim to the init/foreground fetch, and note onNewToken still forwards while KlaviyoPushService is the registered FCM service. - Clarify that onNewToken only forwards when KlaviyoPushService is the registered FCM service; custom services must call setPushToken themselves. - Link the sample-app TODO to MAGE-770. - Nest Collecting Push Tokens and Receiving Push Notifications under Option B (and matching TOC) so Option B is no longer an empty wrapper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…n (MAGE-770) (#508) * feat(sample): product flavors for manual vs automatic push integration (MAGE-770) Add an `integration` flavor dimension with `manual` and `automatic` flavors so the sample demonstrates both Klaviyo push integration styles side by side, mirroring the iOS SPMExample / SPMExampleAutomatic split. - manual: unchanged Option B path (app fetches the FCM token + calls setPushToken, and calls Klaviyo.handlePush(intent) on notification taps). - automatic: opts in via the com.klaviyo.push.automatic_push_tracking manifest flag; the SDK auto-registers the token at initialize()/foreground and routes taps through KlaviyoTrampolineActivity, so SampleActivity carries zero push boilerplate. SampleActivity.kt moves out of src/main into each flavor source set (AGP can't exclude a main class per flavor); everything else stays shared under src/main. README documents how to switch build variants. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sample): refresh auto-registered push token in UI on resume (MAGE-770) The automatic flavor never calls updatePushToken (the SDK auto-registers the token asynchronously), and SampleViewModel only seeded pushToken once at construction, so the Push Token field could stay empty even though registration succeeded. Re-read Klaviyo.getPushToken() from the existing ON_RESUME lifecycle observer in SampleView so the displayed token reflects the auto-registered value. Shared code only — the automatic SampleActivity stays free of push boilerplate. Only overwrites when the SDK has a token, so a shown value is never clobbered with an empty one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sample): drop iOS example reference from flavor docs (MAGE-770) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(sample): fix stale build.gradle link in README (MAGE-770) Line 56 still pointed at ./build.gradle; the file uses the Kotlin DSL extension. Matches the corrected link on line 12. Flagged by CodeRabbit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(push): decouple auto push tracking and token forwarding (MAGE-886) (#509) * feat(push): decouple auto push tracking and token forwarding (MAGE-886) Replace the nested `disable_automatic_token_forwarding` escape hatch with a fully independent opt-in flag `automatic_token_forwarding`. The two push flags are now orthogonal: - `automatic_push_tracking` gates only automatic open tracking (trampoline) - `automatic_token_forwarding` gates only automatic token fetch/forwarding Both flags are unshipped (feature branch only), so there is no backward-compat cost. `KlaviyoPushService.onNewToken()` still forwards unconditionally; hosts opt out by registering their own FirebaseMessagingService. Also retargets the SdkFeatures telemetry entry to the new flag (dropping the inverted reportedValue); the `auto_push_token_forwarding` wire name is unchanged so the backend contract is preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(push): address review feedback on docs/comments (MAGE-886) Per review on #509: - Drop the added KDoc block on KlaviyoPushService.onNewToken() - Remove flag-independence note from Constants KDoc (belongs in README) - Stop exposing internal KlaviyoTrampolineActivity in sample docs; describe the behavior at the public API level (Klaviyo.handlePush) instead Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(sample): set automatic flavor push flags to true (MAGE-886) The automatic flavor manifest had both automatic_push_tracking and automatic_token_forwarding at false, so automaticDebug behaved like the manual flavor despite the Option A docs. Restore both to true to match the intended automatic integration and the sample/PR documentation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* docs(readme): document decoupled push flags (MAGE-888) Update the root README Push Notifications section for the independent push flags introduced in MAGE-886 (#509): - Option A now enables both automatic_push_tracking and the new opt-in automatic_token_forwarding - Replace the removed disable_automatic_token_forwarding opt-out with an "independent flags" section documenting all four valid combinations - Describe open tracking at the API level (Opened Push / handlePush) instead of naming internal implementation - Link the sample app's automatic product flavor as a runnable reference Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(readme): drop four-combination flag table (MAGE-888) Per review feedback, remove the verbose flag-combination table; the surrounding prose already explains the independent opt-ins and the mixed-flag case, matching the iOS README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…-932) (#513) * refactor(push): rename automatic push integration manifest keys (MAGE-932) Rename the two opt-in push-integration manifest <meta-data> keys for clarity: - com.klaviyo.push.automatic_push_tracking -> com.klaviyo.push.automatic_push_open_tracking - com.klaviyo.push.automatic_token_forwarding -> com.klaviyo.push.automatic_push_token_forwarding Also renames the corresponding Kotlin constants/aliases and updates the sample app and README docs. The X-Klaviyo-Sdk-Features wire names (auto_push_tracking, auto_push_token_forwarding) are intentionally left unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test: rename push-flag test helpers to match renamed constants - setAutomaticPushTracking -> setAutomaticPushOpenTracking - setTokenForwardingEnabled -> setAutomaticPushTokenForwardingEnabled - enableAutomaticTracking -> enableAutomaticPushOpenTracking Addresses CodeRabbit review feedback on #513. -Claude --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Enterprise Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 816f86c. Configure here.
…Token (MAGE-937) (#516) * feat(push): default automatic_push_token_forwarding ON and gate onNewToken (MAGE-937) Flip automatic_push_token_forwarding to default ON and route both automatic token-collection paths — the initialize/foreground fetch and KlaviyoPushService.onNewToken — through a single shared gate (isAutomaticPushTokenForwardingEnabled in core), so automatic_push_token_forwarding="false" is a complete opt-out. The public Klaviyo.setPushToken API and SdkFeatures telemetry are intentionally unchanged. Update the sample manual flavor to opt out explicitly and reconcile the root and sample READMEs to document the default-ON behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(push): read token-forwarding flag from Context in onNewToken to avoid pre-init crash (MAGE-937) KlaviyoPushService.onNewToken can fire before Klaviyo.initialize (FCM may deliver a token at process start), so gating it via Registry.config threw MissingConfig out of the messaging service. Read the flag from the service Context instead — mirroring how KlaviyoNotification reads the sibling open-tracking flag — which is safe pre-init and still honors the opt-out. The analytics path (Klaviyo.maybeAutoRegisterPushToken) keeps reading via Registry.config (always post-initialization). Both paths share Constants.AUTOMATIC_PUSH_TOKEN_FORWARDING and the new Constants.AUTOMATIC_PUSH_TOKEN_FORWARDING_DEFAULT so their default can't drift, consistent with how the sibling open-tracking flag is read per-module. Addresses Cursor Bugbot finding on PR #516. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(readme): generalize push-provider examples in sample README (MAGE-937) Address review feedback: replace the specific "Braze/Airship/Iterable" list with "other push providers" in the sample README's token-forwarding note. -Claude Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… guide + README (#520) * docs: add 4.4.0 automatic push tracking flags migration guide and README note - Add migration guide entry for `automatic_push_open_tracking` (opt-in, default off) and `automatic_push_token_forwarding` (default on, pending #516/MAGE-937 merge) - Token-forwarding entry is explicitly marked pending klaviyo-android-sdk#516; finalize on merge before releasing docs - Add two-stage rollout summary table covering both Android and iOS flags - Add anonymous-profile note to README Anonymous Tracking section Relates to MAGE-897. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: drop pending-#516 caveats from Android migration guide MAGE-937 (#516) is landing imminently; remove the "pending merge" note, heading suffix, and table caveat, and fix the README anchor link. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: fix Anonymous Tracking anchor to point at the README Bare `#anonymous-tracking` fragment resolved against MIGRATION_GUIDE.md; link explicitly to ./README.md#anonymous-tracking. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: correct token-forwarding timing to init/foreground/refresh Per maybeAutoRegisterPushToken (called from initialize and on app foreground) plus onNewToken: the SDK forwards the current token at initialization, on foreground, and on FCM token refresh — not only "as soon as FCM delivers one". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: correct auto-push release version to 4.5.0 (Android) The automatic push behaviors ship in the upcoming 4.5.0 minor, not 4.4.0. Updated the migration guide section and the README anonymous-profile note; left install snippets and unrelated version references untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: Android-only migration guide + clarify default-on token forwarding - Remove iOS flag rows from the rollout summary (Android guide should not document iOS behavior). - Reword the intro to distinguish the opt-in open-tracking flag from the default-on token forwarding, and note the manual-control opt-out (addresses CodeRabbit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: move opt-in/out instructions to README, point migration guide there Per review feedback: configuration instructions (XML snippets) belong in the README alongside the existing Option A setup guide; the migration guide now links there instead of duplicating them. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolves conflicts in KlaviyoTrampolineActivity and its test by keeping the expanded dispatch logic from feat/auto-push-tracking and adopting the makeBrowserIntent → makeExternalIntent rename from feat/push-trampoline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

All work associated with Automatic Push Behavior Integration
#493
#495
#500
#503
#506
#507
#508
#509
#512
#513
#516