Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9253208
Add brand design radius theme tokens and overlay
mikescamell Sep 9, 2026
c7f73f4
Apply brand design radius overlay through activity themes
mikescamell Sep 9, 2026
6810126
Cover brand design radius overlay in theming tests
mikescamell Sep 9, 2026
9f7ce19
Apply brand design radius to standard bottom sheets
mikescamell Sep 10, 2026
23e2b62
Apply brand design radius to the Fire sheet
mikescamell Sep 11, 2026
bc677b5
Apply brand design radius to Quick Setup sheets
mikescamell Sep 11, 2026
5514166
Apply brand design radius to the Duck.ai sheet
mikescamell Sep 11, 2026
f209583
Apply brand design radius to shared dialogs
mikescamell Sep 11, 2026
f64e599
Apply brand design radius to shared cards
mikescamell Sep 11, 2026
4c61cb0
Apply brand design radius to InfoPanel
mikescamell Sep 20, 2026
b83c90e
Apply brand design radius to Dax bubble dialogs
mikescamell Sep 21, 2026
2707c4a
Apply brand design radius to the Lottie Dax dialog
mikescamell Sep 21, 2026
674eca8
Apply brand design radius to popup menus
mikescamell Sep 22, 2026
02b6e0f
Apply brand design radius to beta pill
mikescamell Sep 22, 2026
507bf40
Apply brand design radius to ADS search bars
mikescamell Sep 22, 2026
5421860
Apply brand design radius to chip backgrounds
mikescamell Sep 22, 2026
c631a79
Apply brand design radius to What's New cards
mikescamell Sep 22, 2026
b5ec00e
Remove app module duplicate of extraLargeShapeCornerRadius
mikescamell Sep 29, 2026
2d46183
Keep brand design menu radius in the light Fire input card
mikescamell Sep 30, 2026
1491889
Merge branch 'develop' into feature/mikescamell/rebrand-radius
mikescamell Sep 30, 2026
7bbd1e6
Use 16dp snackbar corners behind the Radius Changes toggle
mikescamell Oct 1, 2026
8756da4
Fix snackbar content and shadow clipping in the design system preview
mikescamell Oct 1, 2026
29d07f3
Merge branch 'develop' into feature/mikescamell/rebrand-radius
mikescamell Oct 1, 2026
5df90d3
Round bookmark sync promo behind the Radius Changes toggle
mikescamell Oct 2, 2026
ae90453
Clip native input menus to their rounded backgrounds
mikescamell Oct 2, 2026
f9bba36
Merge branch 'develop' into feature/mikescamell/rebrand-radius
mikescamell Oct 5, 2026
9886639
Use test dispatcher for show-on-app-launch feature toggle
mikescamell Oct 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Apply brand design radius to popup menus
  • Loading branch information
mikescamell committed Sep 29, 2026
commit 674eca80ec057f659cf367490f21c35b9005f015
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import android.app.Activity
import android.content.Context
import android.os.Build.VERSION
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
Expand All @@ -42,6 +43,7 @@ open class PopupMenu(
init {
elevation = ELEVATION
animationStyle = android.R.style.Animation_Dialog
contentView.applyMenuRadiusClipping()
applyRoundedRippleCorners()
}

Expand Down Expand Up @@ -185,3 +187,8 @@ open class PopupMenu(
fun getPopupWidth(context: Context): Int = context.resources.getDimensionPixelSize(R.dimen.popupMenuWidth)
}
}

fun View.applyMenuRadiusClipping() {
val value = TypedValue()
clipToOutline = context.theme.resolveAttribute(R.attr.daxMenuClipToOutline, value, true) && value.data != 0
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@

<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/daxColorWindow" />
<corners android:radius="@dimen/smallShapeCornerRadius" />
</shape>
<corners android:radius="?attr/daxMenuRadius" />
</shape>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="@color/black" />
<corners android:bottomLeftRadius="@dimen/smallShapeCornerRadius" android:bottomRightRadius="@dimen/smallShapeCornerRadius" />
<corners
android:bottomLeftRadius="?attr/daxMenuRadius"
android:bottomRightRadius="?attr/daxMenuRadius" />
</shape>
</item>
</ripple>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="@color/black" />
<corners android:radius="@dimen/smallShapeCornerRadius" />
<corners android:radius="?attr/daxMenuRadius" />
</shape>
</item>
</ripple>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="@color/black" />
<corners android:topLeftRadius="@dimen/smallShapeCornerRadius" android:topRightRadius="@dimen/smallShapeCornerRadius" />
<corners
android:topLeftRadius="?attr/daxMenuRadius"
android:topRightRadius="?attr/daxMenuRadius" />
</shape>
</item>
</ripple>
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
<attr name="daxDialogRadius" format="dimension" />
<attr name="daxSheetRadius" format="dimension" />
<attr name="daxLargeSheetRadius" format="dimension" />
<attr name="daxMenuRadius" format="dimension" />
<attr name="daxMenuClipToOutline" format="boolean" />
<attr name="daxOnboardingSheetRadius" format="dimension" />
<attr name="daxInfoPanelRadius" format="dimension" />
<attr name="daxMessageCtaCloseButtonSize" format="dimension" />
Expand Down Expand Up @@ -185,6 +187,8 @@
<item name="daxDialogRadius">@dimen/rebrandContainerRadius</item>
<item name="daxSheetRadius">@dimen/rebrandContainerRadius</item>
<item name="daxLargeSheetRadius">@dimen/rebrandContainerRadius</item>
<item name="daxMenuRadius">@dimen/largeShapeCornerRadius</item>
<item name="daxMenuClipToOutline">true</item>
<item name="daxOnboardingSheetRadius">@dimen/rebrandContainerRadius</item>
<item name="daxInfoPanelRadius">@dimen/rebrandContainerRadius</item>
<item name="daxMessageCtaCloseButtonSize">@dimen/rebrandMessageCtaCloseButtonSize</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@
<item name="daxDialogRadius">@dimen/dialogBorderRadius</item>
<item name="daxSheetRadius">@dimen/dialogBorderRadius</item>
<item name="daxLargeSheetRadius">@dimen/largeShapeCornerRadius</item>
<item name="daxMenuRadius">@dimen/smallShapeCornerRadius</item>
<item name="daxMenuClipToOutline">false</item>
<item name="daxOnboardingSheetRadius">@dimen/onboardingBottomSheetCornerRadius</item>
<item name="daxInfoPanelRadius">@dimen/smallShapeCornerRadius</item>
<item name="daxMessageCtaCloseButtonSize">@dimen/messageCtaCloseButtonSize</item>
Expand Down Expand Up @@ -525,6 +527,8 @@
<item name="daxDialogRadius">@dimen/dialogBorderRadius</item>
<item name="daxSheetRadius">@dimen/dialogBorderRadius</item>
<item name="daxLargeSheetRadius">@dimen/largeShapeCornerRadius</item>
<item name="daxMenuRadius">@dimen/smallShapeCornerRadius</item>
<item name="daxMenuClipToOutline">false</item>
<item name="daxOnboardingSheetRadius">@dimen/onboardingBottomSheetCornerRadius</item>
<item name="daxInfoPanelRadius">@dimen/smallShapeCornerRadius</item>
<item name="daxMessageCtaCloseButtonSize">@dimen/messageCtaCloseButtonSize</item>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Copyright (c) 2026 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.common.ui

import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.RippleDrawable
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.widget.LinearLayout
import androidx.appcompat.content.res.AppCompatResources
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.duckduckgo.common.ui.menu.PopupMenu
import com.duckduckgo.mobile.android.R
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config

@RunWith(AndroidJUnit4::class)
class PopupMenuRadiusDrawableTest {

@Test
fun whenRadiusOverlayIsAbsentThenPopupSurfaceAndCustomRipplesUse8DpInLightAndDarkThemes() {
assertPopupRadii(
overlayEnabled = false,
expectedDimen = R.dimen.smallShapeCornerRadius,
)
}

@Test
@Config(qualifiers = "xhdpi")
fun whenRadiusOverlayIsAppliedThenPopupSurfaceAndCustomRipplesUse16DpInLightAndDarkThemes() {
assertPopupRadii(
overlayEnabled = true,
expectedDimen = R.dimen.largeShapeCornerRadius,
)
}

private fun assertPopupRadii(
overlayEnabled: Boolean,
expectedDimen: Int,
) {
listOf(
R.style.Theme_DuckDuckGo_Light,
R.style.Theme_DuckDuckGo_Dark,
).forEach { themeResId ->
val context = ContextThemeWrapper(RuntimeEnvironment.getApplication(), themeResId)
if (overlayEnabled) {
context.theme.applyStyle(R.style.ThemeOverlay_Rebrand_Radius, true)
}
val expectedRadius = context.resources.getDimension(expectedDimen)

val popupBackground = checkNotNull(AppCompatResources.getDrawable(context, R.drawable.popup_menu_bg)) as GradientDrawable
assertEquals(expectedRadius, popupBackground.getCornerRadius(), 0f)

val singleItemMask = rippleMask(context, R.drawable.ripple_rectangle_rounded)
assertEquals(expectedRadius, singleItemMask.getCornerRadius(), 0f)

val topMask = rippleMask(context, R.drawable.ripple_top_rounded)
assertArrayEquals(
floatArrayOf(expectedRadius, expectedRadius, expectedRadius, expectedRadius, 0f, 0f, 0f, 0f),
checkNotNull(topMask.cornerRadii),
0f,
)

val bottomMask = rippleMask(context, R.drawable.ripple_bottom_rounded)
assertArrayEquals(
floatArrayOf(0f, 0f, 0f, 0f, expectedRadius, expectedRadius, expectedRadius, expectedRadius),
checkNotNull(bottomMask.cornerRadii),
0f,
)

assertPopupStyleUsesSharedBackground(
context,
R.style.Widget_DuckDuckGo_PopupMenu,
android.R.attr.popupBackground,
expectedRadius,
)
assertPopupStyleUsesSharedBackground(
context,
R.style.Widget_DuckDuckGo_PopupMenu,
com.google.android.material.R.attr.popupMenuBackground,
expectedRadius,
)
assertPopupStyleUsesSharedBackground(
context,
R.style.Widget_DuckDuckGo_PopUpOverflowMenu,
android.R.attr.popupBackground,
expectedRadius,
)

val popupContent = LinearLayout(context).apply {
setBackgroundResource(R.drawable.popup_menu_bg)
}
PopupMenu(
layoutInflater = LayoutInflater.from(context),
resourceId = R.layout.view_popup_menu_item,
view = popupContent,
)
assertEquals(overlayEnabled, popupContent.clipToOutline)
}
}

private fun rippleMask(
context: ContextThemeWrapper,
drawableRes: Int,
): GradientDrawable {
val ripple = checkNotNull(AppCompatResources.getDrawable(context, drawableRes)) as RippleDrawable
return checkNotNull(ripple.findDrawableByLayerId(android.R.id.mask)) as GradientDrawable
}

private fun assertPopupStyleUsesSharedBackground(
baseContext: ContextThemeWrapper,
popupStyle: Int,
backgroundAttr: Int,
expectedRadius: Float,
) {
val popupContext = ContextThemeWrapper(baseContext, popupStyle)
val values = popupContext.obtainStyledAttributes(intArrayOf(backgroundAttr))
try {
assertEquals(R.drawable.popup_menu_bg, values.getResourceId(0, 0))
val drawable = checkNotNull(AppCompatResources.getDrawable(popupContext, R.drawable.popup_menu_bg)) as GradientDrawable
assertEquals(expectedRadius, drawable.getCornerRadius(), 0f)
} finally {
values.recycle()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ class ThemingRebrandOverlayTest {
assertEquals(28f, resolveDimension(activity, R.attr.daxDialogRadius), 0f)
assertEquals(28f, resolveDimension(activity, R.attr.daxSheetRadius), 0f)
assertEquals(28f, resolveDimension(activity, R.attr.daxLargeSheetRadius), 0f)
assertEquals(16f, resolveDimension(activity, R.attr.daxMenuRadius), 0f)
assertTrue(resolveBoolean(activity, R.attr.daxMenuClipToOutline))
assertEquals(28f, resolveDimension(activity, R.attr.daxOnboardingSheetRadius), 0f)
assertEquals(28f, resolveDimension(activity, R.attr.daxInfoPanelRadius), 0f)
assertEquals(48f, resolveDimension(activity, R.attr.daxMessageCtaCloseButtonSize), 0f)
Expand All @@ -258,6 +260,8 @@ class ThemingRebrandOverlayTest {
assertEquals(12f, resolveDimension(activity, R.attr.daxDialogRadius), 0f)
assertEquals(12f, resolveDimension(activity, R.attr.daxSheetRadius), 0f)
assertEquals(16f, resolveDimension(activity, R.attr.daxLargeSheetRadius), 0f)
assertEquals(8f, resolveDimension(activity, R.attr.daxMenuRadius), 0f)
assertFalse(resolveBoolean(activity, R.attr.daxMenuClipToOutline))
assertEquals(36f, resolveDimension(activity, R.attr.daxOnboardingSheetRadius), 0f)
assertEquals(8f, resolveDimension(activity, R.attr.daxInfoPanelRadius), 0f)
assertEquals(40f, resolveDimension(activity, R.attr.daxMessageCtaCloseButtonSize), 0f)
Expand All @@ -282,6 +286,8 @@ class ThemingRebrandOverlayTest {
R.attr.daxDialogRadius,
R.attr.daxSheetRadius,
R.attr.daxLargeSheetRadius,
R.attr.daxMenuRadius,
R.attr.daxMenuClipToOutline,
R.attr.daxOnboardingSheetRadius,
R.attr.daxInfoPanelRadius,
R.attr.daxMessageCtaCloseButtonSize,
Expand All @@ -305,6 +311,8 @@ class ThemingRebrandOverlayTest {
assertEquals(28f, resolveDimension(activity, R.attr.daxDialogRadius), 0f)
assertEquals(28f, resolveDimension(activity, R.attr.daxSheetRadius), 0f)
assertEquals(28f, resolveDimension(activity, R.attr.daxLargeSheetRadius), 0f)
assertEquals(16f, resolveDimension(activity, R.attr.daxMenuRadius), 0f)
assertTrue(resolveBoolean(activity, R.attr.daxMenuClipToOutline))
assertEquals(28f, resolveDimension(activity, R.attr.daxOnboardingSheetRadius), 0f)
assertEquals(28f, resolveDimension(activity, R.attr.daxInfoPanelRadius), 0f)
assertEquals(48f, resolveDimension(activity, R.attr.daxMessageCtaCloseButtonSize), 0f)
Expand Down Expand Up @@ -340,6 +348,8 @@ class ThemingRebrandOverlayTest {
assertEquals(28f, resolveDimension(activity, R.attr.daxDialogRadius), 0f)
assertEquals(28f, resolveDimension(activity, R.attr.daxSheetRadius), 0f)
assertEquals(28f, resolveDimension(activity, R.attr.daxLargeSheetRadius), 0f)
assertEquals(16f, resolveDimension(activity, R.attr.daxMenuRadius), 0f)
assertTrue(resolveBoolean(activity, R.attr.daxMenuClipToOutline))
assertEquals(28f, resolveDimension(activity, R.attr.daxOnboardingSheetRadius), 0f)
assertEquals(28f, resolveDimension(activity, R.attr.daxInfoPanelRadius), 0f)
assertEquals(48f, resolveDimension(activity, R.attr.daxMessageCtaCloseButtonSize), 0f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope
import com.duckduckgo.anvil.annotations.InjectWith
import com.duckduckgo.app.browser.favicon.FaviconManager
import com.duckduckgo.common.ui.menu.applyMenuRadiusClipping
import com.duckduckgo.common.ui.view.text.DaxTextView
import com.duckduckgo.common.ui.view.toPx
import com.duckduckgo.common.utils.ViewViewModelFactory
Expand Down Expand Up @@ -325,6 +326,7 @@ class AttachmentView(
val container = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
setBackgroundResource(com.duckduckgo.mobile.android.R.drawable.popup_menu_bg)
applyMenuRadiusClipping()
}
val popup = PopupWindow(
ScrollView(context).apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.lifecycle.lifecycleScope
import com.duckduckgo.anvil.annotations.InjectWith
import com.duckduckgo.common.ui.menu.applyMenuRadiusClipping
import com.duckduckgo.common.ui.view.PopupMenuItemView
import com.duckduckgo.common.ui.view.divider.HorizontalDivider
import com.duckduckgo.common.ui.view.text.DaxTextView
Expand Down Expand Up @@ -231,6 +232,7 @@ class ModelPickerView @JvmOverloads constructor(
val container = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
setBackgroundResource(com.duckduckgo.mobile.android.R.drawable.popup_menu_bg)
applyMenuRadiusClipping()
}
return container
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.lifecycle.lifecycleScope
import com.duckduckgo.anvil.annotations.InjectWith
import com.duckduckgo.common.ui.menu.applyMenuRadiusClipping
import com.duckduckgo.common.ui.view.text.DaxTextView
import com.duckduckgo.common.utils.ViewViewModelFactory
import com.duckduckgo.di.scopes.ViewScope
Expand Down Expand Up @@ -198,6 +199,7 @@ class OptionsView(context: Context, private val host: NativeInputHost) : LinearL
val container = LinearLayout(context).apply {
orientation = VERTICAL
setBackgroundResource(com.duckduckgo.mobile.android.R.drawable.popup_menu_bg)
applyMenuRadiusClipping()
}
val popup = PopupWindow(
ScrollView(context).apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.lifecycle.lifecycleScope
import com.duckduckgo.anvil.annotations.InjectWith
import com.duckduckgo.common.ui.menu.applyMenuRadiusClipping
import com.duckduckgo.common.ui.view.divider.HorizontalDivider
import com.duckduckgo.common.ui.view.text.DaxTextView
import com.duckduckgo.common.utils.ViewViewModelFactory
Expand Down Expand Up @@ -169,6 +170,7 @@ class ReasoningModePickerView @JvmOverloads constructor(
val container = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
setBackgroundResource(com.duckduckgo.mobile.android.R.drawable.popup_menu_bg)
applyMenuRadiusClipping()
}
val popup = PopupWindow(
ScrollView(context).apply {
Expand Down
Loading