Skip to content

Add the m_app_return pixel, fired on every foreground return - #9502

Merged
YoussefKeyrouz merged 4 commits into
developfrom
feature/youssef/app_return_pixel
Aug 20, 2026
Merged

Add the m_app_return pixel, fired on every foreground return#9502
YoussefKeyrouz merged 4 commits into
developfrom
feature/youssef/app_return_pixel

Conversation

@YoussefKeyrouz

@YoussefKeyrouz YoussefKeyrouz commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Task/Issue URL: https://app.asana.com/1/137249556945/task/1217515446826485?focus=true
Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/481882893211075/task/1217382844057496?focus=true
API Proposals URL(s) (if applicable): None

Description

m_app_return is a new pixel fired on every foreground transition. It’s the denominator for every "% of app opens" panel on the dashboard, and reports:

Param Source
time_away_bucket SettingsDataStore.lastSessionBackgroundTimestamp, bucketed (cold_start, lt_1m, 1_5m, 5_15m, 15_30m, 30_60m, gt_60m)
exceeded_idle_threshold elapsed time vs IdleThresholdResolver.effectiveThresholdSeconds(), reported even when the after-idle feature is ineligible
idle_threshold_seconds IdleThresholdResolver.effectiveThresholdSeconds(settingsDataStore.userSelectedIdleThresholdSeconds)
after_inactivity_option ShowOnAppLaunchOptionDataStore.optionFlow, mapped via the existing ShowOnAppLaunchOption.getDailyPixelValue()
feature_eligible AndroidBrowserConfigFeature.showNTPAfterIdleReturn().isEnabled()
unified_input_available DuckAiFeatureState.nativeInputFieldEnabled
toggle_visible DuckChatInputModeState.inputModeCapability == SEARCH_AND_DUCK_AI
launch_source LaunchSourceTracker.launchSource() from various entry points

Steps to test this PR

Attach logcat, filter for Pixel sent:. You'll see m_app_return_count and m_app_return_daily fire on every foreground.

Cold start

  • Clear app data (or fresh install), open the app -> time_away_bucket=cold_start,
    exceeded_idle_threshold=false

Time-away buckets

  • Background the app (home button) for a few seconds, reopen: time_away_bucket=lt_1m
  • Background for over a minute time_away_bucket matches the elapsed bucket time_away_bucket=1_5m

Idle threshold / after-inactivity option

  • In Settings → General → After Inactivity, change the timeout to 1 minute.
  • Background the app then open it after few seconds: exceeded_idle_threshold=false and idle_threshold_seconds=60
  • Background the app and wait for more than 1 minute exceeded_idle_threshold=true
  • Change the after-inactivity option between New Tab Page / Last Opened Tab / Specific Page: When resuming the app, after_inactivity_option is new_tab_page / last_opened_tab / specific_page accordingly

Duck.ai capability

  • With Search + Duck.ai enabled, reopen the app -> toggle_visible=true
  • Turn Search + Duck.ai off (Search only), reopen -> toggle_visible=false

Fire-button suppression

  • Use the Fire Button (which restarts the process): the immediate restart's should
    not fire m_app_return_count/_daily (same as ml), only a subsequent real return does

launch_source

  • Background or Force-stop the app, tap the launcher icon: launch_source=standard
  • Background or Force-stop, add the widget to your homescreen, then tap a Favorites/search home-screen widget: launch_source=widget
  • Open a website then Menu -> Add to home screen (to create a shortcut). Background or Force-stop, then open the pinned site shortcut: launch_source=shortcut
  • Background or Force-stop, set ddg as your default browser, then open a link from any messaging app (whatsapp, MM, etc…): launch_source=url
  • Background or Force-stop, set ddg as your default voice assistant, then trigger the system Assist / voice search entry (long-press home / power button, depending on device): launch_source=other
  • All the above should be correct whether you background or force stop the app.

UI changes

Before After
No UI changes No UI changes

Note

Low Risk
Telemetry-only changes with no auth or data-handling impact; fire suppression mirrors existing app-launch pixel behavior.

Overview
Adds m_app_return (m_app_return_count / m_app_return_daily) as the denominator for “% of app opens” analytics, fired on each foreground return without feature-flag gating.

AppReturnPixelSender centralizes firing: it buckets time away (cold_start through gt_60m), compares elapsed background time to IdleThresholdResolver, and attaches after-inactivity option, NTP-after-idle eligibility, Duck.ai input/toggle state, and launch_source. Fire-button process restarts are suppressed for the first resume in a session (same grace window as ml), with per-foreground-session deduplication reset on process onStop.

Entry points call fireIfNeeded from LaunchBridgeActivity, IntentDispatcherActivity, BrowserActivity.onResume, and SystemSearchActivity. BrowserActivity.intent stamps LAUNCH_SOURCE_PIXEL_VALUE from BrowserLaunchSource, and favorites widget bundles do the same. EnqueuedPixelWorker now uses isLaunchByFireAction() from the sender instead of duplicating that logic.

Reviewed by Cursor Bugbot for commit a770375. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread app/src/main/java/com/duckduckgo/app/pixels/AppReturnPixelSender.kt Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Privacy Review task: https://app.asana.com/0/69071770703008/1217524061930625

Comment thread app/src/main/java/com/duckduckgo/app/pixels/AppReturnPixelSender.kt
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/app_return_pixel branch from 376a28f to 9cfb377 Compare August 17, 2026 08:22
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/app_return_pixel branch from 0d686e5 to 79e3ec7 Compare August 17, 2026 17:55
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/measure_exposure_unified_input branch from f54ff6c to 1025f5e Compare August 17, 2026 17:55
Comment thread app/src/main/java/com/duckduckgo/app/browser/BrowserActivity.kt
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/measure_exposure_unified_input branch from 1025f5e to a9e17c5 Compare August 17, 2026 19:00
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/app_return_pixel branch from 79e3ec7 to 784e798 Compare August 17, 2026 19:00

@malmstein malmstein left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed statically, stacked PR so no CI APK. CI is green, the two red checks on the PR page are a superseded cancelled run.

Well built. The once-per-foreground latch is clean, fired reset in the process-lifecycle onStop with the fire-restart resume explicitly consumed rather than swallowed forever, and pulling isLaunchByFireAction() out of EnqueuedPixelWorker removes a duplicate rather than adding one. toPixelLaunchSourceValue() being an exhaustive when over BrowserLaunchSource means a new launch source cannot silently fall into other. Checked every enum against emitted values: time_away_bucket, launch_source and after_inactivity_option all match, and the first_daily_count suffix generates the _count and _daily names the code fires. No PII, times bucketed.

Two non-blocking things inline, both about a declared value not matching what can actually be sent.

Comment thread PixelDefinitions/pixels/definitions/ntp_after_idle.json5
Comment thread app/src/main/java/com/duckduckgo/app/pixels/AppReturnPixelSender.kt
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/measure_exposure_unified_input branch from a9e17c5 to 5e38d86 Compare August 18, 2026 20:02
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/app_return_pixel branch from 784e798 to b707ebc Compare August 18, 2026 20:02
Comment thread PixelDefinitions/pixels/definitions/ntp_after_idle.json5 Outdated
Comment thread app/src/main/java/com/duckduckgo/app/pixels/AppReturnPixelSender.kt
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/app_return_pixel branch from b707ebc to b2fa6f9 Compare August 18, 2026 22:06
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/measure_exposure_unified_input branch 2 times, most recently from 44612f1 to dac42dc Compare August 18, 2026 23:04
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/app_return_pixel branch from b2fa6f9 to c4fd050 Compare August 18, 2026 23:04

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 5 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ 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 c4fd050. Configure here.

Comment thread app/src/main/java/com/duckduckgo/app/pixels/AppReturnPixelSender.kt
Comment thread app/src/main/java/com/duckduckgo/app/systemsearch/SystemSearchActivity.kt Outdated
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/measure_exposure_unified_input branch from dac42dc to 4bd21d6 Compare August 19, 2026 03:06
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/app_return_pixel branch from c4fd050 to 668fc71 Compare August 19, 2026 03:06
Base automatically changed from feature/youssef/measure_exposure_unified_input to develop August 19, 2026 03:58
@YoussefKeyrouz
YoussefKeyrouz added this pull request to the merge queue Aug 20, 2026
Merged via the queue into develop with commit d062710 Aug 20, 2026
18 checks passed
@YoussefKeyrouz
YoussefKeyrouz deleted the feature/youssef/app_return_pixel branch August 20, 2026 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants