Skip to content

feat: lifecycle storage + bundle metadata foundation [1/4]#30

Open
choudlet wants to merge 2 commits into
mainfrom
chrish/sc-38233/lifecycle-storage-foundation
Open

feat: lifecycle storage + bundle metadata foundation [1/4]#30
choudlet wants to merge 2 commits into
mainfrom
chrish/sc-38233/lifecycle-storage-foundation

Conversation

@choudlet

Copy link
Copy Markdown
Collaborator

Shortcut: sc-38233
Parent: sc-36799 — Application Lifecycle Events (Android)
iOS reference: sc-38228
Slice 1 of 4 — pure additions, no behavior change yet.

Summary

Foundational types for the lifecycle-events feature. Lands safely on main immediately because nothing references the new types yet — slices 2–4 build on this.

  • LifecycleStorageSharedPreferences wrapper for (version, build) under com.metarouter.analytics.lifecycle, separate from identity prefs so reset() does not wipe install/update state.
  • IdentityStorage.hasAnyValue() — distinguishes a fresh install from a user upgrading from a pre-lifecycle SDK build (existing identity, no lifecycle keys → "Updated unknown→unknown" rather than "Installed").
  • IdentityManager.hasAnyValue() — forwarder used by the tracker (slice 2).
  • AppContext.fromContext(Context) — single-source-of-truth factory that reads PackageManager once. Slice 3 stops DeviceContextProvider from re-reading the manifest on every event.
  • LifecycleEventNames / LifecycleEventProperties — extracted constants the tracker (slice 2) and coordinator (slice 3) reference.
  • LifecycleStorageTest — round-trip + namespace-isolation coverage.

Stack

  1. this PR — storage + bundle metadata foundation
  2. sc-38234 — LifecycleEventTracker algorithm + tests
  3. sc-38235 — MetaRouterAnalyticsClient wiring + openURL public API
  4. sc-38236 — README documentation

Test plan

  • ./gradlew :metarouter-sdk:test passes
  • LifecycleStorageTest exercises round-trip + namespace isolation
  • IdentityStorage.clear() does not affect lifecycle prefs (covered in test)
  • AppContext.fromContext(...) returns sane values on a Robolectric application context

Foundational types for the application-lifecycle events feature. Pure
additions; no behavior change to MetaRouterAnalyticsClient yet.

- LifecycleStorage: SharedPreferences wrapper for (version, build) under
  com.metarouter.analytics.lifecycle, separate from identity prefs so
  reset() does not wipe install/update state
- IdentityStorage.hasAnyValue(): identifies users that pre-date the
  lifecycle SDK (existing identity, no lifecycle keys) so the tracker
  can emit Application Updated instead of Application Installed
- IdentityManager.hasAnyValue(): forwarder
- AppContext.fromContext(): single-source-of-truth factory that reads
  PackageManager once. The lifecycle subsystem and DeviceContextProvider
  will share the cached snapshot so per-event enrichment never re-reads
  the manifest
- LifecycleEventNames / LifecycleEventProperties: extracted constants
  used by the tracker and (later) the coordinator

Refs: sc-38233
@github-actions

github-actions Bot commented Apr 27, 2026

Copy link
Copy Markdown

Test Results

 64 files  + 4   64 suites  +4   1m 7s ⏱️ -4s
498 tests +12  498 ✅ +12  0 💤 ±0  0 ❌ ±0 
996 runs  +24  996 ✅ +24  0 💤 ±0  0 ❌ ±0 

Results for commit d244b06. ± Comparison against base commit 50b2102.

♻️ This comment has been updated with latest results.

val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(0))
} else {
@Suppress("DEPRECATION")

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are legacy overloads that are required for previous sdk versions and our need to support 23 as our min SDK version

Two small follow-ups from code review on slice 1.

- AppContext.fromContext: when the outer PackageManager read fails
  entirely, derive a best-effort `name` from the last segment of the
  package id rather than returning literal 'unknown'. Better attribution
  on ingest dashboards in the rare case the read throws (corrupted
  install / instrumentation contexts) and matches what most launchers
  show when applicationLabel resolution fails.
- AppContextTest: direct Robolectric coverage for the factory. Slice 1
  introduces fromContext standalone — it had no direct tests until now,
  only transitive coverage via DeviceContextProvider.

Refs: sc-38233
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.

2 participants