Wire the onboarding password import step to the Google import flow - #9624
Conversation
Adds the onboarding cards for the password import experiment: the prompt card, and the outcome card with its parsing, finished and failed states. The step indicator gains a CONTINUES_PREVIOUS mode so the prompt and outcome cards read as one product step. The import flow itself is not wired up yet: the launch step is a no-op with a TODO, and the outcome card stays on its parsing state until the follow-up lands. Task: https://app.asana.com/1/137249556945/project/72649045549333/task/1216462243759588 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> # Conflicts: # app/src/main/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingEvent.kt # app/src/main/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingPlanProvider.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/BrandDesignUpdatePageViewModel.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/configdriven/ContentConfig.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/configdriven/DialogConfigResolver.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/configdriven/OnboardingDialogShownPixels.kt # app/src/main/res/layout/pre_onboarding_dax_dialog_cta_brand_design_update.xml # app/src/test/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingPlanProviderTest.kt
Adds the ImportPasswordsFromGoogle API so onboarding can start the Google password import web flow, read its result, and follow the import status for the counts. The web flow's ActivityParams move to AutofillScreens and gain a launch source so the flow can attribute itself. Replaces the TODO left by the UI change: the launch step now starts the flow, and the outcome card is driven by the real result. Task: https://app.asana.com/1/137249556945/project/72649045549333/task/1216462243759588 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This stack of pull requests is managed by Graphite. Learn more about stacking. |
…rt-step # Conflicts: # app/src/main/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingEvent.kt # app/src/main/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingPlanProvider.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/configdriven/ContentConfig.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/configdriven/DialogConfigResolver.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/configdriven/OnboardingDialogShownPixels.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/configdriven/engine/ContentController.kt # app/src/main/res/layout/pre_onboarding_dax_dialog_cta_brand_design_update.xml # app/src/test/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingPlanProviderTest.kt # app/src/test/java/com/duckduckgo/app/onboarding/ui/page/configdriven/DialogConfigResolverTest.kt
…feature/cradoiu/onboarding-password-import-flow
Report whether a failed Google passwords import can be retried, so the onboarding step offers a retry alert for a WebView crash and reports a CSV parsing failure straight on the outcome card. Dismissing the retry alert now skips the import and its outcome step. https://app.asana.com/1/137249556945/project/1212087397361015/task/1217824836952478 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…feature/cradoiu/onboarding-password-import-flow
The manager was returning TREATMENT unconditionally with the real enrollment commented out for local testing, which forced the import step on every config-driven onboarding user. https://app.asana.com/1/137249556945/project/1212087397361015/task/1217824836952478 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…t-flow # Conflicts: # app/src/main/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingEvent.kt # app/src/main/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingPlanContext.kt # app/src/main/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingPlanProvider.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/OnboardingBackgroundAnimator.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/OnboardingPixelSender.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/configdriven/ConfigDrivenOnboardingPageViewModel.kt # app/src/main/java/com/duckduckgo/app/onboarding/ui/page/configdriven/DialogConfigResolver.kt # app/src/test/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingPlanProviderTest.kt
…t-flow # Conflicts: # app/src/test/java/com/duckduckgo/app/onboarding/orchestrator/NewUserOnboardingPlanProviderTest.kt
A transient import error left the flow on the card-less launch step with retry only in an AlertDialog, which is torn down on configuration change. The still-visible import card emitted PasswordImportRequested into a step that ignored it, so only Skip could move onboarding forward. Go back to the import step on a transient error so its Import and Skip actions stay live, and drive the retry alert from retained view state instead of a one-shot command so a recreated view puts it back.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 95a31f7. Configure here.
CDRussell
left a comment
There was a problem hiding this comment.
@catalinradoiu Haven't been through the code; only testing out the functionality. But there's one thing that's definitely needing fixed:
- go through the import flow
- on the success screen, hit back button (app backgrounds)
- when you launch the app again you're shown a in-progress shimmer that never completes (user is stuck here)
Pressing back on the outcome card finishes the onboarding activity, but the run keeps its place in the app-scoped orchestrator. On relaunch the card was rebuilt on its parsing state with no activity result left to come, so it showed a shimmer that never completed and a permanently disabled CTA. Record what the run knows about the import in the plan context, so the card is seeded from it rather than from a default that claims an import is in flight, and have the card ask for an outcome whenever it binds without one. https://app.asana.com/1/137249556945/project/1201621853593513/task/1217265967787215 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reporting the bind through the content controller keeps the outcome card a renderer: it no longer has to raise a signal for state it cannot reach, and ContentInteraction goes back to carrying user interactions only. Binding is also the one event every card gets, first render and rebuild alike, so the step id comes from it and the hardcoded content-state key goes away. https://app.asana.com/1/137249556945/project/1201621853593513/task/1217265967787215 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| private fun PasswordImportResult.toOutcome(): PasswordImportOutcome = when (this) { | ||
| is PasswordImportResult.Imported -> PasswordImportOutcome.SUCCESS | ||
| PasswordImportResult.Failed, | ||
| PasswordImportResult.InProgress, |
There was a problem hiding this comment.
curious, why InProgress leads to PERMANENT_ERROR?
There was a problem hiding this comment.
When we get the outcome we shouldn't be InProgress anymore. But I agree this is confusing and I will fix this.
There was a problem hiding this comment.
Fixed it to avoid the confusion
toOutcome had to answer for InProgress even though it can never arrive: the parsed event only ever carries a resolved import. Nesting the two resolved outcomes under Terminal and typing the event as that removes the branch rather than mapping an import in flight to a permanent error. https://app.asana.com/1/137249556945/project/1201621853593513/task/1217265967787215 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| content: ContentConfig, | ||
| scope: BindScope, | ||
| ): ContentHandle { | ||
| onContentBound(stepId, content) |
There was a problem hiding this comment.
This is called before the content is actually bound. Can we call it at the end of this block? Or if the timing matter, rename the callback?
There was a problem hiding this comment.
I will rename it in the next PR as we discussed


Task/Issue URL: https://app.asana.com/1/137249556945/project/1201621853593513/task/1217265967787215?focus=true
Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/481882893211075/task/1217143698674642
API Proposals URL(s) (if applicable): https://app.asana.com/1/137249556945/project/1211724162604201/task/1217824836952478?focus=true
Description
Wire the password import step UI in the linaer onboarding to the existing Google password import web flow.
Add the new
-apisurface,ImportPasswordsFromGoogle:isSupported()- whether the WebView on this device can run the import at all. Used as an enrolment prerequisite so users who could never reach the step are kept out of the experiment.parseResult(data)- reads the outcome out of the result intent.importStatus()- progress of the import itself, which continues after the web flow returnsSuccess. It replays its most recent value, which is load-bearing here: the onboarding collector attaches after the activity result, so without replay a fast import is missed entirely.Steps to test this PR
Apply the following patch:
Onboarding + password import test patch
Successful import
Cancel
Error retry
RealImportPasswordsFromGoogleand set the value ofsimulatedResulttoImportPasswordsResult.Error.TransientPermanent error
RealImportPasswordsFromGoogleand set the value ofsimulatedResulttoImportPasswordsResult.Error.PermanentSkip
Existing flow
UI changes
No UI changes, this only wires the import step to the web flow and shows the error dialog in case of a transient error.
Note
Medium Risk
Touches onboarding flow state, activity-result handling, and autofill import plumbing; mistakes could strand users on import steps or mis-report experiment pixels, but changes are scoped with substantial test coverage.
Overview
Connects linear onboarding’s password import steps to the existing Google Password Manager web flow via a new autofill
ImportPasswordsFromGoogleAPI (isSupported,parseResult, replayingimportStatus).The config-driven onboarding ViewModel launches
AutofillImportPasswordsScreen, maps web-flow results to orchestrator events, shows a retry alert on transient errors (survives rotation), and waits onimportStatus(20s timeout) to populate the outcome card. The plan orchestrator trackspasswordImportResultinstead of a boolean success flag: transient failuresGoBackto the import prompt; permanent failures advance to a failed outcome step; success shows parsing then counts.Experiment enrollment now also requires
isSupported()so unsupported devices are excluded. Existing settings/password-management import entry points switch to the sharedAutofillImportPasswordsScreenparams;Onboardingis added as a launch source. Telemetry uses explicit outcomevaluestrings (both error kinds report"error").Reviewed by Cursor Bugbot for commit a432797. Bugbot is set up for automated code reviews on this repo. Configure here.