Skip to content

Commit 3fb340e

Browse files
authored
Dialog component for the onboarding design experiment (#6157)
Task/Issue URL: https://app.asana.com/1/137249556945/project/1205648422731273/task/1210409460462702?focus=true ### Description This PR adds new dialog component for use in the upcoming onboarding design experiment. ### Steps to test this PR QA-optional ### No UI changes
1 parent 3650026 commit 3fb340e

File tree

4 files changed

+202
-0
lines changed

4 files changed

+202
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright (c) 2025 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.onboarding.ui.page
18+
19+
import android.animation.AnimatorSet
20+
import android.animation.ValueAnimator
21+
import android.content.Context
22+
import android.util.AttributeSet
23+
import android.view.ViewOutlineProvider
24+
import android.view.animation.PathInterpolator
25+
import android.widget.FrameLayout
26+
import android.widget.ScrollView
27+
import androidx.core.animation.doOnEnd
28+
import androidx.core.content.ContextCompat
29+
import androidx.core.view.isEmpty
30+
import androidx.core.view.marginTop
31+
import androidx.core.view.updateLayoutParams
32+
import com.duckduckgo.app.browser.R
33+
import com.duckduckgo.common.ui.view.toPx
34+
35+
class BuckOnboardingDialogView @JvmOverloads constructor(
36+
context: Context,
37+
attrs: AttributeSet? = null,
38+
defStyle: Int = 0,
39+
) : FrameLayout(context, attrs, defStyle) {
40+
41+
init {
42+
alpha = 0f
43+
background = ContextCompat.getDrawable(context, R.drawable.background_buck_onboarding_dialog)
44+
clipToPadding = false
45+
clipChildren = true
46+
clipToOutline = true
47+
outlineProvider = ViewOutlineProvider.BACKGROUND
48+
}
49+
50+
fun animateEntrance() {
51+
if (isEmpty()) return
52+
53+
val contentView = getChildAt(0)
54+
contentView.alpha = 0f
55+
56+
post {
57+
val animationDuration = 800L
58+
59+
val targetWidth = width
60+
val targetHeight = height
61+
val targetLayoutParamsHeight = layoutParams.height
62+
val targetLayoutParamsWidth = layoutParams.width
63+
val targetMarginTop = marginTop
64+
65+
val initialWidth = 64.toPx()
66+
val initialHeight = 64.toPx()
67+
val initialContentTranslationY = 100f.toPx()
68+
69+
// If the content view is a scrollable view, we want to disable the vertical scrollbar during animation
70+
val verticalScrollbarEnabled = (contentView as? ScrollView)?.isVerticalScrollBarEnabled
71+
72+
// https://m3.material.io/styles/motion/easing-and-duration/tokens-specs#7e37d374-0c1b-4007-8187-6f29bb1fb3e7
73+
val standardEasingInterpolator = PathInterpolator(0.2f, 0f, 0f, 1f)
74+
75+
// Start small (minimal dimensions)
76+
updateLayoutParams {
77+
width = initialWidth
78+
height = initialHeight
79+
80+
// Adjust the top margin to keep the bubble aligned to the bottom
81+
(this as MarginLayoutParams).topMargin = targetMarginTop + (targetHeight - initialHeight)
82+
}
83+
84+
(contentView as? ScrollView)?.isVerticalScrollBarEnabled = false
85+
86+
// Animate alpha
87+
val alphaAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
88+
duration = animationDuration / 2
89+
interpolator = standardEasingInterpolator
90+
91+
addUpdateListener { animator ->
92+
alpha = animator.animatedValue as Float
93+
}
94+
}
95+
96+
// Animate width (horizontal expansion)
97+
val widthAnimator = ValueAnimator.ofInt(initialWidth, targetWidth).apply {
98+
duration = animationDuration / 2
99+
interpolator = standardEasingInterpolator
100+
101+
addUpdateListener { animator ->
102+
updateLayoutParams {
103+
width = animator.animatedValue as Int
104+
}
105+
}
106+
}
107+
108+
// Animate height (vertical expansion from bottom to top)
109+
val heightAnimator = ValueAnimator.ofInt(initialHeight, targetHeight).apply {
110+
duration = animationDuration / 2
111+
startDelay = animationDuration / 2 // begin vertical expansion after horizontal expansion is complete
112+
interpolator = standardEasingInterpolator
113+
114+
addUpdateListener { animator ->
115+
val currentHeight = animator.animatedValue as Int
116+
updateLayoutParams {
117+
height = currentHeight
118+
(this as MarginLayoutParams).topMargin = targetMarginTop + (targetHeight - currentHeight)
119+
}
120+
}
121+
}
122+
123+
// Content fade-in
124+
val contentAlphaAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
125+
duration = animationDuration / 2
126+
startDelay = animationDuration / 2
127+
interpolator = standardEasingInterpolator
128+
129+
addUpdateListener { animator ->
130+
contentView.alpha = animator.animatedValue as Float
131+
}
132+
}
133+
134+
// Content slide-up
135+
val contentTranslationYAnimator = ValueAnimator.ofFloat(initialContentTranslationY, 0f).apply {
136+
duration = animationDuration / 2
137+
startDelay = animationDuration / 2
138+
interpolator = standardEasingInterpolator
139+
140+
addUpdateListener { animator ->
141+
contentView.translationY = animator.animatedValue as Float
142+
}
143+
}
144+
145+
AnimatorSet().apply {
146+
playTogether(
147+
alphaAnimator,
148+
widthAnimator,
149+
heightAnimator,
150+
contentAlphaAnimator,
151+
contentTranslationYAnimator,
152+
)
153+
154+
doOnEnd {
155+
if (verticalScrollbarEnabled != null) {
156+
contentView.isVerticalScrollBarEnabled = verticalScrollbarEnabled
157+
}
158+
159+
updateLayoutParams {
160+
width = targetLayoutParamsWidth
161+
height = targetLayoutParamsHeight
162+
}
163+
}
164+
165+
start()
166+
}
167+
}
168+
}
169+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (c) 2025 DuckDuckGo
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<shape xmlns:android="http://schemas.android.com/apk/res/android"
19+
android:shape="rectangle">
20+
<solid android:color="?attr/daxColorOnboardingDialogBackground"/>
21+
<corners
22+
android:topLeftRadius="32dp"
23+
android:topRightRadius="32dp"
24+
android:bottomRightRadius="32dp"
25+
android:bottomLeftRadius="0dp"/>
26+
</shape>

common/common-ui/src/main/res/values/design-system-theming.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@
245245
<item name="daxColorPreonboardingProgressBarStart">@color/blue20</item>
246246
<item name="daxColorPreonboardingProgressBarCenter">@color/purple20</item>
247247
<item name="daxColorPreonboardingProgressBarEnd">@color/red10</item>
248+
<item name="daxColorOnboardingDialogBackground">@color/daxOnboardingDialogBackgroundColorDark</item>
248249

249250
</style>
250251

@@ -348,6 +349,7 @@
348349
<item name="daxColorPreonboardingProgressBarStart">@color/blue50</item>
349350
<item name="daxColorPreonboardingProgressBarCenter">@color/purple100</item>
350351
<item name="daxColorPreonboardingProgressBarEnd">@color/red50</item>
352+
<item name="daxColorOnboardingDialogBackground">@color/daxOnboardingDialogBackgroundColorLight</item>
351353

352354
</style>
353355

common/common-ui/src/main/res/values/temp_colors.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
<attr name="daxColorPreonboardingProgressBarCenter" format="color" />
2525
<attr name="daxColorPreonboardingProgressBarEnd" format="color" />
2626

27+
<attr name="daxColorOnboardingDialogBackground" format="color" />
28+
2729
<!-- Design System Raw Colors -->
2830
<color name="purple20">#A591DC</color>
2931
<color name="purple100">#6B4EBA</color>
@@ -33,4 +35,7 @@
3335
<color name="buckYellow">#FFE080</color>
3436
<color name="buckLightBlue">#4284F4</color>
3537

38+
<color name="daxOnboardingDialogBackgroundColorLight">#FEFEFE</color>
39+
<color name="daxOnboardingDialogBackgroundColorDark">#011D34</color>
40+
3641
</resources>

0 commit comments

Comments
 (0)