Dynamic Color Tile Update
Update wallpaper color tile preview mapping in revamped UI. Also
adjusted to adapt to new requirement for color preview to be different
between ligth and dark theme. Instead of providing color preview based
on light or dark theme in the view model, which isn't aware of context
changes, this CL refactors so that light and dark colors are both
included in the view model and then binded based on the view's context.
Colors in Original UI: https://screenshot.googleplex.com/paBb2pTDc3UrjS8.png
Colors in Revamped UI Dark Theme: https://screenshot.googleplex.com/5RWjka3KkZZNHLE.png
Colors in Revamped UI Light Theme: https://screenshot.googleplex.com/3ySrCMuuTzjEmPo.png
Bug: 275625332
Test: Verified manually on both revamped and original UI, and verified
that colors change when dark theme toggle is updated. Also made sure
color picker interactor and view model tests still pass
Change-Id: I4299aa5d4132646f1516599f9f7f77ce4d740a09
diff --git a/src/com/android/customization/model/color/ColorCustomizationManager.java b/src/com/android/customization/model/color/ColorCustomizationManager.java
index 29f6ba6..790c86f 100644
--- a/src/com/android/customization/model/color/ColorCustomizationManager.java
+++ b/src/com/android/customization/model/color/ColorCustomizationManager.java
@@ -181,7 +181,23 @@
if (lockWallpaperColors != null && mLockWallpaperColors.equals(mHomeWallpaperColors)) {
lockWallpaperColors = null;
}
- mProvider.fetch(callback, reload, mHomeWallpaperColors, lockWallpaperColors);
+ mProvider.fetch(callback, reload, mHomeWallpaperColors,
+ lockWallpaperColors, /* shouldUseRevampedUi= */ false);
+ }
+
+ /**
+ * Fetch options function for the customization hub revamped UI
+ *
+ * TODO (b/276417460): refactor to reduce code repetition with the other fetch options function
+ */
+ public void fetchRevampedUIOptions(OptionsFetchedListener<ColorOption> callback,
+ boolean reload) {
+ WallpaperColors lockWallpaperColors = mLockWallpaperColors;
+ if (lockWallpaperColors != null && mLockWallpaperColors.equals(mHomeWallpaperColors)) {
+ lockWallpaperColors = null;
+ }
+ mProvider.fetch(callback, reload, mHomeWallpaperColors,
+ lockWallpaperColors, /* shouldUseRevampedUi= */ true);
}
/**
diff --git a/src/com/android/customization/model/color/ColorOptionsProvider.java b/src/com/android/customization/model/color/ColorOptionsProvider.java
index 2803c7b..166b4da 100644
--- a/src/com/android/customization/model/color/ColorOptionsProvider.java
+++ b/src/com/android/customization/model/color/ColorOptionsProvider.java
@@ -70,8 +70,12 @@
* @param homeWallpaperColors to get seed colors from
* @param lockWallpaperColors WallpaperColors from the lockscreen wallpaper to get seeds from,
* if different than homeWallpaperColors
+ * @param shouldUseRevampedUi fetches color options with new preview mappings for the revamped
+ * UI if set to true
*/
void fetch(OptionsFetchedListener<ColorOption> callback, boolean reload,
@Nullable WallpaperColors homeWallpaperColors,
- @Nullable WallpaperColors lockWallpaperColors);
+ @Nullable WallpaperColors lockWallpaperColors,
+ boolean shouldUseRevampedUi
+ );
}
diff --git a/src/com/android/customization/model/color/ColorProvider.kt b/src/com/android/customization/model/color/ColorProvider.kt
index f91ec6b..639a886 100644
--- a/src/com/android/customization/model/color/ColorProvider.kt
+++ b/src/com/android/customization/model/color/ColorProvider.kt
@@ -87,7 +87,8 @@
callback: OptionsFetchedListener<ColorOption>?,
reload: Boolean,
homeWallpaperColors: WallpaperColors?,
- lockWallpaperColors: WallpaperColors?
+ lockWallpaperColors: WallpaperColors?,
+ shouldUseRevampedUi: Boolean
) {
val wallpaperColorsChanged =
this.homeWallpaperColors != homeWallpaperColors ||
@@ -103,7 +104,11 @@
loadPreset()
}
if (wallpaperColorsChanged || reload) {
- loadSeedColors(homeWallpaperColors, lockWallpaperColors)
+ loadSeedColors(
+ homeWallpaperColors,
+ lockWallpaperColors,
+ shouldUseRevampedUi
+ )
}
} catch (e: Throwable) {
colorsAvailable = false
@@ -127,7 +132,8 @@
private fun loadSeedColors(
homeWallpaperColors: WallpaperColors?,
- lockWallpaperColors: WallpaperColors?
+ lockWallpaperColors: WallpaperColors?,
+ shouldUseRevampedUi: Boolean,
) {
if (homeWallpaperColors == null) return
@@ -147,7 +153,8 @@
colorsPerSource,
if (shouldLockColorsGoFirst) COLOR_SOURCE_LOCK else COLOR_SOURCE_HOME,
true,
- bundles
+ bundles,
+ shouldUseRevampedUi
)
// Second half of the colors
buildColorSeeds(
@@ -155,10 +162,18 @@
MAX_SEED_COLORS - bundles.size / styleSize,
if (shouldLockColorsGoFirst) COLOR_SOURCE_HOME else COLOR_SOURCE_LOCK,
false,
- bundles
+ bundles,
+ shouldUseRevampedUi
)
} else {
- buildColorSeeds(homeWallpaperColors, colorsPerSource, COLOR_SOURCE_HOME, true, bundles)
+ buildColorSeeds(
+ homeWallpaperColors,
+ colorsPerSource,
+ COLOR_SOURCE_HOME,
+ true,
+ bundles,
+ shouldUseRevampedUi
+ )
}
bundles.addAll(colorBundles?.filterNot { it is ColorSeedOption } ?: emptyList())
@@ -170,13 +185,14 @@
maxColors: Int,
source: String,
containsDefault: Boolean,
- bundles: MutableList<ColorOption>
+ bundles: MutableList<ColorOption>,
+ shouldUseRevampedUi: Boolean,
) {
val seedColors = ColorScheme.getSeedColors(wallpaperColors)
val defaultSeed = seedColors.first()
- buildBundle(defaultSeed, 0, containsDefault, source, bundles)
+ buildBundle(defaultSeed, 0, containsDefault, source, bundles, shouldUseRevampedUi)
for ((i, colorInt) in seedColors.drop(1).take(maxColors - 1).withIndex()) {
- buildBundle(colorInt, i + 1, false, source, bundles)
+ buildBundle(colorInt, i + 1, false, source, bundles, shouldUseRevampedUi)
}
}
@@ -185,7 +201,8 @@
i: Int,
isDefault: Boolean,
source: String,
- bundles: MutableList<ColorOption>
+ bundles: MutableList<ColorOption>,
+ shouldUseRevampedUi: Boolean,
) {
// TODO(b/202145216): Measure time cost in the loop.
for (style in styleList) {
@@ -193,8 +210,14 @@
val lightColorScheme = ColorScheme(colorInt, /* darkTheme= */ false, style)
val darkColorScheme = ColorScheme(colorInt, /* darkTheme= */ true, style)
builder
- .setLightColors(lightColorScheme.getLightColorPreview())
- .setDarkColors(darkColorScheme.getDarkColorPreview())
+ .setLightColors(
+ if (shouldUseRevampedUi) lightColorScheme.getRevampedUILightColorPreview()
+ else lightColorScheme.getLightColorPreview()
+ )
+ .setDarkColors(
+ if (shouldUseRevampedUi) darkColorScheme.getRevampedUIDarkColorPreview()
+ else darkColorScheme.getDarkColorPreview()
+ )
.addOverlayPackage(
OVERLAY_CATEGORY_SYSTEM_PALETTE,
if (isDefault) "" else toColorString(colorInt)
@@ -247,6 +270,30 @@
return getLightColorPreview()
}
+ @ColorInt
+ private fun ColorScheme.getRevampedUILightColorPreview(): IntArray {
+ return intArrayOf(
+ setAlphaComponent(this.accent1.s600, ALPHA_MASK),
+ setAlphaComponent(this.accent1.s600, ALPHA_MASK),
+ setAlphaComponent(this.accent2.s100, ALPHA_MASK),
+ ColorStateList.valueOf(this.accent3.s500).withLStar(59f).colors[0],
+ )
+ }
+
+ /**
+ * Returns the color for the dark theme version of the preview of a ColorScheme based on this
+ * order: |-------| | 0 | 1 | |---+---| | 2 | 3 | |-------|
+ */
+ @ColorInt
+ private fun ColorScheme.getRevampedUIDarkColorPreview(): IntArray {
+ return intArrayOf(
+ setAlphaComponent(this.accent1.s200, ALPHA_MASK),
+ setAlphaComponent(this.accent1.s200, ALPHA_MASK),
+ setAlphaComponent(this.accent2.s700, ALPHA_MASK),
+ setAlphaComponent(this.accent3.s100, ALPHA_MASK),
+ )
+ }
+
private fun ColorScheme.getPresetColorPreview(seed: Int): IntArray {
return when (this.style) {
Style.FRUIT_SALAD -> intArrayOf(seed, this.accent1.s100)
diff --git a/src/com/android/customization/model/color/ColorSeedOption.java b/src/com/android/customization/model/color/ColorSeedOption.java
index ba61ed1..ed38049 100644
--- a/src/com/android/customization/model/color/ColorSeedOption.java
+++ b/src/com/android/customization/model/color/ColorSeedOption.java
@@ -100,6 +100,7 @@
/**
* Returns the colors to be applied corresponding with the current
* configuration's UI mode.
+ * @param res resources to read to the UI mode configuration from
* @return one of {@link #lightColors} or {@link #darkColors}
*/
@ColorInt
@@ -108,6 +109,17 @@
== Configuration.UI_MODE_NIGHT_YES;
return night ? darkColors : lightColors;
}
+
+ /**
+ * Returns the preview colors based on whether dark theme or light theme colors are
+ * requested.
+ * @param darkTheme if true, returns dark theme colors, otherwise returns light theme colors
+ * @return one of {@link #lightColors} or {@link #darkColors}
+ */
+ @ColorInt
+ public int[] resolveColors(boolean darkTheme) {
+ return darkTheme ? darkColors : lightColors;
+ }
}
/**
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
index b358873..9a67c49 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
@@ -15,6 +15,7 @@
*/
package com.android.customization.picker.clock.ui.binder
+import android.content.res.Configuration
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
@@ -67,7 +68,10 @@
lifecycleOwner = lifecycleOwner,
bindIcon = { foregroundView: View, colorIcon: ColorOptionIconViewModel ->
val viewGroup = foregroundView as? ViewGroup
- viewGroup?.let { ColorOptionIconBinder.bind(viewGroup, colorIcon) }
+ val night =
+ (view.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
+ Configuration.UI_MODE_NIGHT_YES)
+ viewGroup?.let { ColorOptionIconBinder.bind(viewGroup, colorIcon, night) }
}
)
colorOptionContainerView.adapter = colorOptionAdapter
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
index b66f977..a0ac370 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
@@ -145,10 +145,14 @@
key = MutableStateFlow(colorModel.colorId) as StateFlow<String>,
payload =
ColorOptionIconViewModel(
- colorModel.color,
- colorModel.color,
- colorModel.color,
- colorModel.color,
+ lightThemeColor0 = colorModel.color,
+ lightThemeColor1 = colorModel.color,
+ lightThemeColor2 = colorModel.color,
+ lightThemeColor3 = colorModel.color,
+ darkThemeColor0 = colorModel.color,
+ darkThemeColor1 = colorModel.color,
+ darkThemeColor2 = colorModel.color,
+ darkThemeColor3 = colorModel.color,
),
text =
Text.Loaded(
@@ -200,8 +204,26 @@
val optionItemPayload =
when (colorOption) {
is ColorSeedOption -> {
- val colors = colorOption.previewInfo.resolveColors(context.resources)
- ColorOptionIconViewModel(colors[0], colors[1], colors[2], colors[3])
+ val lightThemeColors =
+ colorOption.previewInfo.resolveColors(
+ /** darkTheme= */
+ false
+ )
+ val darkThemeColors =
+ colorOption.previewInfo.resolveColors(
+ /** darkTheme= */
+ true
+ )
+ ColorOptionIconViewModel(
+ lightThemeColor0 = lightThemeColors[0],
+ lightThemeColor1 = lightThemeColors[1],
+ lightThemeColor2 = lightThemeColors[2],
+ lightThemeColor3 = lightThemeColors[3],
+ darkThemeColor0 = darkThemeColors[0],
+ darkThemeColor1 = darkThemeColors[1],
+ darkThemeColor2 = darkThemeColors[2],
+ darkThemeColor3 = darkThemeColors[3],
+ )
}
is ColorBundle -> {
val primaryColor =
@@ -209,10 +231,14 @@
val secondaryColor =
colorOption.previewInfo.resolveSecondaryColor(context.resources)
ColorOptionIconViewModel(
- primaryColor,
- secondaryColor,
- primaryColor,
- secondaryColor
+ lightThemeColor0 = primaryColor,
+ lightThemeColor1 = secondaryColor,
+ lightThemeColor2 = primaryColor,
+ lightThemeColor3 = secondaryColor,
+ darkThemeColor0 = primaryColor,
+ darkThemeColor1 = secondaryColor,
+ darkThemeColor2 = primaryColor,
+ darkThemeColor3 = secondaryColor,
)
}
else -> null
diff --git a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
index 512a500..a044174 100644
--- a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
@@ -52,7 +52,7 @@
.map { (homeColors, lockColors) ->
suspendCancellableCoroutine { continuation ->
colorManager.setWallpaperColors(homeColors, lockColors)
- colorManager.fetchOptions(
+ colorManager.fetchRevampedUIOptions(
object : CustomizationManager.OptionsFetchedListener<ColorOption?> {
override fun onOptionsLoaded(options: MutableList<ColorOption?>?) {
val wallpaperColorOptions: MutableList<ColorOptionModel> =
diff --git a/src/com/android/customization/picker/color/ui/binder/ColorOptionIconBinder.kt b/src/com/android/customization/picker/color/ui/binder/ColorOptionIconBinder.kt
index 1478cc4..d238180 100644
--- a/src/com/android/customization/picker/color/ui/binder/ColorOptionIconBinder.kt
+++ b/src/com/android/customization/picker/color/ui/binder/ColorOptionIconBinder.kt
@@ -28,14 +28,30 @@
fun bind(
view: ViewGroup,
viewModel: ColorOptionIconViewModel,
+ darkTheme: Boolean,
) {
val color0View: ImageView = view.requireViewById(R.id.color_preview_0)
val color1View: ImageView = view.requireViewById(R.id.color_preview_1)
val color2View: ImageView = view.requireViewById(R.id.color_preview_2)
val color3View: ImageView = view.requireViewById(R.id.color_preview_3)
- color0View.drawable.colorFilter = BlendModeColorFilter(viewModel.color0, BlendMode.SRC)
- color1View.drawable.colorFilter = BlendModeColorFilter(viewModel.color1, BlendMode.SRC)
- color2View.drawable.colorFilter = BlendModeColorFilter(viewModel.color2, BlendMode.SRC)
- color3View.drawable.colorFilter = BlendModeColorFilter(viewModel.color3, BlendMode.SRC)
+ if (darkTheme) {
+ color0View.drawable.colorFilter =
+ BlendModeColorFilter(viewModel.darkThemeColor0, BlendMode.SRC)
+ color1View.drawable.colorFilter =
+ BlendModeColorFilter(viewModel.darkThemeColor1, BlendMode.SRC)
+ color2View.drawable.colorFilter =
+ BlendModeColorFilter(viewModel.darkThemeColor2, BlendMode.SRC)
+ color3View.drawable.colorFilter =
+ BlendModeColorFilter(viewModel.darkThemeColor3, BlendMode.SRC)
+ } else {
+ color0View.drawable.colorFilter =
+ BlendModeColorFilter(viewModel.lightThemeColor0, BlendMode.SRC)
+ color1View.drawable.colorFilter =
+ BlendModeColorFilter(viewModel.lightThemeColor1, BlendMode.SRC)
+ color2View.drawable.colorFilter =
+ BlendModeColorFilter(viewModel.lightThemeColor2, BlendMode.SRC)
+ color3View.drawable.colorFilter =
+ BlendModeColorFilter(viewModel.lightThemeColor3, BlendMode.SRC)
+ }
}
}
diff --git a/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt b/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
index 7623048..452e8b6 100644
--- a/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
+++ b/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
@@ -17,6 +17,7 @@
package com.android.customization.picker.color.ui.binder
+import android.content.res.Configuration
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
@@ -32,7 +33,6 @@
import com.android.customization.picker.common.ui.view.ItemSpacing
import com.android.wallpaper.R
import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -64,7 +64,10 @@
lifecycleOwner = lifecycleOwner,
bindIcon = { foregroundView: View, colorIcon: ColorOptionIconViewModel ->
val viewGroup = foregroundView as? ViewGroup
- viewGroup?.let { ColorOptionIconBinder.bind(viewGroup, colorIcon) }
+ val night =
+ (view.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
+ Configuration.UI_MODE_NIGHT_YES)
+ viewGroup?.let { ColorOptionIconBinder.bind(viewGroup, colorIcon, night) }
}
)
colorOptionContainerView.adapter = colorOptionAdapter
diff --git a/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt b/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
index 05b0916..10013e6 100644
--- a/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
+++ b/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
@@ -17,6 +17,7 @@
package com.android.customization.picker.color.ui.binder
+import android.content.res.Configuration
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -91,10 +92,15 @@
})
.let { if (it < 0) 0 else it }
options.subList(0, colorOptionSlotSize).forEach { item ->
+ val night =
+ (view.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
+ Configuration.UI_MODE_NIGHT_YES)
val itemView =
LayoutInflater.from(view.context)
.inflate(R.layout.color_option_no_background, view, false)
- item.payload?.let { ColorOptionIconBinder.bind(itemView as ViewGroup, item.payload) }
+ item.payload?.let {
+ ColorOptionIconBinder.bind(itemView as ViewGroup, item.payload, night)
+ }
val optionSelectedView = itemView.findViewById<ImageView>(R.id.option_selected)
lifecycleOwner.lifecycleScope.launch {
diff --git a/src/com/android/customization/picker/color/ui/viewmodel/ColorOptionIconViewModel.kt b/src/com/android/customization/picker/color/ui/viewmodel/ColorOptionIconViewModel.kt
index d32538d..8723c8c 100644
--- a/src/com/android/customization/picker/color/ui/viewmodel/ColorOptionIconViewModel.kt
+++ b/src/com/android/customization/picker/color/ui/viewmodel/ColorOptionIconViewModel.kt
@@ -20,8 +20,12 @@
import android.annotation.ColorInt
data class ColorOptionIconViewModel(
- @ColorInt val color0: Int,
- @ColorInt val color1: Int,
- @ColorInt val color2: Int,
- @ColorInt val color3: Int,
+ @ColorInt val lightThemeColor0: Int,
+ @ColorInt val lightThemeColor1: Int,
+ @ColorInt val lightThemeColor2: Int,
+ @ColorInt val lightThemeColor3: Int,
+ @ColorInt val darkThemeColor0: Int,
+ @ColorInt val darkThemeColor1: Int,
+ @ColorInt val darkThemeColor2: Int,
+ @ColorInt val darkThemeColor3: Int,
)
diff --git a/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt b/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt
index 73c1ac6..97dbba6 100644
--- a/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt
+++ b/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt
@@ -101,8 +101,14 @@
colorOptionEntry.value.map { colorOptionModel ->
val colorSeedOption: ColorSeedOption =
colorOptionModel.colorOption as ColorSeedOption
- val colors =
- colorSeedOption.previewInfo.resolveColors(context.resources)
+ val lightThemeColors =
+ colorSeedOption.previewInfo.resolveColors(
+ /* darkTheme= */ false
+ )
+ val darkThemeColors =
+ colorSeedOption.previewInfo.resolveColors(
+ /* darkTheme= */ true
+ )
val isSelectedFlow: StateFlow<Boolean> =
interactor.activeColorOption
.map {
@@ -118,10 +124,14 @@
as StateFlow<String>,
payload =
ColorOptionIconViewModel(
- colors[0],
- colors[1],
- colors[2],
- colors[3]
+ lightThemeColor0 = lightThemeColors[0],
+ lightThemeColor1 = lightThemeColors[1],
+ lightThemeColor2 = lightThemeColors[2],
+ lightThemeColor3 = lightThemeColors[3],
+ darkThemeColor0 = darkThemeColors[0],
+ darkThemeColor1 = darkThemeColors[1],
+ darkThemeColor2 = darkThemeColors[2],
+ darkThemeColor3 = darkThemeColors[3],
),
text =
Text.Loaded(
@@ -173,10 +183,14 @@
as StateFlow<String>,
payload =
ColorOptionIconViewModel(
- primaryColor,
- secondaryColor,
- primaryColor,
- secondaryColor
+ lightThemeColor0 = primaryColor,
+ lightThemeColor1 = secondaryColor,
+ lightThemeColor2 = primaryColor,
+ lightThemeColor3 = secondaryColor,
+ darkThemeColor0 = primaryColor,
+ darkThemeColor1 = secondaryColor,
+ darkThemeColor2 = primaryColor,
+ darkThemeColor3 = secondaryColor,
),
text =
Text.Loaded(