Merge "Add grid icon for wallpaper picker" into main
diff --git a/Android.bp b/Android.bp
index 58e6413..5efee51 100644
--- a/Android.bp
+++ b/Android.bp
@@ -74,6 +74,17 @@
         "androidx.recyclerview_recyclerview",
         "SystemUICustomizationLib",
         "hilt_android",
+        // Compose
+        "PlatformComposeCore",
+        "androidx.activity_activity-compose",
+        "androidx.compose.foundation_foundation",
+        "androidx.compose.foundation_foundation-layout",
+        "androidx.compose.material3_material3",
+        "androidx.compose.runtime_runtime",
+        "androidx.compose.ui_ui",
+        "androidx.compose.ui_ui-tooling",
+        "androidx.lifecycle_lifecycle-runtime-compose",
+        "androidx.lifecycle_lifecycle-viewmodel-compose",
     ],
 
     srcs: [
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 2c4355f..5ddf84c 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,7 +7,5 @@
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
 
-flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PATH}
-
 [Tool Paths]
 ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh
diff --git a/res/layout/floating_sheet_clock_color_content.xml b/res/layout/floating_sheet_clock_color_content.xml
index acd274a..79e5b9a 100644
--- a/res/layout/floating_sheet_clock_color_content.xml
+++ b/res/layout/floating_sheet_clock_color_content.xml
@@ -16,6 +16,7 @@
 
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:paddingVertical="@dimen/floating_sheet_content_vertical_padding"
@@ -69,16 +70,15 @@
             android:clipToPadding="false" />
     </FrameLayout>
 
-    <SeekBar
+    <com.google.android.material.slider.Slider
         android:id="@+id/clock_color_slider"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:paddingHorizontal="@dimen/floating_sheet_content_horizontal_padding"
         android:minHeight="@dimen/touch_target_min_height"
-        android:thumb="@null"
         android:contentDescription="@string/accessibility_clock_slider_description"
-        android:background="@null"
-        android:progressDrawable="@drawable/saturation_progress_drawable"
-        android:splitTrack="false" />
+        app:trackHeight="@dimen/slider_track_height"
+        app:thumbHeight="@dimen/slider_thumb_height"
+        android:theme="@style/Theme.Material3.DynamicColors.DayNight" />
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/floating_sheet_clock_font_content.xml b/res/layout/floating_sheet_clock_font_content.xml
index 164b625..ba6c70c 100644
--- a/res/layout/floating_sheet_clock_font_content.xml
+++ b/res/layout/floating_sheet_clock_font_content.xml
@@ -40,7 +40,7 @@
         style="@style/CustomizationOptionEntryTitleTextStyle"
         android:text="@string/tab_placeholder_text" />
 
-    <SeekBar
+    <com.google.android.material.slider.Slider
         android:id="@+id/clock_axis_slider1"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
@@ -50,10 +50,9 @@
         app:layout_constraintStart_toEndOf="@+id/clock_axis_slider_name1"
         app:layout_constraintEnd_toEndOf="parent"
         android:layout_marginVertical="@dimen/clock_axis_control_slider_row_margin_vertical"
-        android:background="@null"
-        android:progressDrawable="@drawable/saturation_progress_drawable"
-        android:splitTrack="false"
-        android:thumb="@null" />
+        app:trackHeight="@dimen/slider_track_height"
+        app:thumbHeight="@dimen/slider_thumb_height"
+        android:theme="@style/Theme.Material3.DynamicColors.DayNight" />
 
     <androidx.constraintlayout.widget.Barrier
         android:id="@+id/barrier1"
@@ -77,7 +76,7 @@
         style="@style/CustomizationOptionEntryTitleTextStyle"
         android:text="@string/tab_placeholder_text" />
 
-    <SeekBar
+    <com.google.android.material.slider.Slider
         android:id="@+id/clock_axis_slider2"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
@@ -87,10 +86,9 @@
         app:layout_constraintStart_toEndOf="@+id/clock_axis_slider_name2"
         app:layout_constraintEnd_toEndOf="parent"
         android:layout_marginVertical="@dimen/clock_axis_control_slider_row_margin_vertical"
-        android:background="@null"
-        android:progressDrawable="@drawable/saturation_progress_drawable"
-        android:splitTrack="false"
-        android:thumb="@null" />
+        app:trackHeight="@dimen/slider_track_height"
+        app:thumbHeight="@dimen/slider_thumb_height"
+        android:theme="@style/Theme.Material3.DynamicColors.DayNight" />
 
     <androidx.constraintlayout.widget.Barrier
         android:id="@+id/barrier2"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 756ae2d..819f7e3 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -212,4 +212,8 @@
     <dimen name="clock_axis_control_slider_row_margin_vertical">10dp</dimen>
     <dimen name="clock_axis_control_switch_row_margin_vertical">8dp</dimen>
     <dimen name="clock_font_apply_padding_start">8dp</dimen>
+
+    <!-- Shared dimensions for Material 3 sliders -->
+    <dimen name="slider_track_height">24dp</dimen>
+    <dimen name="slider_thumb_height">36dp</dimen>
 </resources>
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
index 9508fa1..db38342 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
@@ -104,7 +104,7 @@
 
     /** The currently-selected clock. This also emits the clock color information. */
     override val selectedClock: Flow<ClockMetadataModel> =
-        callbackFlow {
+        callbackFlow<ClockMetadataModel?> {
                 fun send() {
                     val activeClockId = registry.activeClockId
                     val metadata = registry.settings?.metadata
@@ -146,6 +146,9 @@
             }
             .flowOn(mainDispatcher)
             .mapNotNull { it }
+            // Make this a shared flow to prevent ClockRegistry.registerClockChangeListener from
+            // being called every time this flow is collected, since ClockRegistry is a singleton.
+            .shareIn(mainScope, SharingStarted.WhileSubscribed(), 1)
 
     override suspend fun setSelectedClock(clockId: String) {
         registry.mutateSetting { oldSettings ->
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt b/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt
index 15d9088..3c3926f 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockRegistryProvider.kt
@@ -19,7 +19,6 @@
 import android.content.ComponentName
 import android.content.Context
 import android.view.LayoutInflater
-import com.android.systemui.Flags
 import com.android.systemui.plugins.Plugin
 import com.android.systemui.plugins.PluginManager
 import com.android.systemui.shared.clocks.ClockRegistry
@@ -30,6 +29,7 @@
 import com.android.systemui.shared.plugins.PluginManagerImpl
 import com.android.systemui.shared.plugins.PluginPrefs
 import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager_Factory
+import com.android.wallpaper.config.BaseFlags
 import java.util.concurrent.Executors
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -45,19 +45,20 @@
     private val backgroundDispatcher: CoroutineDispatcher,
 ) {
     private val clockRegistry: ClockRegistry by lazy {
+        val flags = BaseFlags.get()
         ClockRegistry(
             context,
             createPluginManager(context),
             coroutineScope,
             mainDispatcher,
             backgroundDispatcher,
-            isEnabled = true,
+            isEnabled = flags.isCustomClocksEnabled(context),
             handleAllUsers = false,
             DefaultClockProvider(
                 ctx = context,
                 layoutInflater = LayoutInflater.from(context),
                 resources = context.resources,
-                isClockReactiveVariantsEnabled = Flags.clockReactiveVariants(),
+                isClockReactiveVariantsEnabled = flags.isClockReactiveVariantsEnabled(),
             ),
             keepAllLoaded = true,
             subTag = "Picker",
@@ -93,7 +94,7 @@
 
                 override fun setDisabled(
                     component: ComponentName,
-                    @PluginEnabler.DisableReason reason: Int
+                    @PluginEnabler.DisableReason reason: Int,
                 ) = Unit
 
                 override fun isEnabled(component: ComponentName): Boolean {
diff --git a/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt b/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
index 8a2edfb..8414960 100644
--- a/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
+++ b/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
@@ -35,6 +35,8 @@
     @ColorInt val seedColor: Int?,
 ) {
     companion object {
+        const val MIN_COLOR_TONE_PROGRESS = 0
+        const val MAX_COLOR_TONE_PROGRESS = 100
         const val DEFAULT_COLOR_TONE_PROGRESS = 75
     }
 }
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
index 0ed0362..27bc42c 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -30,6 +30,7 @@
 import androidx.core.view.doOnPreDraw
 import androidx.core.view.get
 import androidx.core.view.isNotEmpty
+import androidx.core.view.isVisible
 import com.android.customization.picker.clock.shared.ClockSize
 import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselItemViewModel
 import com.android.systemui.plugins.clocks.ClockController
@@ -57,6 +58,7 @@
         val clockCarousel = LayoutInflater.from(context).inflate(R.layout.clock_carousel, this)
         carousel = clockCarousel.requireViewById(R.id.carousel)
         motionLayout = clockCarousel.requireViewById(R.id.motion_container)
+        motionLayout.isVisible = false
         motionLayout.contentDescription = context.getString(R.string.custom_clocks_label)
         clockViewScale =
             TypedValue().let {
@@ -133,6 +135,11 @@
         onClockSelected: (clock: ClockCarouselItemViewModel) -> Unit,
         isTwoPaneAndSmallWidth: Boolean,
     ) {
+        if (clocks.isEmpty()) {
+            // Hide the carousel if clock list is empty
+            motionLayout.isVisible = false
+            return
+        }
         if (isTwoPaneAndSmallWidth) {
             overrideScreenPreviewWidth()
         }
@@ -311,6 +318,7 @@
                 ) {}
             }
         )
+        motionLayout.isVisible = true
     }
 
     fun setSelectedClockIndex(index: Int) {
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 52521d6..88e3f80 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
@@ -31,6 +31,8 @@
 import com.android.customization.picker.color.shared.model.ColorOptionModel
 import com.android.customization.picker.color.shared.model.ColorType
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
+import com.android.systemui.monet.ColorScheme
+import com.android.systemui.monet.Style
 import com.android.themepicker.R
 import com.android.wallpaper.picker.common.text.ui.viewmodel.Text
 import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel
@@ -140,6 +142,16 @@
                 }
 
                 colorMap.values.forEachIndexed { index, colorModel ->
+                    // Adjust clock colors for light theme for better color contrast
+                    val lightThemeColor =
+                        ColorScheme(
+                                /* seed= */ colorModel.color,
+                                /* darkTheme= */ false,
+                                /* style= */ if (colorModel.colorId == "GRAY") Style.MONOCHROMATIC
+                                else Style.RAINBOW,
+                            )
+                            .accent1
+                            .getAtTone(450f)
                     val isSelectedFlow =
                         selectedColorId
                             .map { colorMap.keys.indexOf(it) == index }
@@ -150,10 +162,10 @@
                             key = MutableStateFlow(colorModel.colorId) as StateFlow<String>,
                             payload =
                                 ColorOptionIconViewModel(
-                                    lightThemeColor0 = colorModel.color,
-                                    lightThemeColor1 = colorModel.color,
-                                    lightThemeColor2 = colorModel.color,
-                                    lightThemeColor3 = colorModel.color,
+                                    lightThemeColor0 = lightThemeColor,
+                                    lightThemeColor1 = lightThemeColor,
+                                    lightThemeColor2 = lightThemeColor,
+                                    lightThemeColor3 = lightThemeColor,
                                     darkThemeColor0 = colorModel.color,
                                     darkThemeColor1 = colorModel.color,
                                     darkThemeColor2 = colorModel.color,
diff --git a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl2.kt b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl2.kt
index 5e90b41..96f9368 100644
--- a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl2.kt
+++ b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl2.kt
@@ -16,14 +16,18 @@
  */
 package com.android.customization.picker.color.data.repository
 
+import android.app.WallpaperColors
+import android.app.WallpaperManager
+import android.os.Handler
+import android.os.Looper
 import android.util.Log
 import com.android.customization.model.CustomizationManager
 import com.android.customization.model.color.ColorCustomizationManager
 import com.android.customization.model.color.ColorOption
 import com.android.customization.model.color.ColorOptionImpl
 import com.android.customization.picker.color.shared.model.ColorType
-import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository
-import com.android.wallpaper.picker.customization.shared.model.WallpaperColorsModel
+import com.android.wallpaper.model.Screen
+import com.android.wallpaper.picker.customization.data.content.WallpaperClient
 import com.android.wallpaper.picker.di.modules.BackgroundDispatcher
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -31,11 +35,11 @@
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.suspendCancellableCoroutine
 
 @Singleton
@@ -43,41 +47,41 @@
 @Inject
 constructor(
     @BackgroundDispatcher private val scope: CoroutineScope,
-    wallpaperColorsRepository: WallpaperColorsRepository,
     private val colorManager: ColorCustomizationManager,
+    client: WallpaperClient,
 ) : ColorPickerRepository2 {
 
-    private val homeWallpaperColors: StateFlow<WallpaperColorsModel?> =
-        wallpaperColorsRepository.homeWallpaperColors
-    private val lockWallpaperColors: StateFlow<WallpaperColorsModel?> =
-        wallpaperColorsRepository.lockWallpaperColors
+    private val wallpaperColorsCallback: Flow<Pair<Screen, WallpaperColors?>> =
+        callbackFlow {
+                trySend(Pair(Screen.HOME_SCREEN, client.getWallpaperColors(Screen.HOME_SCREEN)))
+                trySend(Pair(Screen.LOCK_SCREEN, client.getWallpaperColors(Screen.LOCK_SCREEN)))
+                val listener = { colors: WallpaperColors?, which: Int ->
+                    if (which and WallpaperManager.FLAG_SYSTEM != 0) {
+                        trySend(Pair(Screen.HOME_SCREEN, colors))
+                    }
+                    if (which and WallpaperManager.FLAG_LOCK != 0) {
+                        trySend(Pair(Screen.LOCK_SCREEN, colors))
+                    }
+                }
+                client.addOnColorsChangedListener(listener, Handler(Looper.getMainLooper()))
+                awaitClose { client.removeOnColorsChangedListener(listener) }
+            }
+            // Make this a shared flow to make sure only one listener is added.
+            .shareIn(scope = scope, started = SharingStarted.WhileSubscribed(), replay = 1)
+    private val homeWallpaperColors: Flow<WallpaperColors?> =
+        wallpaperColorsCallback
+            .filter { (screen, _) -> screen == Screen.HOME_SCREEN }
+            .map { (_, colors) -> colors }
+    private val lockWallpaperColors: Flow<WallpaperColors?> =
+        wallpaperColorsCallback
+            .filter { (screen, _) -> screen == Screen.LOCK_SCREEN }
+            .map { (_, colors) -> colors }
 
     override val colorOptions: Flow<Map<ColorType, List<ColorOption>>> =
-        combine(homeWallpaperColors, lockWallpaperColors) { homeColors, lockColors ->
-                homeColors to lockColors
-            }
+        combine(homeWallpaperColors, lockWallpaperColors, ::Pair)
             .map { (homeColors, lockColors) ->
                 suspendCancellableCoroutine { continuation ->
-                    if (
-                        homeColors is WallpaperColorsModel.Loading ||
-                            lockColors is WallpaperColorsModel.Loading
-                    ) {
-                        continuation.resumeWith(
-                            Result.success(
-                                mapOf(
-                                    ColorType.WALLPAPER_COLOR to listOf(),
-                                    ColorType.PRESET_COLOR to listOf(),
-                                )
-                            )
-                        )
-                        return@suspendCancellableCoroutine
-                    }
-                    val homeColorsLoaded = homeColors as WallpaperColorsModel.Loaded
-                    val lockColorsLoaded = lockColors as WallpaperColorsModel.Loaded
-                    colorManager.setWallpaperColors(
-                        homeColorsLoaded.colors,
-                        lockColorsLoaded.colors,
-                    )
+                    colorManager.setWallpaperColors(homeColors, lockColors)
                     colorManager.fetchOptions(
                         object : CustomizationManager.OptionsFetchedListener<ColorOption> {
                             override fun onOptionsLoaded(options: MutableList<ColorOption>?) {
@@ -114,12 +118,17 @@
                     )
                 }
             }
+            .shareIn(scope = scope, started = SharingStarted.WhileSubscribed(), replay = 1)
 
-    private val settingsChanged = callbackFlow {
-        trySend(Unit)
-        colorManager.setListener { trySend(Unit) }
-        awaitClose { colorManager.setListener(null) }
-    }
+    private val settingsChanged =
+        callbackFlow {
+                trySend(Unit)
+                colorManager.setListener { trySend(Unit) }
+                awaitClose { colorManager.setListener(null) }
+            }
+            // Make this a shared flow to prevent colorManager.setListener from being called
+            // every time this flow is collected, since colorManager is a singleton.
+            .shareIn(scope = scope, started = SharingStarted.WhileSubscribed(), replay = 1)
 
     override val selectedColorOption =
         combine(colorOptions, settingsChanged) { options, _ ->
@@ -132,7 +141,7 @@
                 }
                 return@combine null
             }
-            .stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initialValue = null)
+            .shareIn(scope = scope, started = SharingStarted.WhileSubscribed(), replay = 1)
 
     override suspend fun select(colorOption: ColorOption) {
         suspendCancellableCoroutine { continuation ->
@@ -155,6 +164,6 @@
     }
 
     companion object {
-        private const val TAG = "ColorPickerRepositoryImpl"
+        private const val TAG = "ColorPickerRepositoryImpl2"
     }
 }
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 88b7d39..2558d8f 100644
--- a/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
+++ b/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
@@ -129,15 +129,14 @@
                 )
             }
             val optionSelectedView = itemView.requireViewById<ImageView>(R.id.option_selected)
-            val colorView: View = itemView.requireViewById(R.id.option_tile)
-            colorView.isClickable = true
-            colorView.isFocusable = true
+            itemView.isClickable = true
+            itemView.isFocusable = true
 
             lifecycleOwner.lifecycleScope.launch {
                 launch {
                     item.isSelected.collect { isSelected ->
                         optionSelectedView.isVisible = isSelected
-                        colorView.isSelected = isSelected
+                        itemView.isSelected = isSelected
                     }
                 }
                 launch {
diff --git a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
index 480ae11..9dca30c 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
@@ -27,7 +27,6 @@
 import android.view.ViewTreeObserver.OnGlobalLayoutListener
 import android.widget.FrameLayout
 import android.widget.ImageView
-import android.widget.SeekBar
 import androidx.core.graphics.drawable.DrawableCompat
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
@@ -37,6 +36,7 @@
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import com.android.customization.picker.clock.shared.ClockSize
+import com.android.customization.picker.clock.shared.model.ClockMetadataModel
 import com.android.customization.picker.color.ui.binder.ColorOptionIconBinder2
 import com.android.customization.picker.color.ui.view.ColorOptionIconView2
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
@@ -57,7 +57,10 @@
 import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
 import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter2
 import com.google.android.material.materialswitch.MaterialSwitch
+import com.google.android.material.slider.LabelFormatter
+import com.google.android.material.slider.Slider
 import java.lang.ref.WeakReference
+import kotlin.math.roundToInt
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -157,20 +160,17 @@
                 layoutManager =
                     LinearLayoutManager(appContext, LinearLayoutManager.HORIZONTAL, false)
             }
-        val clockColorSlider: SeekBar = view.requireViewById(R.id.clock_color_slider)
-        clockColorSlider.setOnSeekBarChangeListener(
-            object : SeekBar.OnSeekBarChangeListener {
-                override fun onProgressChanged(p0: SeekBar?, progress: Int, fromUser: Boolean) {
-                    if (fromUser) {
-                        viewModel.onSliderProgressChanged(progress)
-                    }
+        val clockColorSlider: Slider = view.requireViewById(R.id.clock_color_slider)
+        clockColorSlider.apply {
+            valueFrom = ClockMetadataModel.MIN_COLOR_TONE_PROGRESS.toFloat()
+            valueTo = ClockMetadataModel.MAX_COLOR_TONE_PROGRESS.toFloat()
+            labelBehavior = LabelFormatter.LABEL_GONE
+            addOnChangeListener { _, value, fromUser ->
+                if (fromUser) {
+                    viewModel.onSliderProgressChanged(value.roundToInt())
                 }
-
-                override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit
-
-                override fun onStopTrackingTouch(seekBar: SeekBar?) = Unit
             }
-        )
+        }
 
         // Clock size switch
         val clockSizeSwitch =
@@ -312,7 +312,7 @@
 
                 launch {
                     viewModel.previewingSliderProgress.collect { progress ->
-                        clockColorSlider.setProgress(progress, true)
+                        clockColorSlider.value = progress.toFloat()
                     }
                 }
 
@@ -401,7 +401,7 @@
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
-                    viewModel.selectedClockFontAxes.filterNotNull().collect { fontAxes ->
+                    viewModel.previewingClockFontAxes.filterNotNull().collect { fontAxes ->
                         // This data flow updates only when a new clock style is selected. We
                         // initiate the clock font content with regard to that clock style.
                         sliderViewMap.clear()
diff --git a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
index cf29ce0..307c893 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
@@ -43,6 +43,7 @@
 import com.android.systemui.plugins.clocks.ClockPreviewConfig
 import com.android.systemui.shared.Flags
 import com.android.themepicker.R
+import com.android.wallpaper.config.BaseFlags
 import com.android.wallpaper.customization.ui.util.ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption
 import com.android.wallpaper.customization.ui.util.ThemePickerCustomizationOptionUtil.ThemePickerLockCustomizationOption
 import com.android.wallpaper.customization.ui.viewmodel.ThemePickerCustomizationOptionsViewModel
@@ -56,6 +57,7 @@
 import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
 import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationOptionsViewModel
 import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationPickerViewModel2
+import com.android.wallpaper.picker.data.WallpaperModel
 import com.google.android.material.materialswitch.MaterialSwitch
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -82,6 +84,7 @@
         navigateToMoreLockScreenSettingsActivity: () -> Unit,
         navigateToColorContrastSettingsActivity: () -> Unit,
         navigateToLockScreenNotificationsSettingsActivity: () -> Unit,
+        navigateToPreviewScreen: ((wallpaperModel: WallpaperModel) -> Unit)?,
     ) {
         defaultCustomizationOptionsBinder.bind(
             view,
@@ -95,8 +98,11 @@
             navigateToMoreLockScreenSettingsActivity,
             navigateToColorContrastSettingsActivity,
             navigateToLockScreenNotificationsSettingsActivity,
+            navigateToPreviewScreen,
         )
 
+        val isComposeRefactorEnabled = BaseFlags.get().isComposeRefactorEnabled()
+
         val optionsViewModel =
             viewModel.customizationOptionsViewModel as ThemePickerCustomizationOptionsViewModel
 
@@ -372,16 +378,18 @@
                 )
             }
 
-        customizationOptionFloatingSheetViewMap
-            ?.get(ThemePickerHomeCustomizationOption.COLORS)
-            ?.let {
-                ColorsFloatingSheetBinder.bind(
-                    it,
-                    optionsViewModel,
-                    colorUpdateViewModel,
-                    lifecycleOwner,
-                )
-            }
+        if (!isComposeRefactorEnabled) {
+            customizationOptionFloatingSheetViewMap
+                ?.get(ThemePickerHomeCustomizationOption.COLORS)
+                ?.let {
+                    ColorsFloatingSheetBinder.bind(
+                        it,
+                        optionsViewModel,
+                        colorUpdateViewModel,
+                        lifecycleOwner,
+                    )
+                }
+        }
 
         customizationOptionFloatingSheetViewMap
             ?.get(ThemePickerHomeCustomizationOption.APP_SHAPE_GRID)
diff --git a/src/com/android/wallpaper/customization/ui/compose/ColorFloatingSheet.kt b/src/com/android/wallpaper/customization/ui/compose/ColorFloatingSheet.kt
new file mode 100644
index 0000000..c81811d
--- /dev/null
+++ b/src/com/android/wallpaper/customization/ui/compose/ColorFloatingSheet.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * 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.android.wallpaper.customization.ui.compose
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.android.compose.theme.PlatformTheme
+import com.android.themepicker.R
+
+@Preview
+@Composable
+fun ColorFloatingSheet() {
+    // TODO (b/391927276): figure out how to animate color scheme changes
+    PlatformTheme {
+        val colorScheme = MaterialTheme.colorScheme
+        // TODO (b/391927276): replace placeholder colors with actual values
+        val colorsList = remember {
+            mutableStateListOf(
+                Color.Red,
+                Color.Green,
+                Color.Blue,
+                Color.Cyan,
+                Color.Magenta,
+                Color.Yellow,
+                Color.Black,
+            )
+        }
+        Box(
+            modifier =
+                Modifier.fillMaxWidth()
+                    .padding(horizontal = 16.dp)
+                    .clip(shape = RoundedCornerShape(28.dp))
+                    .drawBehind { drawRect(colorScheme.surfaceBright) }
+        ) {
+            Column(modifier = Modifier.padding(vertical = 20.dp)) {
+                Text(
+                    modifier = Modifier.padding(horizontal = 20.dp),
+                    text = stringResource(R.string.wallpaper_color_subheader),
+                    color = colorScheme.onSurface,
+                )
+
+                Spacer(modifier = Modifier.height(16.dp))
+
+                LazyRow(
+                    contentPadding = PaddingValues(horizontal = 20.dp),
+                    horizontalArrangement = Arrangement.spacedBy(4.dp),
+                ) {
+                    items(colorsList) { color ->
+                        Box(
+                            modifier =
+                                Modifier.size(
+                                    dimensionResource(R.dimen.floating_sheet_color_option_size)
+                                )
+                        ) {
+                            ColorOptionIcon(color)
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun ColorOptionIcon(color: Color) {
+    Box(modifier = Modifier.clip(CircleShape).fillMaxSize().drawBehind { drawRect(color) })
+}
diff --git a/src/com/android/wallpaper/customization/ui/util/ThemePickerCustomizationOptionUtil.kt b/src/com/android/wallpaper/customization/ui/util/ThemePickerCustomizationOptionUtil.kt
index ec38af5..922384a 100644
--- a/src/com/android/wallpaper/customization/ui/util/ThemePickerCustomizationOptionUtil.kt
+++ b/src/com/android/wallpaper/customization/ui/util/ThemePickerCustomizationOptionUtil.kt
@@ -16,26 +16,33 @@
 
 package com.android.wallpaper.customization.ui.util
 
+import android.content.Context
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
 import android.widget.LinearLayout
+import androidx.compose.ui.platform.ComposeView
 import com.android.customization.picker.mode.shared.util.DarkModeLifecycleUtil
 import com.android.themepicker.R
+import com.android.wallpaper.config.BaseFlags
+import com.android.wallpaper.customization.ui.compose.ColorFloatingSheet
 import com.android.wallpaper.model.Screen
 import com.android.wallpaper.model.Screen.HOME_SCREEN
 import com.android.wallpaper.model.Screen.LOCK_SCREEN
 import com.android.wallpaper.picker.customization.ui.util.CustomizationOptionUtil
 import com.android.wallpaper.picker.customization.ui.util.DefaultCustomizationOptionUtil
+import dagger.hilt.android.qualifiers.ActivityContext
 import dagger.hilt.android.scopes.ActivityScoped
 import javax.inject.Inject
 
 @ActivityScoped
 class ThemePickerCustomizationOptionUtil
 @Inject
-constructor(private val defaultCustomizationOptionUtil: DefaultCustomizationOptionUtil) :
-    CustomizationOptionUtil {
+constructor(
+    private val defaultCustomizationOptionUtil: DefaultCustomizationOptionUtil,
+    @ActivityContext private val context: Context,
+) : CustomizationOptionUtil {
 
     // Instantiate DarkModeLifecycleUtil for it to observe lifecycle and update DarkModeRepository
     @Inject lateinit var darkModeLifecycleUtil: DarkModeLifecycleUtil
@@ -143,6 +150,7 @@
     ): Map<CustomizationOptionUtil.CustomizationOption, View> {
         val map =
             defaultCustomizationOptionUtil.initFloatingSheet(bottomSheetContainer, layoutInflater)
+        val isComposeRefactorEnabled = BaseFlags.get().isComposeRefactorEnabled()
         return buildMap {
             putAll(map)
             put(
@@ -165,11 +173,15 @@
             )
             put(
                 ThemePickerHomeCustomizationOption.COLORS,
-                inflateFloatingSheet(
-                        ThemePickerHomeCustomizationOption.COLORS,
-                        bottomSheetContainer,
-                        layoutInflater,
-                    )
+                if (isComposeRefactorEnabled) {
+                        ComposeView(context).apply { setContent { ColorFloatingSheet() } }
+                    } else {
+                        inflateFloatingSheet(
+                            ThemePickerHomeCustomizationOption.COLORS,
+                            bottomSheetContainer,
+                            layoutInflater,
+                        )
+                    }
                     .also { bottomSheetContainer.addView(it) },
             )
             put(
diff --git a/src/com/android/wallpaper/customization/ui/view/ClockFontSliderViewHolder.kt b/src/com/android/wallpaper/customization/ui/view/ClockFontSliderViewHolder.kt
index 8bdf073..fb14342 100644
--- a/src/com/android/wallpaper/customization/ui/view/ClockFontSliderViewHolder.kt
+++ b/src/com/android/wallpaper/customization/ui/view/ClockFontSliderViewHolder.kt
@@ -16,12 +16,13 @@
 
 package com.android.wallpaper.customization.ui.view
 
-import android.widget.SeekBar
 import android.widget.TextView
 import androidx.core.view.isInvisible
 import com.android.systemui.plugins.clocks.ClockFontAxis
+import com.google.android.material.slider.LabelFormatter
+import com.google.android.material.slider.Slider
 
-class ClockFontSliderViewHolder(val name: TextView, val slider: SeekBar) {
+class ClockFontSliderViewHolder(val name: TextView, val slider: Slider) {
 
     fun setIsVisible(isVisible: Boolean) {
         name.isInvisible = !isVisible
@@ -31,30 +32,19 @@
     fun initView(clockFontAxis: ClockFontAxis, onFontAxisValueUpdated: (value: Float) -> Unit) {
         name.text = clockFontAxis.name
         slider.apply {
-            max = clockFontAxis.maxValue.toInt()
-            min = clockFontAxis.minValue.toInt()
-            progress = clockFontAxis.currentValue.toInt()
-            setOnSeekBarChangeListener(
-                object : SeekBar.OnSeekBarChangeListener {
-                    override fun onProgressChanged(
-                        seekBar: SeekBar?,
-                        progress: Int,
-                        fromUser: Boolean,
-                    ) {
-                        if (fromUser) {
-                            onFontAxisValueUpdated.invoke(progress.toFloat())
-                        }
-                    }
-
-                    override fun onStartTrackingTouch(seekBar: SeekBar?) {}
-
-                    override fun onStopTrackingTouch(seekBar: SeekBar?) {}
+            valueFrom = clockFontAxis.minValue
+            valueTo = clockFontAxis.maxValue
+            value = clockFontAxis.currentValue
+            labelBehavior = LabelFormatter.LABEL_GONE
+            addOnChangeListener { _, value, fromUser ->
+                if (fromUser) {
+                    onFontAxisValueUpdated.invoke(value)
                 }
-            )
+            }
         }
     }
 
     fun setValue(value: Float) {
-        slider.progress = value.toInt()
+        slider.value = value
     }
 }
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ClockOptionItemViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ClockOptionItemViewModel.kt
deleted file mode 100644
index cd223a0..0000000
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ClockOptionItemViewModel.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * 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.android.wallpaper.customization.ui.viewmodel
-
-import android.graphics.drawable.Drawable
-
-data class ClockOptionItemViewModel(
-    val clockId: String,
-    val isSelected: Boolean,
-    val contentDescription: String,
-    val thumbnail: Drawable,
-)
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
index f3be2eb..7fe81ed 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
@@ -187,14 +187,13 @@
 
     // Clock font axis
     private val overrideClockFontAxisMap = MutableStateFlow<Map<String, Float>>(emptyMap())
-    val selectedClockFontAxes =
+    val previewingClockFontAxes =
         previewingClock
             .map { clock -> clock.fontAxes }
-            .stateIn(viewModelScope, SharingStarted.Eagerly, null)
+            .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
     private val selectedClockFontAxisMap =
-        selectedClockFontAxes
-            .filterNotNull()
-            .map { fontAxes -> fontAxes.associate { it.key to it.currentValue } }
+        selectedClock
+            .map { clock -> clock.fontAxes.associate { it.key to it.currentValue } }
             .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
     private val isFontAxisMapEdited =
         combine(overrideClockFontAxisMap, selectedClockFontAxisMap) {
@@ -432,9 +431,10 @@
                 isClockColorEdited ||
                 isSliderProgressEdited
         }
-
+    private val onApplyClicked: MutableStateFlow<Boolean> = MutableStateFlow(false)
     val onApply: Flow<(suspend () -> Unit)?> =
         combine(
+            onApplyClicked,
             isEdited,
             previewingClock,
             previewingClockSize,
@@ -442,14 +442,16 @@
             previewingSliderProgress,
             previewingClockFontAxisMap,
         ) { array ->
-            val isEdited: Boolean = array[0] as Boolean
-            val clock: ClockMetadataModel = array[1] as ClockMetadataModel
-            val size: ClockSize = array[2] as ClockSize
-            val previewingColorId: String = array[3] as String
-            val previewProgress: Int = array[4] as Int
-            val axisMap: Map<String, Float> = array[5] as Map<String, Float>
-            if (isEdited) {
+            val onApplyClicked: Boolean = array[0] as Boolean
+            val isEdited: Boolean = array[1] as Boolean
+            val clock: ClockMetadataModel = array[2] as ClockMetadataModel
+            val size: ClockSize = array[3] as ClockSize
+            val previewingColorId: String = array[4] as String
+            val previewProgress: Int = array[5] as Int
+            val axisMap: Map<String, Float> = array[6] as Map<String, Float>
+            if (isEdited && !onApplyClicked) {
                 {
+                    this.onApplyClicked.value = true
                     clockPickerInteractor.applyClock(
                         clockId = clock.clockId,
                         size = size,
@@ -477,6 +479,7 @@
         overridingSliderProgress.value = null
         overrideClockFontAxisMap.value = emptyMap()
         _selectedTab.value = Tab.STYLE
+        onApplyClicked.value = false
     }
 
     companion object {
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ClockSizeOptionViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ClockSizeOptionViewModel.kt
deleted file mode 100644
index de2c54a..0000000
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ClockSizeOptionViewModel.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * 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.android.wallpaper.customization.ui.viewmodel
-
-import com.android.customization.picker.clock.shared.ClockSize
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
-
-data class ClockSizeOptionViewModel(
-    val size: ClockSize,
-    val isSelected: StateFlow<Boolean>,
-    val onClicked: Flow<(() -> Unit)?>,
-)