Skip to content

feat: app-lifecycle [android]#29

Closed
choudlet wants to merge 1 commit into
mainfrom
chrish/sc-36799/story-application-lifecycle-events
Closed

feat: app-lifecycle [android]#29
choudlet wants to merge 1 commit into
mainfrom
chrish/sc-36799/story-application-lifecycle-events

Conversation

@choudlet

Copy link
Copy Markdown
Collaborator

Summary

Implements the four application-lifecycle events for the Android SDK (Shortcut sc-36799):

  • Application Installed — emitted on first launch with no prior (version, build) and no identity storage.
  • Application Updated — emitted on first launch after (version, build) changes, OR when no (version, build) is stored but identity storage already exists (existing user upgrading from a pre-lifecycle SDK build); previous values default to "unknown" in that upgrade case.
  • Application Opened — emitted on cold launch (after Installed/Updated if applicable) with from_background:false, and on every background → active transition with from_background:true. Skipped when the process is woken in the background (push, JobScheduler, WorkManager) — the next true foreground entry emits the cold-launch-style event instead. Optional url and referring_application properties are populated when the host app calls the new handleDeepLink(...) method before the next emit.
  • Application Backgrounded — emitted on active → background, before the existing flush, so it lands in the same drain.

All four flow through the existing track() path (enrichment + dispatcher).

Public API

  • InitOptions.trackLifecycleEvents: Boolean = true — opt-out flag.
  • AnalyticsInterface.handleDeepLink(uri: Uri, sourceApplication: String? = null) — host hook for populating optional deep-link properties on the next Application Opened. Buffer is one-shot and cleared after emit.

Storage

(version, build) are persisted in a dedicated SharedPreferences file (com.metarouter.analytics.lifecycle) so IdentityManager.reset() and MetaRouterAnalyticsClient.reset() cannot wipe install/update state. Keys: metarouter:lifecycle:version, metarouter:lifecycle:build.

Test plan

  • ./gradlew :metarouter-sdk:test — 511 tests, 0 failures (debug + release variants).
  • LifecycleEventTrackerTest covers all eleven emission rules from the plan: install/update detection, cold-launch foreground vs deferred, dedup against the imminent ProcessLifecycleOwner.onStart, multiple background↔foreground cycles, Application Backgrounded empty payload, enabled=false no-op, and one-shot deep-link buffer.
  • LifecycleStorageTest covers round-trip, persistence across instances, isolation from IdentityStorage.clear(), and key-name parity with the cross-platform contract.
  • MetaRouterAnalyticsClientTest integration cases: cold launch persists (version, build) and enqueues at least the Installed event; trackLifecycleEvents=false produces zero lifecycle events; client.reset() does NOT clear lifecycle storage.
  • InitOptionsTest covers default + explicit flag values.

Notes for reviewers

  • Cold-launch double-fire dedup uses two AtomicBooleans: suppressNextForeground (set when cold-launch emits Opened, suppresses imminent observer-driven onStart) and coldLaunchOpenedDeferred (set when process started in background, consumed by first true foreground entry).
  • build is serialized as a string in the wire payload to match the cross-platform schema; stored as a string in LifecycleStorage. (context.app.build retains its existing numeric Android type — these two fields intentionally diverge.)
  • Default test options in MetaRouterAnalyticsClientTest set trackLifecycleEvents = false so existing assertions on queueLength are not affected by the new automatic emission. Lifecycle integration cases re-enable explicitly via options.copy(trackLifecycleEvents = true).

Adds automatic emission of the four application-lifecycle events,
gated behind a new `InitOptions.trackLifecycleEvents` flag (default true).
Events flow through the existing track() path. Persisted (version, build)
state lives in a dedicated SharedPreferences file so it survives reset().

Closes sc-36799.
@github-actions

Copy link
Copy Markdown

Test Results

   64 files  + 4     64 suites  +4   1m 8s ⏱️ -3s
  511 tests +25    511 ✅ +25  0 💤 ±0  0 ❌ ±0 
1 022 runs  +50  1 022 ✅ +50  0 💤 ±0  0 ❌ ±0 

Results for commit 766cf1e. ± Comparison against base commit 50b2102.

@choudlet

Copy link
Copy Markdown
Collaborator Author

Superseded by stacked PRs #30 (sc-38233), #31 (sc-38234), #32 (sc-38235), #33 (sc-38236). The work has been split per the iOS slicing pattern (parity with sc-36764) and reorganized to incorporate post-PR refinements: openURL rename, trackLifecycleEvents opt-in default, LifecycleCoordinator extraction, single AppContext source of truth, Logger.warn on disabled openURL, README docs.

Each new PR is reviewable in isolation; merge in numeric order (#30#31#32#33) and they will auto-retarget to main as predecessors merge.

@choudlet choudlet closed this Apr 27, 2026
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