Skip to content

Onboarding Brand Design Update: Add address bar position screen#8216

Merged
mikescamell merged 33 commits intodevelopfrom
feature/mike/onboading-brand-design-updates/address-bar-position
Apr 9, 2026
Merged

Onboarding Brand Design Update: Add address bar position screen#8216
mikescamell merged 33 commits intodevelopfrom
feature/mike/onboading-brand-design-updates/address-bar-position

Conversation

@mikescamell
Copy link
Copy Markdown
Contributor

@mikescamell mikescamell commented Apr 8, 2026

Task/Issue URL: https://app.asana.com/1/137249556945/project/1207908166761516/task/1212699261164257

Description

Implements the address bar position selection screen for the onboarding brand design update. This is the second dialog step in the redesigned onboarding flow, allowing users to choose between top, bottom, or split address bar positions.

Key changes:

  • Address bar position dialog with animated title typing, bobbing Dax, and option toggles
  • Step indicator view showing progress through onboarding dialogs
  • Bottom wing Lottie animation with dismissal logic
  • Animated bubble arrow during comparison chart transition
  • Background images for address bar and comparison chart screens
  • Multiple bug fixes for rotation, tablet layout, and animation edge cases

Steps to test this PR

Designs

Apply patch

Subject: [PATCH] onboarding
---
Index: app/src/main/java/com/duckduckgo/app/onboarding/ui/OnboardingViewModel.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/ui/OnboardingViewModel.kt b/app/src/main/java/com/duckduckgo/app/onboarding/ui/OnboardingViewModel.kt
--- a/app/src/main/java/com/duckduckgo/app/onboarding/ui/OnboardingViewModel.kt	(revision 06d747bcc29beada0a444df876b05f55f0dbb4d5)
+++ b/app/src/main/java/com/duckduckgo/app/onboarding/ui/OnboardingViewModel.kt	(date 1775628931668)
@@ -45,7 +45,7 @@
     val viewState = _viewState.asStateFlow()
 
     fun initializePages() {
-        pageLayoutManager.buildPageBlueprints()
+        pageLayoutManager.buildBrandDesignUpdatePageBlueprints()
     }
 
     fun pageCount(): Int {
@@ -70,7 +70,7 @@
     }
 
     fun initializeOnboardingSkipper() {
-        if (!appBuildConfig.canSkipOnboarding) return
+        return
 
         // delay showing skip button until privacy config downloaded
         viewModelScope.launch {

Comparison chart screen

  • Open a fresh install and go through onboarding to the address bar position screen
  • Verify comparison chart transition animates correctly
  • Verify check icon staggered bounce animation plays
  • Rotate and verify state is restored
  • Press "Choose Your Browser"
  • Ensure wing animates out and you transition to the Address bar position screen
  • See next steps

Address bar position screen

  • Verify title typing animation plays on entry
  • Verify bobbing Dax animation slides in and bobs
  • Toggle between top, bottom, and split address bar options
  • Rotate the device and verify the dialog restores correctly with the selected option preserved
  • Verify step indicator shows correct step count
  • Primary CTA does nothing (will work for when Search/AI input is added)

UI changes

See here for demoes


Note

Medium Risk
Moderate risk: adds a new onboarding dialog step with multiple coordinated animations, state restoration, and tablet-specific inset/layout handling that could regress the onboarding flow on different form factors.

Overview
Adds an address bar position selection step to the brand-design-update onboarding flow, including new UI for top/bottom/split options with themed radio-button tinting, new toolbar preview drawables (light/dark, selected/unselected), and a bobbing Dax animation.

Extends onboarding transitions to support a new OnboardingBackgroundStep.AddressBar background, updates BrandDesignUpdateWelcomePage to drive the new dialog’s animations and option selection wiring (including split-option gating and rotation-safe “show without animation” state), and refines tablet handling via a shared DeviceInfo.isTablet() helper and tablet-specific insets.

Improves OnboardingStepIndicatorView by pinning the text label width to prevent layout jitter as steps change.

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

mikescamell and others added 21 commits April 8, 2026 14:33
Apply tappableElement bottom inset as padding on the root layout for
tablets so content isn't hidden behind the opaque taskbar. Phones are
unaffected — their transparent navigation bar continues to draw
edge-to-edge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…screen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gest step text

Otherwise, the view grows as we go from step 1 to step 2
…n in landscape

Widen the ChangeBounds transition scope from cardView to the parent
FrameLayout (daxCtaContainer) and move the content swap to after
beginDelayedTransition. The narrow scope meant the FrameLayout resized
instantly while only the card's internals animated, causing a visible
"square" flash in landscape where the height change is dramatic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update ConstraintLayout params when transitioning to the address bar
screen. On tablet, constrain the dialog above the bobbing dax with
centered bias, matching the comparison chart pattern. On phone, anchor
to parent bottom with top bias. Previously the constraints were stale
from the comparison chart step, causing the dialog to shift down when
the wing animation went GONE asynchronously.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tions after rotation

Extract scaleType detection and backgroundPrimary override into a
single configureBackgroundView() method used by both transitionTo()
and snapTo(). Previously snapTo() checked inView.scaleType directly,
which returned centerCrop when ping-ponging back to backgroundPrimary
(a LottieAnimationView). This skipped the fitCenter override and
dimensionRatio on tablet and phone landscape, causing the background
to render oversized on every other snapTo() call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set translationZ on the dialog FrameLayout so it draws above the
exiting wing animation. In phone landscape the dialog's ChangeBounds
animation makes it visually taller than its layout bounds, causing
the wing (positioned by layout below the dialog) to overlap. Raising
the dialog's z-order hides this overlap without affecting constraints
or the wing's dismiss animation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…screen

Replace static DaxTextView title with TypeAnimationTextView that types in
character-by-character after the ChangeBounds transition, then fades in the
options container and CTA. Matches the pattern used in COMPARISON_CHART and
INPUT_SCREEN.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor Author

mikescamell commented Apr 8, 2026

@mikescamell mikescamell changed the title fix: Ensure Wing is gone after it's finished animating feat(onboarding): implement address bar position screen for brand design update Apr 8, 2026
@mikescamell mikescamell changed the title feat(onboarding): implement address bar position screen for brand design update Onboarding Brand Design Update: Add address bar position screen Apr 8, 2026
@mikescamell
Copy link
Copy Markdown
Contributor Author

bugbot run

@LukasPaczos LukasPaczos self-assigned this Apr 8, 2026
Track ADDRESS_BAR_POSITION ChangeBounds transition for cleanup in
onDestroyView and use safer view == null guard. Align top unselected
light drawable colors (#242323/0.4) with bottom and split variants.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mikescamell
Copy link
Copy Markdown
Contributor Author

bugbot run

Defer primaryCta click listener to onAnimationEnd so the button isn't
tappable while invisible during the address bar transition. Remove
duplicate logo/title alpha assignments in showDialogWithoutAnimation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mikescamell
Copy link
Copy Markdown
Contributor Author

bugbot run

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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 0bfe881. Configure here.

@mikescamell mikescamell marked this pull request as ready for review April 8, 2026 16:06
@mikescamell mikescamell requested a review from malmstein as a code owner April 8, 2026 16:06
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.

Works well 👍 Some comments below - I think the most important is to address the radio button touch area, rest we can address here or in follow ups, or not at all.

  1. The actual radio button is not clickable, only the pictogram is.
Screen_recording_20260409_082104.mp4
  1. The background doesn't go all the way to the bottom edge on Samsung Tab S9+. Not an issue on Pixel 9 Pro.
Image
  1. configureDaxCta function is already over 300 LOC, and it's not even all of it yet. Similar pattern of huge switch-cases repeats across the classes. I understand you're following a previously established pattern, but this project would be a great opportunity to improve that code organization. It's not something to address in this PR but maybe worth considering before shipping all of the steps?


topOmnibarToggleImage.setOnClickListener {
viewModel.onAddressBarPositionOptionSelected(OmnibarType.SINGLE_TOP)
setAddressBarPositionOptions(OmnibarType.SINGLE_TOP, showSplitOption)
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: might be cleaner to rely on the state in the view model instead of changing the option directly on button click, just in case the state is ever modified outside of the button interactions

Comment thread app/src/main/res/layout/include_brand_design_address_bar_position.xml Outdated
@mikescamell
Copy link
Copy Markdown
Contributor Author

Works well 👍 Some comments below - I think the most important is to address the radio button touch area, rest we can address here or in follow ups, or not at all.

  1. The actual radio button is not clickable, only the pictogram is.

Screen_recording_20260409_082104.mp4
2. The background doesn't go all the way to the bottom edge on Samsung Tab S9+. Not an issue on Pixel 9 Pro.

Image 3. `configureDaxCta` function is already over 300 LOC, and it's not even all of it yet. Similar pattern of huge switch-cases repeats across the classes. I understand you're following a previously established pattern, but this project would be a great opportunity to improve that code organization. It's not something to address in this PR but maybe worth considering before shipping all of the steps?
  1. Yeah good point, I need to figure out how we can handle the click ripples, because it looked odd previously where both the image and the radio button would ripple individually

  2. That is odd, I'll look into this but first time I'm seeing a device not respect edgetoedge for the new onboarding

  3. Agreed, I wanted to ask Claude to refactor this after the final dialog PR to get something a little nicer 👍

mikescamell and others added 3 commits April 9, 2026 10:37
…e calls

Address review feedback: replace all `resources.configuration.smallestScreenWidthDp >= 600`
checks with `deviceInfo.isTablet()` extension, and remove unnecessary `?` on
non-null `bobbingDaxAnimation` binding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…spacing

Move click listeners from ImageView to parent container so the entire option
(image, label, radio button) is tappable. Move ripple foreground to container
for visual feedback across the full touch area. Replace fixed 56dp margins with
Space views (0dp width, max 56dp) in a packed chain so spacing compresses on
phones but caps at 56dp on tablets. Switch android:src to tools:src since
drawables are immediately overwritten by code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mikescamell
Copy link
Copy Markdown
Contributor Author

Works well 👍 Some comments below - I think the most important is to address the radio button touch area, rest we can address here or in follow ups, or not at all.

  1. The actual radio button is not clickable, only the pictogram is.

Screen_recording_20260409_082104.mp4
2. The background doesn't go all the way to the bottom edge on Samsung Tab S9+. Not an issue on Pixel 9 Pro.
Image
3. configureDaxCta function is already over 300 LOC, and it's not even all of it yet. Similar pattern of huge switch-cases repeats across the classes. I understand you're following a previously established pattern, but this project would be a great opportunity to improve that code organization. It's not something to address in this PR but maybe worth considering before shipping all of the steps?

  1. Yeah good point, I need to figure out how we can handle the click ripples, because it looked odd previously where both the image and the radio button would ripple individually
  2. That is odd, I'll look into this but first time I'm seeing a device not respect edgetoedge for the new onboarding
  3. Agreed, I wanted to ask Claude to refactor this after the final dialog PR to get something a little nicer 👍

Works well 👍 Some comments below - I think the most important is to address the radio button touch area, rest we can address here or in follow ups, or not at all.

  1. The actual radio button is not clickable, only the pictogram is.

Screen_recording_20260409_082104.mp4
2. The background doesn't go all the way to the bottom edge on Samsung Tab S9+. Not an issue on Pixel 9 Pro.
Image
3. configureDaxCta function is already over 300 LOC, and it's not even all of it yet. Similar pattern of huge switch-cases repeats across the classes. I understand you're following a previously established pattern, but this project would be a great opportunity to improve that code organization. It's not something to address in this PR but maybe worth considering before shipping all of the steps?

  1. Yeah good point, I need to figure out how we can handle the click ripples, because it looked odd previously where both the image and the radio button would ripple individually
  2. That is odd, I'll look into this but first time I'm seeing a device not respect edgetoedge for the new onboarding
  3. Agreed, I wanted to ask Claude to refactor this after the final dialog PR to get something a little nicer 👍
  1. Seems to be a Samsung tablet issue, and it's applying a scrim. The onboarding is still edgetoedge and this doesn't seem to happen on phones as evidenced from this screenshot. I'm going to leave this as is. I do not think it's detrimental to the designs, and we get a similar effect if someone is using 3 button nav instead of gestures regardless.
image

mikescamell and others added 2 commits April 9, 2026 12:27
Remove direct setAddressBarPositionOptions calls from click handlers,
relying on viewState flow to update the UI via showDialogWithoutAnimation.
Single source of truth for the selected address bar position.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add missing translationZ restoration in showDialogWithoutAnimation for
ADDRESS_BAR_POSITION, matching the animated path. Qualify isAnimating
with this. inside apply block to clarify it refers to the
LottieAnimationView property, not the fragment field.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

The full container ripple doesn't look great:

Screen_recording_20260409_134327.mp4

I think we should do the same we do in the appearance and AI settings screens, so that only the pictogram ripples:

Screen_recording_20260409_134524.mp4

mikescamell and others added 2 commits April 9, 2026 14:18
…ve ripple to images

Thread selectedAddressBarPosition from viewState through configureDaxCta
and showDialogWithoutAnimation so the initial selection reflects the
persisted state rather than hardcoding SINGLE_TOP. Remove default
parameter values to enforce explicit passing.

Move ripple foreground from container back to ImageView so only the
image shows the ripple effect. Container remains clickable for a larger
touch target but without its own ripple.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread app/src/main/res/layout/include_brand_design_address_bar_position.xml Outdated
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!

mikescamell and others added 2 commits April 9, 2026 15:20
…o button

ViewModel already defaults selectedAddressBarPosition to SINGLE_TOP and
setAddressBarPositionOptions() always runs before the container is visible.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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 31c3d6d. Configure here.

…ut changes

Ensures requestLayout() is called after modifying ConstraintLayout params
in showDialogWithoutAnimation, matching the COMPARISON_CHART case pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mikescamell mikescamell merged commit 7bc45f8 into develop Apr 9, 2026
17 of 18 checks passed
@mikescamell mikescamell deleted the feature/mike/onboading-brand-design-updates/address-bar-position branch April 9, 2026 16:07
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