Skip to content

Add duck.ai entry point pixel - #9550

Open
YoussefKeyrouz wants to merge 3 commits into
developfrom
feature/youssef/duck_ai_entry_point_attribution
Open

Add duck.ai entry point pixel#9550
YoussefKeyrouz wants to merge 3 commits into
developfrom
feature/youssef/duck_ai_entry_point_attribution

Conversation

@YoussefKeyrouz

@YoussefKeyrouz YoussefKeyrouz commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Task/Issue URL: https://app.asana.com/1/137249556945/project/1212810093780571/task/1217295011063283?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): https://app.asana.com/1/137249556945/project/1212810093780571/task/1217532302535418?focus=true

Description

Adds Duck.ai entry-point count/daily pixels with five bounded parameters: source, duck_ai_enabled, input_screen_enabled, opens_new_tab, and has_prompt.

Every public DuckChat.open* method requires a DuckChatEntryPoint and reports automatically. Navigation paths that bypass those methods call DuckChat.reportDuckChatEntry beside the navigation. Restored tabs and selecting an existing tab are passive and do not report.

Steps to test this PR

Install the internal build; filter Logcat for Pixel sent: and m_aichat_entry_point. Expect a count event for each action and at most one daily event per day. For every event, verify exactly source, duck_ai_enabled, input_screen_enabled, opens_new_tab, and has_prompt.

source Testing steps opens_new_tab has_prompt
address_bar_prompt NTP: Settings > AI Features > Search & Duck.ai; select Duck.ai in the address bar, enter entry test, submit.
Loaded page: repeat after opening https://example.com/.
NTP: false
Loaded page: true
true
address_bar_icon Settings > AI Features > Duck.ai Shortcuts > Address Bar on and Search Only selected. Open an NTP without focusing the bar; tap its Duck.ai icon. false false
suggestion_ask_ai Keep Search selected, focus the address bar, type entry test, and tap Ask Duck.ai. Repeat from an NTP and from https://example.com/. NTP: false
Loaded page: true
true
browsing_menu_ntp On an NTP, open ⋮ > New Chat. true false
browsing_menu_webpage Open https://example.com/, then ⋮ > New Chat. true false
tab_switcher Open the tab switcher and tap its Duck.ai toolbar/FAB button, not an existing Duck.ai tab card. true false
chat_history_new_chat With useNativeStorageChatData enabled, open ⋮ > Chats, then tap toolbar New; if history is empty, tap Open Duck.ai. true false
chat_history_open_chat Create a saved chat, open ⋮ > Chats, and tap its row. Also check an address-bar recent-chat suggestion: NTP reuses the tab; a loaded page opens a new tab. History/contextual row: true
Address-bar row: NTP false, loaded page true
false
voice Settings > AI Features > Duck.ai Shortcuts > Voice Chat on; open ⋮ > Voice Chat. true false
onboarding Clear app data/reinstall, choose the Duck.ai onboarding flow, enter/select a nonblank demo prompt, and continue into Duck.ai. true true
direct_url 1. Type and submit https://duck.ai/.
2. Type and submit https://duck.ai/chat?duckai=5&q=Hello%20from%20entry%20test&prompt=1.
3. Open https://duckduckgo.com/duckduckgo-help-pages/duckai/ and tap Navigate there directly.
Normal typed/intercepted navigation: false;
custom-tab handoff: true
Plain URL/link: false
Auto-submit URL: true
serp Open https://duckduckgo.com/?q=duck.ai, then tap the result that opens https://duck.ai/. false for normal same-tab navigation false
icon_shortcut Settings > AI Features > Duck.ai Shortcuts > Browser Menu on; relaunch if needed, long-press the DuckDuckGo app icon, then tap Duck.ai. true false
contextual_chat Enable contextualModeKillSwitch plus completed standalone migration or contextualMode; open contextual mode from a loaded page, then use the contextual FE handoff that emits openAIChat into full-screen Duck.ai. true false
widget_quick_actions Open Duck.ai once, add the Search and Duck.ai or Search and Favorites widget to the Home screen, then tap the Duck.ai icon in the widget. true false
widget_favorite Add https://duck.ai/ as a Favorite, add the Search and Favorites widget, then tap that Duck.ai favorite. true false
system_search Set Settings > AI Features > Search & Duck.ai; add a DuckDuckGo search widget, tap its search field (not its Duck.ai quick-action icon), enter entry test, then tap the Duck.ai icon inside System Search. true true
digital_assistant Android Settings > Apps > Default apps > Digital assistant app > DuckDuckGo; invoke the assistant gesture/power-key shortcut. true false
deep_link_other With the app closed, run adb shell am start -W -a android.intent.action.VIEW -d 'https://duck.ai/' -p '<applicationId>'. true false
paid_settings From the Settings main screen, under DuckDuckGo Subscription, tap Duck.ai, then Open Duck.ai. true false
address_bar_shortcut_chip NTP Customize > Shortcuts; enable Duck.ai, return to the NTP, tap the Duck.ai chip. false with the selected NTP;
true only in the no-selected-tab fallback
false
address_bar_editing_state Settings > AI Features > Search Only and Duck.ai Shortcuts > Address Bar on. Focus an empty address bar, then tap the Duck.ai icon shown in the editing field. true false

UI changes

No UI changes


Note

Medium Risk
Public DuckChat open APIs now require an entry point, so every Duck.ai launch path is retouched. Risk is telemetry/API-contract, not auth or data handling.

Overview
Adds m_aichat_entry_point count/daily pixels so every user-initiated Duck.ai open reports source, duck_ai_enabled, input_screen_enabled, opens_new_tab, and has_prompt.

Public DuckChat.open* methods now take a DuckChatEntryPoint and fire automatically. Paths that navigate without those methods (typed URLs, SERP/in-page links, widgets, shortcuts, onboarding, NTP chip, tab switcher, menus) call reportDuckChatEntry next to the navigation. Restored/selected existing tabs stay silent.

Also converts direct-navigation duck_ai_enabled to a boolean, adds input_screen_enabled there, and derives omnibar toggle_visible from input-mode capability instead of toggle position.

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

@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/duck_ai_entry_point_attribution branch from 29001d4 to 0e965ef Compare August 20, 2026 07:02
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/duck_ai_entry_point_attribution branch 2 times, most recently from e086b0f to 66dcdb8 Compare August 20, 2026 08:34
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/wide_event_return_session branch from 7f575d2 to 7f795b8 Compare August 20, 2026 09:47
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/duck_ai_entry_point_attribution branch 2 times, most recently from 77a8ba7 to ca18c92 Compare August 20, 2026 10:02
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/wide_event_return_session branch from 7f795b8 to 9ba46aa Compare August 20, 2026 10:02

@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 1 potential issue.

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 ca18c92. Configure here.

@YoussefKeyrouz
YoussefKeyrouz changed the base branch from feature/youssef/wide_event_return_session to graphite-base/9550 August 20, 2026 18:12
@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/duck_ai_entry_point_attribution branch from ca18c92 to 20a76d0 Compare August 20, 2026 19:18
@YoussefKeyrouz
YoussefKeyrouz changed the base branch from graphite-base/9550 to develop August 20, 2026 19:19

@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.

did not install or smoke-test the APK. nice work on the coverage, the truth-table test and the one that pins every enum to its wire value are the right tests to have written, and the contextual double-report case being tested explicitly is good to see. main things i'd like answered before merge are the has_prompt meaning on contextual expand and the narrowing of the existing direct-navigation pixel, since both change what analysts see. the rest is non-blocking: entry reporting would sit better in RealDuckChatPixels alongside fireCountAndDaily, and the prompt check now exists in three places with the url contract hardcoded in two of them.

Comment thread PixelDefinitions/pixels/definitions/duck_chat.json5
Comment thread app/src/main/java/com/duckduckgo/app/browser/BrowserActivity.kt
Comment thread app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt
DuckChatPixelParameters.OPENS_NEW_TAB to opensNewTab.toString(),
DuckChatPixelParameters.HAS_PROMPT to hasPrompt.toString(),
)
pixel.fire(DuckChatPixelName.DUCK_CHAT_ENTRY_POINT_COUNT, parameters = parameters)

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.

suggestion: every other duck chat pixel goes through DuckChatPixels, and fireCountAndDaily there already fires this pair on io. can we move entry reporting into RealDuckChatPixels instead of firing both from here on whatever thread tapped?

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.

Good point, will do. Only caveat is that I’ll have to inject DuckChatPixels as Lazy to prevent cycles.

}
}

private fun DuckChatEntryPoint.toPixelValue(): String = when (this) {

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.

nit: RealDuckChatTest already asserts source equals entryPoint.name.lowercase() for every entry, so this when is 22 lines saying the same thing. can we just use name.lowercase()?

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.

Nice catch, didn’t think about it. Will update

@YoussefKeyrouz
YoussefKeyrouz force-pushed the feature/youssef/duck_ai_entry_point_attribution branch from 20a76d0 to b94db4e Compare August 21, 2026 20:36

@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.

lgtm, the three changes landed as discussed. two things left: toggle_visible on omnibar_shown drops the inputContext == BROWSER half of the canonical predicate so it reads true on duck.ai tabs, and the direct_navigation description still doesn't mention it's USER submissions only. static only, didn't install the apk.

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