commit 24f55b8044c6eeee335ee5d738a32af57553dab0
parent d2cc7af77543c2e0fc3a80a7b5e9dfe063f7da02
Author: Matthew Tighe <matthewdtighe@gmail.com>
Date: Wed, 19 Nov 2025 19:34:57 +0000
Bug 1999547 - use wallpaper text color for the homepage header items r=android-reviewers,sfamisa,devota
Differential Revision: https://phabricator.services.mozilla.com/D272166
Diffstat:
4 files changed, 123 insertions(+), 21 deletions(-)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/store/HomepageState.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/store/HomepageState.kt
@@ -8,7 +8,9 @@ import android.content.res.Configuration
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.res.colorResource
import mozilla.components.feature.top.sites.TopSite
+import mozilla.components.ui.icons.R
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.components.appstate.AppState
@@ -25,6 +27,7 @@ import org.mozilla.fenix.home.recentsyncedtabs.RecentSyncedTabState
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem
import org.mozilla.fenix.home.topsites.TopSiteColors
+import org.mozilla.fenix.home.ui.getAttr
import org.mozilla.fenix.search.SearchDialogFragment
import org.mozilla.fenix.utils.Settings
@@ -34,9 +37,9 @@ import org.mozilla.fenix.utils.Settings
internal sealed class HomepageState {
/**
- * Whether to show the homepage header.
+ * Data related to the header of the homepage.
*/
- abstract val showHeader: Boolean
+ abstract val headerState: HeaderState
/**
* Flag indicating whether the first frame of the homescreen has been drawn.
@@ -51,13 +54,13 @@ internal sealed class HomepageState {
/**
* State type corresponding with private browsing mode.
*
- * @property showHeader Whether to show the homepage header.
+ * @property headerState State related to the header of the homepage.
* @property firstFrameDrawn Flag indicating whether the first frame of the homescreen has been drawn.
* @property isSearchInProgress Whether search is currently active on the homepage.
* @property privateModeRedesignEnabled Whether private browsing mode redesign is enabled.
*/
internal data class Private(
- override val showHeader: Boolean,
+ override val headerState: HeaderState,
override val firstFrameDrawn: Boolean = false,
override val isSearchInProgress: Boolean,
val privateModeRedesignEnabled: Boolean,
@@ -81,7 +84,7 @@ internal sealed class HomepageState {
* @property showRecentlyVisited Whether to show recent history section.
* @property showPocketStories Whether to show the pocket stories section.
* @property showCollections Whether to show the collections section.
- * @property showHeader Whether to show the homepage header.
+ * @property headerState State related to the header of the homepage.
* @property searchBarVisible Whether the middle search bar should be visible or not.
* @property searchBarEnabled Whether the middle search bar is enabled or not.
* @property firstFrameDrawn Flag indicating whether the first frame of the homescreen has been drawn.
@@ -109,7 +112,7 @@ internal sealed class HomepageState {
val showRecentlyVisited: Boolean,
val showPocketStories: Boolean,
val showCollections: Boolean,
- override val showHeader: Boolean,
+ override val headerState: HeaderState,
val searchBarVisible: Boolean,
val searchBarEnabled: Boolean,
override val firstFrameDrawn: Boolean = false,
@@ -136,7 +139,7 @@ internal sealed class HomepageState {
internal fun isMinimalLayout(): Boolean {
return (this as? Normal)?.run {
!showRecentTabs && !showRecentSyncedTab && !showBookmarks && !showRecentlyVisited &&
- (!showCollections || collectionsState == CollectionsState.Gone) && !showHeader
+ (!showCollections || collectionsState == CollectionsState.Gone) && !headerState.showHeader
} ?: false
}
@@ -179,12 +182,21 @@ internal sealed class HomepageState {
* @param appState State to build the [HomepageState.Private] from.
* @param settings [Settings] corresponding to how the homepage should be displayed.
*/
+ @Composable
private fun buildPrivateState(
appState: AppState,
settings: Settings,
) = with(appState) {
Private(
- showHeader = settings.showHomepageHeader,
+ headerState = HeaderState(
+ showHeader = settings.showHomepageHeader,
+ wordmarkColor = null,
+ privateBrowsingButtonColor = colorResource(
+ getAttr(
+ R.attr.mozac_ic_private_mode_circle_fill_icon_color,
+ ),
+ ),
+ ),
firstFrameDrawn = firstFrameDrawn,
isSearchInProgress = searchState.isSearchActive,
privateModeRedesignEnabled = settings.enablePrivateBrowsingModeRedesign,
@@ -231,7 +243,16 @@ internal sealed class HomepageState {
showPocketStories = settings.showPocketRecommendationsFeature &&
recommendationState.pocketStories.isNotEmpty(),
showCollections = settings.collections,
- showHeader = settings.showHomepageHeader,
+ headerState = HeaderState(
+ showHeader = settings.showHomepageHeader,
+ wordmarkColor = wallpaperState.currentWallpaper.textColor?.let { Color(it) },
+ privateBrowsingButtonColor = wallpaperState.currentWallpaper.textColor
+ ?.let { Color(it) } ?: colorResource(
+ getAttr(
+ R.attr.mozac_ic_private_mode_circle_fill_icon_color,
+ ),
+ ),
+ ),
searchBarVisible = shouldShowSearchBar(appState = appState),
searchBarEnabled = settings.enableHomepageSearchBar &&
settings.toolbarPosition == ToolbarPosition.TOP &&
@@ -254,6 +275,19 @@ internal sealed class HomepageState {
}
/**
+ * A simple wrapper around state required for the homepage header.
+ *
+ * @property showHeader whether the header should be shown
+ * @property wordmarkColor an optional color for the wordmark text and logo
+ * @property privateBrowsingButtonColor the color to use for the private browsing button
+ */
+internal data class HeaderState(
+ val showHeader: Boolean,
+ val wordmarkColor: Color?,
+ val privateBrowsingButtonColor: Color,
+)
+
+/**
* Returns whether the search bar should be shown. Only show if the search dialog
* [SearchDialogFragment] is not visible, and the user does not have their toolbar set to be on the
* bottom, and the screen is not in landscape mode. This is in addition to logic in the view layer
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/Homepage.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/Homepage.kt
@@ -23,6 +23,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
@@ -64,6 +65,7 @@ import org.mozilla.fenix.home.recentvisits.view.RecentlyVisited
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
import org.mozilla.fenix.home.sessioncontrol.MessageCardInteractor
import org.mozilla.fenix.home.setup.ui.SetupChecklist
+import org.mozilla.fenix.home.store.HeaderState
import org.mozilla.fenix.home.store.HomepageState
import org.mozilla.fenix.home.store.NimbusMessageState
import org.mozilla.fenix.home.topsites.TopSiteColors
@@ -74,6 +76,7 @@ import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.theme.Theme
import org.mozilla.fenix.utils.isLargeScreenSize
import org.mozilla.fenix.wallpapers.WallpaperState
+import mozilla.components.ui.icons.R as iconsR
/**
* Top level composable for the homepage.
@@ -114,8 +117,10 @@ internal fun Homepage(
.verticalScroll(scrollState),
horizontalAlignment = Alignment.CenterHorizontally,
) {
- if (state.showHeader) {
+ if (state.headerState.showHeader) {
HomepageHeader(
+ wordmarkColor = state.headerState.wordmarkColor,
+ privateBrowsingButtonColor = state.headerState.privateBrowsingButtonColor,
browsingMode = state.browsingMode,
browsingModeChanged = interactor::onPrivateModeButtonClicked,
)
@@ -479,7 +484,15 @@ private fun HomepagePreview() {
showRecentlyVisited = true,
showPocketStories = true,
showCollections = true,
- showHeader = false,
+ headerState = HeaderState(
+ showHeader = false,
+ wordmarkColor = null,
+ privateBrowsingButtonColor = colorResource(
+ getAttr(
+ iconsR.attr.mozac_ic_private_mode_circle_fill_icon_color,
+ ),
+ ),
+ ),
searchBarVisible = true,
searchBarEnabled = false,
firstFrameDrawn = true,
@@ -521,7 +534,15 @@ private fun HomepagePreviewCollections() {
showRecentlyVisited = true,
showPocketStories = true,
showCollections = true,
- showHeader = false,
+ headerState = HeaderState(
+ showHeader = false,
+ wordmarkColor = null,
+ privateBrowsingButtonColor = colorResource(
+ getAttr(
+ iconsR.attr.mozac_ic_private_mode_circle_fill_icon_color,
+ ),
+ ),
+ ),
searchBarVisible = true,
searchBarEnabled = false,
firstFrameDrawn = true,
@@ -563,7 +584,15 @@ private fun MinimalHomepagePreview() {
showRecentlyVisited = false,
showPocketStories = true,
showCollections = false,
- showHeader = false,
+ HeaderState(
+ showHeader = false,
+ wordmarkColor = null,
+ privateBrowsingButtonColor = colorResource(
+ getAttr(
+ iconsR.attr.mozac_ic_private_mode_circle_fill_icon_color,
+ ),
+ ),
+ ),
searchBarVisible = false,
searchBarEnabled = false,
firstFrameDrawn = true,
@@ -590,7 +619,15 @@ private fun PrivateHomepagePreview() {
FirefoxTheme(theme = Theme.Private) {
Homepage(
state = HomepageState.Private(
- showHeader = false,
+ headerState = HeaderState(
+ showHeader = false,
+ wordmarkColor = null,
+ privateBrowsingButtonColor = colorResource(
+ getAttr(
+ iconsR.attr.mozac_ic_private_mode_circle_fill_icon_color,
+ ),
+ ),
+ ),
firstFrameDrawn = true,
isSearchInProgress = false,
privateModeRedesignEnabled = false,
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/HomepageHeader.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/HomepageHeader.kt
@@ -20,6 +20,8 @@ import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
@@ -45,6 +47,8 @@ import mozilla.components.ui.icons.R as iconsR
*/
@Composable
fun HomepageHeader(
+ wordmarkColor: Color?,
+ privateBrowsingButtonColor: Color,
browsingMode: BrowsingMode,
browsingModeChanged: (BrowsingMode) -> Unit,
) {
@@ -55,13 +59,14 @@ fun HomepageHeader(
.padding(start = 16.dp, end = 16.dp, top = 18.dp, bottom = 32.dp),
verticalAlignment = Alignment.CenterVertically,
) {
- WordmarkLogo()
+ WordmarkLogo(wordmarkColor)
- WordmarkText()
+ WordmarkText(wordmarkColor)
Spacer(modifier = Modifier.weight(1f))
PrivateBrowsingButton(
+ color = privateBrowsingButtonColor,
browsingMode = browsingMode,
browsingModeChanged = browsingModeChanged,
)
@@ -69,7 +74,7 @@ fun HomepageHeader(
}
@Composable
-private fun WordmarkLogo() {
+private fun WordmarkLogo(color: Color?) {
Image(
modifier = Modifier
.height(40.dp)
@@ -79,12 +84,13 @@ private fun WordmarkLogo() {
}
.padding(end = 10.dp),
painter = painterResource(getAttr(R.attr.fenixWordmarkLogo)),
+ colorFilter = color?.let { ColorFilter.tint(it) },
contentDescription = null,
)
}
@Composable
-private fun WordmarkText() {
+private fun WordmarkText(color: Color?) {
Image(
modifier = Modifier
.semantics {
@@ -93,12 +99,14 @@ private fun WordmarkText() {
}
.height(dimensionResource(R.dimen.wordmark_text_height)),
painter = painterResource(getAttr(R.attr.fenixWordmarkText)),
+ colorFilter = color?.let { ColorFilter.tint(it) },
contentDescription = stringResource(R.string.app_name),
)
}
@Composable
private fun PrivateBrowsingButton(
+ color: Color,
browsingMode: BrowsingMode,
browsingModeChanged: (BrowsingMode) -> Unit,
) {
@@ -119,7 +127,7 @@ private fun PrivateBrowsingButton(
},
) {
Icon(
- tint = colorResource(getAttr(iconsR.attr.mozac_ic_private_mode_circle_fill_icon_color)),
+ tint = color,
painter = painterResource(iconsR.drawable.mozac_ic_private_mode_24),
contentDescription = stringResource(R.string.content_description_private_browsing),
)
@@ -127,7 +135,7 @@ private fun PrivateBrowsingButton(
}
@Composable
-private fun getAttr(resId: Int): Int {
+internal fun getAttr(resId: Int): Int {
val typedArray = LocalContext.current.obtainStyledAttributes(intArrayOf(resId))
val newResId = typedArray.getResourceId(0, 0)
typedArray.recycle()
@@ -141,6 +149,12 @@ private fun HomepageHeaderPreview() {
FirefoxTheme {
Surface {
HomepageHeader(
+ wordmarkColor = null,
+ privateBrowsingButtonColor = colorResource(
+ getAttr(
+ iconsR.attr.mozac_ic_private_mode_circle_fill_icon_color,
+ ),
+ ),
browsingMode = BrowsingMode.Normal,
browsingModeChanged = {},
)
@@ -154,6 +168,12 @@ private fun PrivateHomepageHeaderPreview() {
FirefoxTheme(theme = Theme.Private) {
Surface {
HomepageHeader(
+ wordmarkColor = null,
+ privateBrowsingButtonColor = colorResource(
+ getAttr(
+ iconsR.attr.mozac_ic_private_mode_circle_fill_icon_color,
+ ),
+ ),
browsingMode = BrowsingMode.Private,
browsingModeChanged = {},
)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/MiddleSearchHomepage.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/MiddleSearchHomepage.kt
@@ -21,14 +21,17 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.res.colorResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
+import mozilla.components.ui.icons.R
import org.mozilla.fenix.home.fake.FakeHomepagePreview
import org.mozilla.fenix.home.interactor.HomepageInteractor
import org.mozilla.fenix.home.pocket.ui.PocketSection
+import org.mozilla.fenix.home.store.HeaderState
import org.mozilla.fenix.home.store.HomepageState
import org.mozilla.fenix.home.topsites.TopSiteColors
import org.mozilla.fenix.home.ui.HomepageTestTag.HOMEPAGE
@@ -174,7 +177,15 @@ private fun MiddleSearchHomepagePreview() {
showRecentlyVisited = true,
showPocketStories = true,
showCollections = true,
- showHeader = false,
+ headerState = HeaderState(
+ showHeader = false,
+ wordmarkColor = null,
+ privateBrowsingButtonColor = colorResource(
+ getAttr(
+ R.attr.mozac_ic_private_mode_circle_fill_icon_color,
+ ),
+ ),
+ ),
searchBarVisible = true,
searchBarEnabled = true,
firstFrameDrawn = true,