Skip to content

Add new onboarding dialog to preview input mode toggle#8206

Merged
lmac012 merged 21 commits intodevelopfrom
feature/lukasz/onboarding-input-screen-toggle-ui
Apr 15, 2026
Merged

Add new onboarding dialog to preview input mode toggle#8206
lmac012 merged 21 commits intodevelopfrom
feature/lukasz/onboarding-input-screen-toggle-ui

Conversation

@lmac012
Copy link
Copy Markdown
Contributor

@lmac012 lmac012 commented Apr 7, 2026

Task/Issue URL: https://app.asana.com/1/137249556945/project/1205648422731273/task/1213970480206403?focus=true

Description

Steps to test this PR

  • Clean install
  • Going through the onboarding, in the "Want easy access to private AI chat in the address bar?" step, make sure "Toggle between Search and Duck.ai" is selected (it is the default option).
  • Verify that the new onboarding step (screenshot in the following section) is not shown.
  • Modify DuckAiOnboardingExperimentManagerImpl::enroll() to return one of the treatment variants
  • Clean install
  • Going through the onboarding, in the "Want easy access to private AI chat in the address bar?" step, make sure "Toggle between Search and Duck.ai" is selected (it is the default option).
  • Verify the new onboarding step is shown (screenshot in the following section).
  • Verify the default state of the toggle matches the variant returned from DuckAiOnboardingExperimentManagerImpl::enroll()
  • Verify there are no jarring transitions
  • Verify tapping on the toggle changes it's state and updates:
    • Hint in the text field
    • Text field height (single line for search, 3 lines for chat)
    • The list of suggestions. Note that suggestions are not final.

UI changes

Before After
n/a Screenshot_20260407_172958

Note

Medium Risk
Changes onboarding flow control and UI by inserting a new, experiment-gated dialog after the input-screen selection step; risks are mainly around navigation/animation regressions and variant gating logic.

Overview
Adds a new INPUT_SCREEN_PREVIEW onboarding step that previews the Search vs AI Chat input-mode toggle (with animated suggestions and optional keyboard) via a new pre_onboarding_input_mode_demo.xml include.

Updates WelcomePage/WelcomePageViewModel to optionally show this preview after the input-screen selection, gated by a new DuckAiOnboardingExperimentManager variant (control/duck-ai-default/search-default); adds OnboardingStore.getChatSuggestions() plus new strings for chat prompts.

Adjusts onboarding CTA layout spacing to accommodate the new embedded preview, and updates unit tests to cover experiment outcomes and the new dialog path.

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

Copy link
Copy Markdown
Contributor Author

lmac012 commented Apr 7, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@lmac012 lmac012 marked this pull request as ready for review April 7, 2026 15:41
Comment thread app/src/main/java/com/duckduckgo/app/onboarding/ui/page/WelcomePage.kt Outdated
@lmac012 lmac012 assigned LukasPaczos and unassigned LukasPaczos Apr 7, 2026
@lmac012 lmac012 requested a review from LukasPaczos April 7, 2026 15:57
Copy link
Copy Markdown
Contributor

@LukasPaczos LukasPaczos left a comment

Choose a reason for hiding this comment

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

  1. Rotating the screen while the search/chat suggestions are animating is crashing.
Stacktrace
FATAL EXCEPTION: main
Process: com.duckduckgo.mobile.android.debug, PID: 22327
java.lang.IllegalStateException: Can't access the Fragment View's LifecycleOwner for WelcomePage{3b5835d} (e873eddd-7c95-40db-b890-bcbda61c9d08) when getView() is null i.e., before onCreateView() or after onDestroyView(
	at androidx.fragment.app.Fragment.getViewLifecycleOwner(Fragment.java:390)
	at com.duckduckgo.common.ui.viewbinding.FragmentViewBindingDelegate.getValue(FragmentViewBindingDelegate.kt:78)
	at com.duckduckgo.app.onboarding.ui.page.WelcomePage.getBinding(WelcomePage.kt:97)
	at com.duckduckgo.app.onboarding.ui.page.WelcomePage.access$getBinding(WelcomePage.kt:85)
	at com.duckduckgo.app.onboarding.ui.page.WelcomePage$configureDaxCta$1$20.invoke$lambda$1$animateButton(WelcomePage.kt:560)
	at com.duckduckgo.app.onboarding.ui.page.WelcomePage$configureDaxCta$1$20.invoke$lambda$1$animateButton$lambda$0(WelcomePage.kt:567)
	at com.duckduckgo.app.onboarding.ui.page.WelcomePage$configureDaxCta$1$20.$r8$lambda$TEzRAOCg8jk2cXYTIkoNyq0JUtw(Unknown Source:0)
	at com.duckduckgo.app.onboarding.ui.page.WelcomePage$configureDaxCta$1$20$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
	at android.view.ViewPropertyAnimator$AnimatorEventListener.onAnimationEnd(ViewPropertyAnimator.java:1120)
	at android.animation.Animator$AnimatorListener.onAnimationEnd(Animator.java:784)
	at android.animation.Animator$AnimatorCaller$$ExternalSyntheticLambda1.call(D8$$SyntheticClass:0)
	at android.animation.Animator.callOnList(Animator.java:742)
	at android.animation.Animator.notifyListeners(Animator.java:640)
	at android.animation.Animator.notifyEndListeners(Animator.java:665)
	at android.animation.Animator.completeEndAnimation(Animator.java:708)
	at android.animation.ValueAnimator.completeEndAnimation(ValueAnimator.java:1336)
	at android.animation.Animator.notifyEndListenersFromEndAnimation(Animator.java:697)
	at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1322)
	at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1592)
	at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:404)
	at android.animation.AnimationHandler.-$$Nest$mdoAnimationFrame(Unknown Source:0)
	at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:106)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1645)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1656)
	at android.view.Choreographer.doCallbacks(Choreographer.java:1252)
	at android.view.Choreographer.doFrame(Choreographer.java:1177)
	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1630)
	at android.os.Handler.handleCallback(Handler.java:1095)
	at android.os.Handler.dispatchMessageImpl(Handler.java:135)
	at android.os.Handler.dispatchMessage(Handler.java:125)
	at android.os.Looper.loopOnce(Looper.java:269)
	at android.os.Looper.loop(Looper.java:367)
	at android.app.ActivityThread.main(ActivityThread.java:9333)

                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:566)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929)
  1. According to designs, the input field should be focused and keyboard should be up when the new onboarding step is presented. This might be problematic in landscape though, as focusing the input text field obscures the whole window right now.
  2. The touch area for the search/submit button seems to span well over the actual button size:
Screen_recording_20260408_094841.mp4
  1. Should we limit the input box to be single line only? Having it multi-line in this scenario could be a little awkward:
Screen_recording_20260408_095103.mp4
  1. In landscape, the bubble with suggestions is clipped and can't be scrolled down enough to reveal the whole shape:
Image

4 and 5 might need more discussion, so we can follow up separately.

Comment thread app/src/main/res/values/strings.xml Outdated
val inputScreenPreviewBinding = binding.daxDialogCta.inputScreenPreview

listOf(inputScreenPreviewBinding.suggestion1, inputScreenPreviewBinding.suggestion2, inputScreenPreviewBinding.suggestion3)
.forEachIndexed { index, button -> suggestions[index].setOptionView(button) }
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: this assumes the provided suggestions list has exactly 3 elements, otherwise it'd crash. Not a problem today but a little fragile.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Technically, it assumes the list of suggestions has at least 3 elements. If we didn't make any assumptions about the number of suggestions, we would have to handle quite a few edge cases (if not here than in other places in the code), and it's not always clear what would be the expected behavior. I'm not sure it is worth the extra effort/complexity.

Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

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 4d51a9c. Configure here.

val inputScreenPreviewBinding = binding.daxDialogCta.inputScreenPreview

listOf(inputScreenPreviewBinding.suggestion1, inputScreenPreviewBinding.suggestion2, inputScreenPreviewBinding.suggestion3)
.forEachIndexed { index, button -> suggestions[index].setOptionView(button) }
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.

Unchecked index access assumes exactly three suggestions

Low Severity

setInputScreenPreviewInputMode directly indexes into suggestions[index] for indices 0, 1, 2 without bounds checking. If suggestions has fewer than 3 elements, this throws an IndexOutOfBoundsException. Currently getSearchOptions() and getChatSuggestions() both return exactly 3 items so it works, but the coupling between the list size and the hardcoded 3 buttons is implicit and fragile — any future change to those provider methods would cause a runtime crash.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4d51a9c. Configure here.

@lmac012
Copy link
Copy Markdown
Contributor Author

lmac012 commented Apr 10, 2026

The touch area for the search/submit button seems to span well over the actual button size:

This is just an odd side effect of the button not having a click listener at the moment. It won't be an issue in the final implementation.

Copy link
Copy Markdown
Contributor

@LukasPaczos LukasPaczos left a comment

Choose a reason for hiding this comment

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

LGTM!

@lmac012 lmac012 merged commit e2666e2 into develop Apr 15, 2026
22 checks passed
@lmac012 lmac012 deleted the feature/lukasz/onboarding-input-screen-toggle-ui branch April 15, 2026 08:46
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