Skip to content

fix: [MR-162] enrich sub-app payloads with attribution context#278

Open
janfb-codev wants to merge 5 commits into
developfrom
feature/MR-162
Open

fix: [MR-162] enrich sub-app payloads with attribution context#278
janfb-codev wants to merge 5 commits into
developfrom
feature/MR-162

Conversation

@janfb-codev

@janfb-codev janfb-codev commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Changes

  • Added an attribution block to every enriched sub-app payload, carrying campaign_id, source, hostname, and apk_package_name. It is written to both user_sessions_data and summary_data Firestore records.
  • Introduced hydrateAttributionContext() in MainActivity, populating AppContext (SOURCE, CAMPAIGN_ID) from InstallReferrerPrefs hydrated on create, after install-referrer resolution, and on language selection.
  • WebApp now derives HOSTNAME from the launched sub-app URL (falling back to "unknown") and stores it in AppContext.
  • Extended AppContextKey with CAMPAIGN_ID, SOURCE, and HOSTNAME; added the attribution map field to AppEventPayload.
  • Attribution/context reads are wrapped in a safe resolver missing or uninitialized AppContext values fall back to defaults so event storage never fails on absent enrichment data.

How to test

  • Manual: launch a sub-app (e.g. Feed The Monster) with a UTM/campaign install referrer set, complete a level, and verify in Firestore that:
    • user_sessions_data documents contain an attribution block (campaign_id, source, hostname, apk_package_name) and metadata.language.
    • summary_data documents contain the attribution block.
  • Verify hostname resolves from the sub-app URL and defaults to "unknown" when the URL is missing.

Ref: MR-162

Summary by CodeRabbit

  • New Features
    • Added attribution tracking for campaign, source, hostname, and app package details.
    • Captures attribution information from install referrals and deferred app links.
    • Records the app’s language with user session events.
    • Includes attribution data in event records for improved reporting and analytics.
    • Uses clear fallback values when attribution or language details are unavailable.

@janfb-codev
janfb-codev requested a review from miguelccodev July 22, 2026 10:07
@janfb-codev janfb-codev self-assigned this Jul 22, 2026
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Attribution values from install referrers and app URLs are stored in AppContext, added to event payloads with language metadata, and persisted in Firestore for user-session and summary records.

Changes

Attribution context and event persistence

Layer / File(s) Summary
Context keys and attribution hydration
app/src/main/java/org/curiouslearning/container/core/context/AppContextKey.java, app/src/main/java/org/curiouslearning/container/core/subapp/payload/AppEventPayload.java, app/src/main/java/org/curiouslearning/container/MainActivity.java
Adds attribution context keys and hydrates source and campaign values from persisted install-referrer data during activity setup and attribution callbacks.
Hostname context capture
app/src/main/java/org/curiouslearning/container/WebApp.java
Derives the app URL hostname and stores it in AppContext, defaulting to "unknown" when unavailable.
Payload enrichment and Firestore persistence
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
Adds attribution fields and resolved language metadata to payloads, then persists attribution for user-session and summary records.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MainActivity
  participant WebApp
  participant AppContext
  participant DefaultAppEventPayloadHandler
  participant Firestore
  MainActivity->>AppContext: hydrate source and campaign_id
  WebApp->>AppContext: store hostname
  DefaultAppEventPayloadHandler->>AppContext: resolve attribution and language
  DefaultAppEventPayloadHandler->>Firestore: persist enriched event record
Loading

Possibly related PRs

Suggested reviewers: miguelccodev, azamanaza, rajesh1041

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Tests & Lint & Coverage ⚠️ Warning No new/updated tests cover the attribution changes, and there’s no lint or Jacoco report proving lint passed or coverage hit 70%. Add/update tests for the new attribution paths, run the project lint task, and provide Jacoco coverage output showing at least 70%.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise, includes the MR key, and accurately describes the main change to enrich sub-app payloads with attribution context.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/MR-162

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/org/curiouslearning/container/MainActivity.java`:
- Around line 1195-1204: The hydrateAttributionContext method conditionally
preserves stale AppContext values when attribution preferences are empty or
missing. Always write both SOURCE and CAMPAIGN_ID during hydration, using the
retrieved values with their existing empty-string fallbacks, rather than
guarding the AppContext.set calls with non-empty checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bf56d8fe-e89b-47e3-a454-f1f5d38dcf92

📥 Commits

Reviewing files that changed from the base of the PR and between 2ece295 and af799ac.

📒 Files selected for processing (5)
  • app/src/main/java/org/curiouslearning/container/MainActivity.java
  • app/src/main/java/org/curiouslearning/container/WebApp.java
  • app/src/main/java/org/curiouslearning/container/core/context/AppContextKey.java
  • app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
  • app/src/main/java/org/curiouslearning/container/core/subapp/payload/AppEventPayload.java

Comment on lines +1195 to +1204
private void hydrateAttributionContext() {
SharedPreferences installReferrerPrefs = getSharedPreferences("InstallReferrerPrefs", MODE_PRIVATE);
String source = installReferrerPrefs.getString("source", "");
String campaignId = installReferrerPrefs.getString("campaign_id", "");
if (source != null && !source.trim().isEmpty()) {
AppContext.getInstance().set(AppContextKey.SOURCE, source);
}
if (campaignId != null && !campaignId.trim().isEmpty()) {
AppContext.getInstance().set(AppContextKey.CAMPAIGN_ID, campaignId);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Clear missing attribution values during hydration.

Because AppContext persists values, these conditional writes leave the previous SOURCE or CAMPAIGN_ID intact when InstallReferrerPrefs contains an empty or missing value. Later Firestore payloads can therefore be attributed to an old campaign/source. Always write the current value or the existing empty-string fallback on every hydration.

Proposed fix
 private void hydrateAttributionContext() {
     SharedPreferences installReferrerPrefs = getSharedPreferences("InstallReferrerPrefs", MODE_PRIVATE);
     String source = installReferrerPrefs.getString("source", "");
     String campaignId = installReferrerPrefs.getString("campaign_id", "");
-    if (source != null && !source.trim().isEmpty()) {
-        AppContext.getInstance().set(AppContextKey.SOURCE, source);
-    }
-    if (campaignId != null && !campaignId.trim().isEmpty()) {
-        AppContext.getInstance().set(AppContextKey.CAMPAIGN_ID, campaignId);
-    }
+    AppContext context = AppContext.getInstance();
+    context.set(AppContextKey.SOURCE,
+            source == null || source.trim().isEmpty() ? "" : source.trim());
+    context.set(AppContextKey.CAMPAIGN_ID,
+            campaignId == null || campaignId.trim().isEmpty() ? "" : campaignId.trim());
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void hydrateAttributionContext() {
SharedPreferences installReferrerPrefs = getSharedPreferences("InstallReferrerPrefs", MODE_PRIVATE);
String source = installReferrerPrefs.getString("source", "");
String campaignId = installReferrerPrefs.getString("campaign_id", "");
if (source != null && !source.trim().isEmpty()) {
AppContext.getInstance().set(AppContextKey.SOURCE, source);
}
if (campaignId != null && !campaignId.trim().isEmpty()) {
AppContext.getInstance().set(AppContextKey.CAMPAIGN_ID, campaignId);
}
private void hydrateAttributionContext() {
SharedPreferences installReferrerPrefs = getSharedPreferences("InstallReferrerPrefs", MODE_PRIVATE);
String source = installReferrerPrefs.getString("source", "");
String campaignId = installReferrerPrefs.getString("campaign_id", "");
AppContext context = AppContext.getInstance();
context.set(AppContextKey.SOURCE,
source == null || source.trim().isEmpty() ? "" : source.trim());
context.set(AppContextKey.CAMPAIGN_ID,
campaignId == null || campaignId.trim().isEmpty() ? "" : campaignId.trim());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/org/curiouslearning/container/MainActivity.java` around
lines 1195 - 1204, The hydrateAttributionContext method conditionally preserves
stale AppContext values when attribution preferences are empty or missing.
Always write both SOURCE and CAMPAIGN_ID during hydration, using the retrieved
values with their existing empty-string fallbacks, rather than guarding the
AppContext.set calls with non-empty checks.

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