Merge "[TP] Make carousel scale and alpha dimen res" into tm-qpr-dev
diff --git a/res/layout/fragment_clock_settings.xml b/res/layout/fragment_clock_settings.xml
index 088ec2a..5208222 100644
--- a/res/layout/fragment_clock_settings.xml
+++ b/res/layout/fragment_clock_settings.xml
@@ -41,6 +41,11 @@
             android:layout_height="match_parent"
             android:layout_gravity="center" />
 
+        <FrameLayout
+            android:id="@+id/clock_host_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center" />
     </com.android.wallpaper.picker.DisplayAspectRatioFrameLayout>
 
 
diff --git a/res/values/clock_colors.xml b/res/values/clock_colors.xml
new file mode 100644
index 0000000..1539a92
--- /dev/null
+++ b/res/values/clock_colors.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2023 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.
+-->
+<resources>
+    <string-array name="clock_color_ids">
+        <item>RED</item>
+        <item>ORANGE</item>
+        <item>YELLOW</item>
+        <item>GREEN</item>
+        <item>BLUE</item>
+        <item>INDIGO</item>
+        <item>VIOLET</item>
+        <item>GRAY</item>
+        <item>TEAL</item>
+    </string-array>
+    <array name="clock_color_names">
+        <item>@string/clock_color_red</item>
+        <item>@string/clock_color_orange</item>
+        <item>@string/clock_color_yellow</item>
+        <item>@string/clock_color_green</item>
+        <item>@string/clock_color_blue</item>
+        <item>@string/clock_color_indigo</item>
+        <item>@string/clock_color_violet</item>
+        <item>@string/clock_color_gray</item>
+        <item>@string/clock_color_teal</item>
+    </array>
+    <array name="clock_colors">
+        <item>#FFA3A7</item>
+        <item>#F7AC69</item>
+        <item>#FFC951</item>
+        <item>#88D86A</item>
+        <item>#8EC8FF</item>
+        <item>#B9AAFF</item>
+        <item>#F6A2FF</item>
+        <item>#B9B9B9</item>
+        <item>#40D9CF</item>
+    </array>
+    <array name="clock_color_tone_min">
+        <item>20</item>
+        <item>20</item>
+        <item>50</item>
+        <item>20</item>
+        <item>20</item>
+        <item>10</item>
+        <item>20</item>
+        <item>0</item>
+        <item>20</item>
+    </array>
+    <array name="clock_color_tone_max">
+        <item>95</item>
+        <item>95</item>
+        <item>95</item>
+        <item>99</item>
+        <item>95</item>
+        <item>95</item>
+        <item>97</item>
+        <item>100</item>
+        <item>99</item>
+    </array>
+</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3bd9e84..18c0103 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -45,6 +45,33 @@
     <!-- Title of a tab to change the clock color. [CHAR LIMIT=15] -->
     <string name="clock_color">Color</string>
 
+    <!-- Title of a clock color red option that will appear in the description of the custom clock section. [CHAR LIMIT=15] -->
+    <string name="clock_color_red">Red</string>
+
+    <!-- Title of a clock color orange option that will appear in the description of the custom clock section. [CHAR LIMIT=15] -->
+    <string name="clock_color_orange">Orange</string>
+
+    <!-- Title of a clock color yellow option that will appear in the description of the custom clock section. [CHAR LIMIT=15] -->
+    <string name="clock_color_yellow">Yellow</string>
+
+    <!-- Title of a clock color green option that will appear in the description of the custom clock section. [CHAR LIMIT=15] -->
+    <string name="clock_color_green">Green</string>
+
+    <!-- Title of a clock color blue option that will appear in the description of the custom clock section. [CHAR LIMIT=15] -->
+    <string name="clock_color_blue">Blue</string>
+
+    <!-- Title of a clock color indigo option that will appear in the description of the custom clock section. [CHAR LIMIT=15] -->
+    <string name="clock_color_indigo">Indigo</string>
+
+    <!-- Title of a clock color violet option that will appear in the description of the custom clock section. [CHAR LIMIT=15] -->
+    <string name="clock_color_violet">Violet</string>
+
+    <!-- Title of a clock color grey option that will appear in the description of the custom clock section. [CHAR LIMIT=15] -->
+    <string name="clock_color_gray">Grey</string>
+
+    <!-- Title of a clock color teal option that will appear in the description of the custom clock section. [CHAR LIMIT=15] -->
+    <string name="clock_color_teal">Teal</string>
+
     <!-- Title of a tab to change the clock size. [CHAR LIMIT=15] -->
     <string name="clock_size">Size</string>
 
diff --git a/src/com/android/customization/model/grid/GridOptionsManager.java b/src/com/android/customization/model/grid/GridOptionsManager.java
index bff7933..b7ee37f 100644
--- a/src/com/android/customization/model/grid/GridOptionsManager.java
+++ b/src/com/android/customization/model/grid/GridOptionsManager.java
@@ -49,6 +49,7 @@
 
     private final LauncherGridOptionsProvider mProvider;
     private final ThemesUserEventLogger mEventLogger;
+    private int mGridOptionSize = -1;
 
     /** Returns the {@link GridOptionsManager} instance. */
     public static GridOptionsManager getInstance(Context context) {
@@ -73,16 +74,17 @@
 
     @Override
     public boolean isAvailable() {
-        int gridOptionSize = 0;
-        try {
-            gridOptionSize = sExecutorService.submit(() -> {
-                List<GridOption> gridOptions = mProvider.fetch(/* reload= */true);
-                return gridOptions == null ? 0 : gridOptions.size();
-            }).get();
-        } catch (InterruptedException | ExecutionException e) {
-            Log.w(TAG, "could not get gridOptionSize", e);
+        if (mGridOptionSize < 0) {
+            try {
+                mGridOptionSize = sExecutorService.submit(() -> {
+                    List<GridOption> gridOptions = mProvider.fetch(/* reload= */true);
+                    return gridOptions == null ? 0 : gridOptions.size();
+                }).get();
+            } catch (InterruptedException | ExecutionException e) {
+                Log.w(TAG, "could not get gridOptionSize", e);
+            }
         }
-        return gridOptionSize > 1 && mProvider.areGridsAvailable();
+        return mGridOptionSize > 1 && mProvider.areGridsAvailable();
     }
 
     @Override
diff --git a/src/com/android/customization/model/grid/GridSectionController.java b/src/com/android/customization/model/grid/GridSectionController.java
index 3e5dba0..c50bfcc 100644
--- a/src/com/android/customization/model/grid/GridSectionController.java
+++ b/src/com/android/customization/model/grid/GridSectionController.java
@@ -96,7 +96,7 @@
 
     @Override
     public void release() {
-        if (mIsRevampedUiEnabled) {
+        if (mIsRevampedUiEnabled && mGridOptionsManager.isAvailable()) {
             mGridOptionsManager.getOptionChangeObservable(/* handler= */ null).removeObserver(
                     mOptionChangeObserver
             );
diff --git a/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java b/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java
index 5ae283a..4e775c6 100644
--- a/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java
+++ b/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java
@@ -126,6 +126,7 @@
 
     /**
      * Returns an observable that receives a new value each time that the grid options are changed.
+     * Do not call if {@link #areGridsAvailable()} returns false
      */
     public LiveData<Object> getOptionChangeObservable(
             @Nullable Handler handler) {
diff --git a/src/com/android/customization/model/grid/data/repository/GridRepository.kt b/src/com/android/customization/model/grid/data/repository/GridRepository.kt
index 7c84aec..9a3be0c 100644
--- a/src/com/android/customization/model/grid/data/repository/GridRepository.kt
+++ b/src/com/android/customization/model/grid/data/repository/GridRepository.kt
@@ -35,7 +35,8 @@
 import kotlinx.coroutines.withContext
 
 interface GridRepository {
-    val optionChanges: Flow<Unit>
+    suspend fun isAvailable(): Boolean
+    fun getOptionChanges(): Flow<Unit>
     suspend fun getOptions(): GridOptionItemsModel
 }
 
@@ -45,7 +46,11 @@
     private val backgroundDispatcher: CoroutineDispatcher,
 ) : GridRepository {
 
-    override val optionChanges: Flow<Unit> =
+    override suspend fun isAvailable(): Boolean {
+        return withContext(backgroundDispatcher) { manager.isAvailable }
+    }
+
+    override fun getOptionChanges(): Flow<Unit> =
         manager.getOptionChangeObservable(/* handler= */ null).asFlow().map {}
 
     private val selectedOption = MutableStateFlow<GridOption?>(null)
diff --git a/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt b/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt
index 5ab9e1f..cdb679d 100644
--- a/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt
+++ b/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt
@@ -24,6 +24,9 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.shareIn
@@ -34,22 +37,30 @@
     private val snapshotRestorer: Provider<GridSnapshotRestorer>,
 ) {
     val options: Flow<GridOptionItemsModel> =
-        // this upstream flow tells us each time the options are changed.
-        repository.optionChanges
-            // when we start, we pretend the options _just_ changed. This way, we load something as
-            // soon as possible into the flow so it's ready by the time the first observer starts to
-            // observe.
-            .onStart { emit(Unit) }
-            // each time the options changed, we load them.
-            .map { reload() }
-            // we place the loaded options in a SharedFlow so downstream observers all
-            // share the same flow and don't trigger a new one each time they want to start
-            // observing.
-            .shareIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                replay = 1,
-            )
+        flow { emit(repository.isAvailable()) }
+            .flatMapLatest { isAvailable ->
+                if (isAvailable) {
+                    // this upstream flow tells us each time the options are changed.
+                    repository
+                        .getOptionChanges()
+                        // when we start, we pretend the options _just_ changed. This way, we load
+                        // something as soon as possible into the flow so it's ready by the time the
+                        // first observer starts to observe.
+                        .onStart { emit(Unit) }
+                        // each time the options changed, we load them.
+                        .map { reload() }
+                        // we place the loaded options in a SharedFlow so downstream observers all
+                        // share the same flow and don't trigger a new one each time they want to
+                        // start observing.
+                        .shareIn(
+                            scope = applicationScope,
+                            started = SharingStarted.WhileSubscribed(),
+                            replay = 1,
+                        )
+                } else {
+                    emptyFlow()
+                }
+            }
 
     suspend fun setSelectedOption(model: GridOptionItemModel) {
         model.onSelected.invoke()
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt
index 66793cd..ae66ce3 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt
@@ -16,6 +16,8 @@
  */
 package com.android.customization.picker.clock.data.repository
 
+import androidx.annotation.ColorInt
+import androidx.annotation.IntRange
 import com.android.customization.picker.clock.shared.ClockSize
 import com.android.customization.picker.clock.shared.model.ClockMetadataModel
 import kotlinx.coroutines.flow.Flow
@@ -33,7 +35,18 @@
 
     fun setSelectedClock(clockId: String)
 
-    fun setClockColor(color: Int?)
+    /**
+     * Set clock color to the settings.
+     *
+     * @param selectedColor selected color in the color option list.
+     * @param colorToneProgress color tone from 0 to 100 to apply to the selected color
+     * @param seedColor the actual clock color after blending the selected color and color tone
+     */
+    fun setClockColor(
+        selectedColorId: String?,
+        @IntRange(from = 0, to = 100) colorToneProgress: Int,
+        @ColorInt seedColor: Int?,
+    )
 
     suspend fun setClockSize(size: ClockSize)
 }
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 a360b6c..880a00b 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
@@ -17,6 +17,8 @@
 package com.android.customization.picker.clock.data.repository
 
 import android.provider.Settings
+import androidx.annotation.ColorInt
+import androidx.annotation.IntRange
 import com.android.customization.picker.clock.shared.ClockSize
 import com.android.customization.picker.clock.shared.model.ClockMetadataModel
 import com.android.systemui.plugins.ClockMetadata
@@ -34,6 +36,7 @@
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.shareIn
+import org.json.JSONObject
 
 /** Implementation of [ClockPickerRepository], using [ClockRegistry]. */
 class ClockPickerRepositoryImpl(
@@ -50,7 +53,7 @@
                         registry
                             .getClocks()
                             .filter { "NOT_IN_USE" !in it.clockId }
-                            .map { it.toModel(null) }
+                            .map { it.toModel() }
                     trySend(allClocks)
                 }
 
@@ -72,18 +75,22 @@
                 allClocks
             }
 
-    /** The currently-selected clock. */
+    /** The currently-selected clock. This also emits the clock color information. */
     override val selectedClock: Flow<ClockMetadataModel> =
         callbackFlow {
                 fun send() {
                     val currentClockId = registry.currentClockId
-                    // It is possible that the model can be null since the full clock list is not
-                    // initiated.
+                    val metadata = registry.settings?.metadata
                     val model =
                         registry
                             .getClocks()
                             .find { clockMetadata -> clockMetadata.clockId == currentClockId }
-                            ?.toModel(registry.seedColor)
+                            ?.toModel(
+                                selectedColorId = metadata?.getSelectedColorId(),
+                                colorTone = metadata?.getColorTone()
+                                        ?: ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS,
+                                seedColor = registry.seedColor
+                            )
                     trySend(model)
                 }
 
@@ -104,11 +111,26 @@
             .mapNotNull { it }
 
     override fun setSelectedClock(clockId: String) {
-        registry.currentClockId = clockId
+        registry.mutateSetting { oldSettings ->
+            val newSettings = oldSettings.copy(clockId = clockId)
+            newSettings.metadata = oldSettings.metadata
+            newSettings
+        }
     }
 
-    override fun setClockColor(color: Int?) {
-        registry.seedColor = color
+    override fun setClockColor(
+        selectedColorId: String?,
+        @IntRange(from = 0, to = 100) colorToneProgress: Int,
+        @ColorInt seedColor: Int?,
+    ) {
+        registry.mutateSetting { oldSettings ->
+            val newSettings = oldSettings.copy(seedColor = seedColor)
+            newSettings.metadata =
+                oldSettings.metadata
+                    .put(KEY_METADATA_SELECTED_COLOR_ID, selectedColorId)
+                    .put(KEY_METADATA_COLOR_TONE_PROGRESS, colorToneProgress)
+            newSettings
+        }
     }
 
     override val selectedClockSize: SharedFlow<ClockSize> =
@@ -131,7 +153,41 @@
         )
     }
 
-    private fun ClockMetadata.toModel(color: Int?): ClockMetadataModel {
-        return ClockMetadataModel(clockId = clockId, name = name, color = color)
+    private fun JSONObject.getSelectedColorId(): String? {
+        return if (this.isNull(KEY_METADATA_SELECTED_COLOR_ID)) {
+            null
+        } else {
+            this.getString(KEY_METADATA_SELECTED_COLOR_ID)
+        }
+    }
+
+    private fun JSONObject.getColorTone(): Int {
+        return this.optInt(
+            KEY_METADATA_COLOR_TONE_PROGRESS,
+            ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS
+        )
+    }
+
+    /** By default, [ClockMetadataModel] has no color information unless specified. */
+    private fun ClockMetadata.toModel(
+        selectedColorId: String? = null,
+        @IntRange(from = 0, to = 100) colorTone: Int = 0,
+        @ColorInt seedColor: Int? = null,
+    ): ClockMetadataModel {
+        return ClockMetadataModel(
+            clockId = clockId,
+            name = name,
+            selectedColorId = selectedColorId,
+            colorToneProgress = colorTone,
+            seedColor = seedColor,
+        )
+    }
+
+    companion object {
+        // The selected color in the color option list
+        private const val KEY_METADATA_SELECTED_COLOR_ID = "metadataSelectedColorId"
+
+        // The color tone to apply to the selected color
+        private const val KEY_METADATA_COLOR_TONE_PROGRESS = "metadataColorToneProgress"
     }
 }
diff --git a/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt b/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt
index 677fcfa..6f3657a 100644
--- a/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt
+++ b/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt
@@ -17,10 +17,13 @@
 
 package com.android.customization.picker.clock.domain.interactor
 
+import androidx.annotation.ColorInt
+import androidx.annotation.IntRange
 import com.android.customization.picker.clock.data.repository.ClockPickerRepository
 import com.android.customization.picker.clock.shared.ClockSize
 import com.android.customization.picker.clock.shared.model.ClockMetadataModel
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 
 /**
@@ -31,9 +34,16 @@
 
     val allClocks: Flow<List<ClockMetadataModel>> = repository.allClocks
 
-    val selectedClock: Flow<ClockMetadataModel> = repository.selectedClock
+    val selectedClockId: Flow<String> =
+        repository.selectedClock.map { clock -> clock.clockId }.distinctUntilChanged()
 
-    val selectedClockColor: Flow<Int?> = repository.selectedClock.map { clock -> clock.color }
+    val selectedColorId: Flow<String?> =
+        repository.selectedClock.map { clock -> clock.selectedColorId }.distinctUntilChanged()
+
+    val colorToneProgress: Flow<Int> =
+        repository.selectedClock.map { clock -> clock.colorToneProgress }
+
+    val seedColor: Flow<Int?> = repository.selectedClock.map { clock -> clock.seedColor }
 
     val selectedClockSize: Flow<ClockSize> = repository.selectedClockSize
 
@@ -41,8 +51,12 @@
         repository.setSelectedClock(clockId)
     }
 
-    fun setClockColor(color: Int?) {
-        repository.setClockColor(color)
+    fun setClockColor(
+        selectedColorId: String?,
+        @IntRange(from = 0, to = 100) colorToneProgress: Int,
+        @ColorInt seedColor: Int?,
+    ) {
+        repository.setClockColor(selectedColorId, colorToneProgress, seedColor)
     }
 
     suspend fun setClockSize(size: ClockSize) {
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 acbc792..bd87ba6 100644
--- a/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
+++ b/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
@@ -17,9 +17,18 @@
 
 package com.android.customization.picker.clock.shared.model
 
+import androidx.annotation.ColorInt
+import androidx.annotation.IntRange
+
 /** Model for clock metadata. */
 data class ClockMetadataModel(
     val clockId: String,
     val name: String,
-    val color: Int?,
-)
+    val selectedColorId: String?,
+    @IntRange(from = 0, to = 100) val colorToneProgress: Int,
+    @ColorInt val seedColor: Int?,
+) {
+    companion object {
+        const val DEFAULT_COLOR_TONE_PROGRESS = 75
+    }
+}
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
index 6c72a5b..9ad735d 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -15,7 +15,6 @@
  */
 package com.android.customization.picker.clock.ui.binder
 
-import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
 import androidx.core.view.isVisible
@@ -24,6 +23,7 @@
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.customization.picker.clock.ui.view.ClockCarouselView
+import com.android.customization.picker.clock.ui.view.ClockViewFactory
 import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
 import com.android.wallpaper.R
 import kotlinx.coroutines.launch
@@ -43,7 +43,7 @@
         carouselView: ClockCarouselView,
         singleClockView: ViewGroup,
         viewModel: ClockCarouselViewModel,
-        clockViewFactory: (clockId: String) -> View,
+        clockViewFactory: ClockViewFactory,
         lifecycleOwner: LifecycleOwner,
     ): Binding {
         val singleClockHostView =
@@ -56,7 +56,7 @@
                     viewModel.allClockIds.collect { allClockIds ->
                         carouselView.setUpClockCarouselView(
                             clockIds = allClockIds,
-                            onGetClockPreview = clockViewFactory,
+                            onGetClockPreview = { clockId -> clockViewFactory.getView(clockId) },
                             onClockSelected = { clockId -> viewModel.setSelectedClock(clockId) },
                         )
                     }
@@ -69,13 +69,17 @@
                 }
 
                 launch {
+                    viewModel.seedColor.collect { clockViewFactory.updateColorForAllClocks(it) }
+                }
+
+                launch {
                     viewModel.isSingleClockViewVisible.collect { singleClockView.isVisible = it }
                 }
 
                 launch {
                     viewModel.clockId.collect { clockId ->
                         singleClockHostView.removeAllViews()
-                        val clockView = clockViewFactory(clockId)
+                        val clockView = clockViewFactory.getView(clockId)
                         // The clock view might still be attached to an existing parent. Detach
                         // before adding to another parent.
                         (clockView.parent as? ViewGroup)?.removeView(clockView)
@@ -84,6 +88,15 @@
                 }
             }
         }
+
+        lifecycleOwner.lifecycleScope.launch {
+            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+                clockViewFactory.registerTimeTicker()
+            }
+            // When paused
+            clockViewFactory.unregisterTimeTicker()
+        }
+
         return object : Binding {
             override fun show() {
                 viewModel.showClockCarousel(true)
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 7ea0210..66d9251 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
@@ -16,6 +16,8 @@
 package com.android.customization.picker.clock.ui.binder
 
 import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
 import android.widget.SeekBar
 import androidx.core.view.isInvisible
 import androidx.core.view.isVisible
@@ -28,10 +30,12 @@
 import com.android.customization.picker.clock.shared.ClockSize
 import com.android.customization.picker.clock.ui.adapter.ClockSettingsTabAdapter
 import com.android.customization.picker.clock.ui.view.ClockSizeRadioButtonGroup
+import com.android.customization.picker.clock.ui.view.ClockViewFactory
 import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel
 import com.android.customization.picker.color.ui.adapter.ColorOptionAdapter
 import com.android.customization.picker.common.ui.view.ItemSpacing
 import com.android.wallpaper.R
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.launch
 
 /** Bind between the clock settings screen and its view model. */
@@ -39,8 +43,11 @@
     fun bind(
         view: View,
         viewModel: ClockSettingsViewModel,
+        clockViewFactory: ClockViewFactory,
         lifecycleOwner: LifecycleOwner,
     ) {
+        val clockHostView: FrameLayout = view.requireViewById(R.id.clock_host_view)
+
         val tabView: RecyclerView = view.requireViewById(R.id.tabs)
         val tabAdapter = ClockSettingsTabAdapter()
         tabView.adapter = tabAdapter
@@ -63,8 +70,10 @@
                     }
                 }
 
-                override fun onStartTrackingTouch(p0: SeekBar?) = Unit
-                override fun onStopTrackingTouch(p0: SeekBar?) = Unit
+                override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit
+                override fun onStopTrackingTouch(seekBar: SeekBar?) {
+                    seekBar?.progress?.let { viewModel.onSliderProgressStop(it) }
+                }
             }
         )
 
@@ -80,6 +89,25 @@
         val colorOptionContainer = view.requireViewById<View>(R.id.color_picker_container)
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    viewModel.selectedClockId
+                        .mapNotNull { it }
+                        .collect { clockId ->
+                            val clockView = clockViewFactory.getView(clockId)
+                            (clockView.parent as? ViewGroup)?.removeView(clockView)
+                            clockHostView.removeAllViews()
+                            clockHostView.addView(clockView)
+                        }
+                }
+
+                launch {
+                    viewModel.seedColor.collect { seedColor ->
+                        viewModel.selectedClockId.value?.let { selectedClockId ->
+                            clockViewFactory.updateColor(selectedClockId, seedColor)
+                        }
+                    }
+                }
+
                 launch { viewModel.tabs.collect { tabAdapter.setItems(it) } }
 
                 launch {
@@ -104,6 +132,18 @@
                 }
 
                 launch {
+                    viewModel.selectedColorOptionPosition.collect { selectedPosition ->
+                        if (selectedPosition != -1) {
+                            // We use "post" because we need to give the adapter item a pass to
+                            // update the view.
+                            colorOptionContainerView.post {
+                                colorOptionContainerView.smoothScrollToPosition(selectedPosition)
+                            }
+                        }
+                    }
+                }
+
+                launch {
                     viewModel.selectedClockSize.collect { size ->
                         when (size) {
                             ClockSize.DYNAMIC -> {
@@ -120,16 +160,22 @@
 
                 launch {
                     viewModel.sliderProgress.collect { progress ->
-                        progress?.let { slider.setProgress(progress, false) }
+                        slider.setProgress(progress, true)
                     }
                 }
 
                 launch {
-                    viewModel.isSliderEnabled.collect { isEnabled ->
-                        slider.isInvisible = !isEnabled
-                    }
+                    viewModel.isSliderEnabled.collect { isEnabled -> slider.isEnabled = isEnabled }
                 }
             }
         }
+
+        lifecycleOwner.lifecycleScope.launch {
+            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+                clockViewFactory.registerTimeTicker()
+            }
+            // When paused
+            clockViewFactory.unregisterTimeTicker()
+        }
     }
 }
diff --git a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
index 9b6d737..ef1a5ef 100644
--- a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
+++ b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
@@ -24,6 +24,7 @@
 import androidx.lifecycle.get
 import com.android.customization.module.ThemePickerInjector
 import com.android.customization.picker.clock.ui.binder.ClockSettingsBinder
+import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
 import com.android.wallpaper.R
 import com.android.wallpaper.module.InjectorProvider
 import com.android.wallpaper.picker.AppbarFragment
@@ -95,6 +96,16 @@
                         onWallpaperColorChanged = { colors ->
                             colorViewModel.setLockWallpaperColors(colors)
                         },
+                        initialExtrasProvider = {
+                            Bundle().apply {
+                                // Hide the clock from the system UI rendered preview so we can
+                                // place the carousel on top of it.
+                                putBoolean(
+                                    ClockPreviewConstants.KEY_HIDE_CLOCK,
+                                    true,
+                                )
+                            }
+                        },
                     ),
                 lifecycleOwner = this,
                 offsetToStart = displayUtils.isOnWallpaperDisplay(activity),
@@ -111,6 +122,7 @@
                     ),
                 )
                 .get(),
+            injector.getClockViewFactory(activity),
             this@ClockSettingsFragment,
         )
 
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
index 488dd08..7f480de 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
@@ -17,21 +17,45 @@
 
 import android.app.Activity
 import android.view.View
+import androidx.annotation.ColorInt
 import com.android.systemui.plugins.ClockController
 import com.android.systemui.shared.clocks.ClockRegistry
 import com.android.wallpaper.R
 import com.android.wallpaper.util.ScreenSizeCalculator
+import com.android.wallpaper.util.TimeUtils.TimeTicker
 
 class ClockViewFactory(
     private val activity: Activity,
     private val registry: ClockRegistry,
 ) {
     private val clockControllers: HashMap<String, ClockController> = HashMap()
+    private var ticker: TimeTicker? = null
 
     fun getView(clockId: String): View {
         return (clockControllers[clockId] ?: initClockController(clockId)).largeClock.view
     }
 
+    fun updateColorForAllClocks(@ColorInt seedColor: Int?) {
+        clockControllers.values.forEach { it.events.onSeedColorChanged(seedColor = seedColor) }
+    }
+
+    fun updateColor(clockId: String, @ColorInt seedColor: Int?) {
+        return (clockControllers[clockId] ?: initClockController(clockId))
+            .events
+            .onSeedColorChanged(seedColor)
+    }
+
+    fun registerTimeTicker() {
+        ticker =
+            TimeTicker.registerNewReceiver(activity.applicationContext) {
+                clockControllers.values.forEach { it.largeClock.events.onTimeTick() }
+            }
+    }
+
+    fun unregisterTimeTicker() {
+        activity.applicationContext.unregisterReceiver(ticker)
+    }
+
     private fun initClockController(clockId: String): ClockController {
         val controller =
             registry.createExampleClock(clockId).also { it?.initialize(activity.resources, 0f, 0f) }
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
index 8d614e4..60a9e85 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
@@ -42,6 +42,8 @@
             allClocks.map { it.clockId }
         }
 
+    val seedColor: Flow<Int?> = interactor.seedColor
+
     private val shouldShowCarousel = MutableStateFlow(false)
     val isCarouselVisible: Flow<Boolean> =
         combine(allClockIds.map { it.size > 1 }.distinctUntilChanged(), shouldShowCarousel) {
@@ -55,8 +57,8 @@
     val selectedIndex: Flow<Int> =
         allClockIds
             .flatMapLatest { allClockIds ->
-                interactor.selectedClock.map { selectedClock ->
-                    val index = allClockIds.indexOf(selectedClock.clockId)
+                interactor.selectedClockId.map { selectedClockId ->
+                    val index = allClockIds.indexOf(selectedClockId)
                     if (index >= 0) {
                         index
                     } else {
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockColorViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockColorViewModel.kt
new file mode 100644
index 0000000..65ebc79
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockColorViewModel.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 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.customization.picker.clock.ui.viewmodel
+
+import android.annotation.ColorInt
+import android.content.res.Resources
+import android.graphics.Color
+import androidx.core.content.res.getStringOrThrow
+import com.android.wallpaper.R
+
+/** The view model that defines custom clock colors. */
+data class ClockColorViewModel(
+    val colorId: String,
+    val colorName: String?,
+    @ColorInt val color: Int,
+    private val colorToneMin: Double,
+    private val colorToneMax: Double,
+) {
+
+    fun getColorTone(progress: Int): Double {
+        return colorToneMin + (progress.toDouble() * (colorToneMax - colorToneMin)) / 100
+    }
+
+    companion object {
+        const val DEFAULT_COLOR_TONE_MIN = 0
+        const val DEFAULT_COLOR_TONE_MAX = 100
+
+        fun getPresetColorMap(resources: Resources): Map<String, ClockColorViewModel> {
+            val ids = resources.obtainTypedArray(R.array.clock_color_ids)
+            val names = resources.obtainTypedArray(R.array.clock_color_names)
+            val colors = resources.obtainTypedArray(R.array.clock_colors)
+            val colorToneMinList = resources.obtainTypedArray(R.array.clock_color_tone_min)
+            val colorToneMaxList = resources.obtainTypedArray(R.array.clock_color_tone_max)
+            return buildList {
+                    for (i in 0 until ids.length()) {
+                        add(
+                            ClockColorViewModel(
+                                ids.getStringOrThrow(i),
+                                names.getString(i),
+                                colors.getColor(i, Color.TRANSPARENT),
+                                colorToneMinList.getInt(i, DEFAULT_COLOR_TONE_MIN).toDouble(),
+                                colorToneMaxList.getInt(i, DEFAULT_COLOR_TONE_MAX).toDouble(),
+                            )
+                        )
+                    }
+                }
+                .associateBy { it.colorId }
+                .also {
+                    ids.recycle()
+                    names.recycle()
+                    colors.recycle()
+                    colorToneMinList.recycle()
+                    colorToneMaxList.recycle()
+                }
+        }
+    }
+}
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt
index 9239c0a..008a125 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt
@@ -22,15 +22,20 @@
 import com.android.wallpaper.R
 import java.util.Locale
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 
 /** View model for the clock section view on the lockscreen customization surface. */
 class ClockSectionViewModel(context: Context, interactor: ClockPickerInteractor) {
     val appContext: Context = context.applicationContext
+    val clockColorMap: Map<String, ClockColorViewModel> =
+        ClockColorViewModel.getPresetColorMap(appContext.resources)
     val selectedClockColorAndSizeText: Flow<String> =
-        interactor.selectedClockSize.map { selectedClockSize ->
-            // TODO (b/241966062) Finalize the colors and their names
-            val colorText = "Violet"
+        combine(interactor.selectedColorId, interactor.selectedClockSize, ::Pair).map {
+            (selectedColorId, selectedClockSize) ->
+            val colorText =
+                clockColorMap[selectedColorId]?.colorName
+                    ?: context.getString(R.string.default_theme_title)
             val sizeText =
                 when (selectedClockSize) {
                     ClockSize.SMALL -> appContext.getString(R.string.clock_size_small)
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 b36c8eb..23fbc7e 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
@@ -24,12 +24,11 @@
 import com.android.customization.model.color.ColorSeedOption
 import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
 import com.android.customization.picker.clock.shared.ClockSize
+import com.android.customization.picker.clock.shared.model.ClockMetadataModel
 import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
 import com.android.customization.picker.color.shared.model.ColorType
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionViewModel
 import com.android.wallpaper.R
-import kotlin.math.abs
-import kotlin.math.roundToInt
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
@@ -38,9 +37,10 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 
@@ -57,64 +57,60 @@
         SIZE,
     }
 
-    private val helperColorHsl: FloatArray by lazy { FloatArray(3) }
+    val colorMap = ClockColorViewModel.getPresetColorMap(context.resources)
+
+    val selectedClockId: StateFlow<String?> =
+        clockPickerInteractor.selectedClockId
+            .distinctUntilChanged()
+            .stateIn(viewModelScope, SharingStarted.Eagerly, null)
+
+    val selectedColorId: StateFlow<String?> =
+        clockPickerInteractor.selectedColorId.stateIn(viewModelScope, SharingStarted.Eagerly, null)
+
+    private val sliderColorToneProgress =
+        MutableStateFlow(ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS)
+    val isSliderEnabled: Flow<Boolean> =
+        clockPickerInteractor.selectedColorId.map { it != null }.distinctUntilChanged()
+    val sliderProgress: Flow<Int> =
+        merge(clockPickerInteractor.colorToneProgress, sliderColorToneProgress)
+
+    private val _seedColor: MutableStateFlow<Int?> = MutableStateFlow(null)
+    val seedColor: Flow<Int?> = merge(clockPickerInteractor.seedColor, _seedColor)
 
     /**
-     * Saturation level of the current selected color. Note that this can be null if the selected
-     * color is null, which means that the clock color respects the system theme color. In this
-     * case, the saturation level is no longer needed since we do not allow changing saturation
-     * level of the system theme color.
+     * The slider color tone updates are quick. Do not set color tone and the blended color to the
+     * settings until [onSliderProgressStop] is called. Update to a locally cached temporary
+     * [sliderColorToneProgress] and [_seedColor] instead.
      */
-    private val saturationLevel: Flow<Float?> =
-        clockPickerInteractor.selectedClockColor
-            .map { selectedColor ->
-                if (selectedColor == null) {
-                    null
-                } else {
-                    ColorUtils.colorToHSL(selectedColor, helperColorHsl)
-                    helperColorHsl[1]
-                }
-            }
-            .shareIn(
-                scope = viewModelScope,
-                started = SharingStarted.WhileSubscribed(),
-                replay = 1,
-            )
-
-    /**
-     * When the selected clock color is null, it means that the clock will respect the system theme
-     * color. And we no longer need the slider, which determines the saturation level of the clock's
-     * overridden color.
-     */
-    val isSliderEnabled: Flow<Boolean> = saturationLevel.map { it != null }
-
-    /**
-     * Slide progress from 0 to 100. Note that this can be null if the selected color is null, which
-     * means that the clock color respects the system theme color. In this case, the saturation
-     * level is no longer needed since we do not allow changing saturation level of the system theme
-     * color.
-     */
-    val sliderProgress: Flow<Int?> =
-        saturationLevel.map { saturation -> saturation?.let { (it * 100).roundToInt() } }
-
     fun onSliderProgressChanged(progress: Int) {
-        val saturation = progress / 100f
-        val selectedOption = colorOptions.value.find { option -> option.isSelected }
-        selectedOption?.let { option ->
-            ColorUtils.colorToHSL(option.color0, helperColorHsl)
-            helperColorHsl[1] = saturation
-            clockPickerInteractor.setClockColor(ColorUtils.HSLToColor(helperColorHsl))
-        }
+        sliderColorToneProgress.value = progress
+        val selectedColorId = selectedColorId.value ?: return
+        val clockColorViewModel = colorMap[selectedColorId] ?: return
+        _seedColor.value =
+            blendColorWithTone(
+                color = clockColorViewModel.color,
+                colorTone = clockColorViewModel.getColorTone(progress),
+            )
+    }
+
+    fun onSliderProgressStop(progress: Int) {
+        val selectedColorId = selectedColorId.value ?: return
+        val clockColorViewModel = colorMap[selectedColorId] ?: return
+        clockPickerInteractor.setClockColor(
+            selectedColorId = selectedColorId,
+            colorToneProgress = progress,
+            seedColor =
+                blendColorWithTone(
+                    color = clockColorViewModel.color,
+                    colorTone = clockColorViewModel.getColorTone(progress),
+                )
+        )
     }
 
     @OptIn(ExperimentalCoroutinesApi::class)
     val colorOptions: StateFlow<List<ColorOptionViewModel>> =
-        combine(
-                colorPickerInteractor.colorOptions,
-                clockPickerInteractor.selectedClockColor,
-                ::Pair,
-            )
-            .mapLatest { (colorOptions, selectedColor) ->
+        combine(colorPickerInteractor.colorOptions, clockPickerInteractor.selectedColorId, ::Pair)
+            .mapLatest { (colorOptions, selectedColorId) ->
                 // Use mapLatest and delay(100) here to prevent too many selectedClockColor update
                 // events from ClockRegistry upstream, caused by sliding the saturation level bar.
                 delay(COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
@@ -125,55 +121,30 @@
                                 ?.colorOption as? ColorSeedOption)
                             ?.toColorOptionViewModel(
                                 context,
-                                selectedColor,
+                                selectedColorId,
                             )
                             ?: (colorOptions[ColorType.BASIC_COLOR]
                                     ?.find { it.isSelected }
                                     ?.colorOption as? ColorBundle)
                                 ?.toColorOptionViewModel(
                                     context,
-                                    selectedColor,
+                                    selectedColorId,
                                 )
                     if (defaultThemeColorOptionViewModel != null) {
                         add(defaultThemeColorOptionViewModel)
                     }
 
-                    if (selectedColor != null) {
-                        ColorUtils.colorToHSL(selectedColor, helperColorHsl)
-                    }
+                    val selectedColorPosition = colorMap.keys.indexOf(selectedColorId)
 
-                    val selectedColorPosition =
-                        if (selectedColor != null) {
-                            getSelectedColorPosition(helperColorHsl)
-                        } else {
-                            -1
-                        }
-
-                    COLOR_LIST_HSL.forEachIndexed { index, colorHSL ->
-                        val color = ColorUtils.HSLToColor(colorHSL)
+                    colorMap.values.forEachIndexed { index, colorModel ->
                         val isSelected = selectedColorPosition == index
-                        val colorToSet: Int by lazy {
-                            val saturation =
-                                if (selectedColor != null) {
-                                    helperColorHsl[1]
-                                } else {
-                                    colorHSL[1]
-                                }
-                            ColorUtils.HSLToColor(
-                                listOf(
-                                        colorHSL[0],
-                                        saturation,
-                                        colorHSL[2],
-                                    )
-                                    .toFloatArray()
-                            )
-                        }
+                        val colorToneProgress = ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS
                         add(
                             ColorOptionViewModel(
-                                color0 = color,
-                                color1 = color,
-                                color2 = color,
-                                color3 = color,
+                                color0 = colorModel.color,
+                                color1 = colorModel.color,
+                                color2 = colorModel.color,
+                                color3 = colorModel.color,
                                 contentDescription =
                                     context.getString(
                                         R.string.content_description_color_option,
@@ -184,7 +155,20 @@
                                     if (isSelected) {
                                         null
                                     } else {
-                                        { clockPickerInteractor.setClockColor(colorToSet) }
+                                        {
+                                            clockPickerInteractor.setClockColor(
+                                                selectedColorId = colorModel.colorId,
+                                                colorToneProgress = colorToneProgress,
+                                                seedColor =
+                                                    blendColorWithTone(
+                                                        color = colorModel.color,
+                                                        colorTone =
+                                                            colorModel.getColorTone(
+                                                                colorToneProgress,
+                                                            ),
+                                                    ),
+                                            )
+                                        }
                                     },
                             )
                         )
@@ -197,9 +181,13 @@
                 initialValue = emptyList(),
             )
 
+    @OptIn(ExperimentalCoroutinesApi::class)
+    val selectedColorOptionPosition: Flow<Int> =
+        colorOptions.mapLatest { it.indexOfFirst { colorOption -> colorOption.isSelected } }
+
     private fun ColorSeedOption.toColorOptionViewModel(
         context: Context,
-        selectedColor: Int?,
+        selectedColorId: String?,
     ): ColorOptionViewModel {
         val colors = previewInfo.resolveColors(context.resources)
         return ColorOptionViewModel(
@@ -209,19 +197,25 @@
             color3 = colors[3],
             contentDescription = getContentDescription(context).toString(),
             title = context.getString(R.string.default_theme_title),
-            isSelected = selectedColor == null,
+            isSelected = selectedColorId == null,
             onClick =
-                if (selectedColor == null) {
+                if (selectedColorId == null) {
                     null
                 } else {
-                    { clockPickerInteractor.setClockColor(null) }
+                    {
+                        clockPickerInteractor.setClockColor(
+                            selectedColorId = null,
+                            colorToneProgress = ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS,
+                            seedColor = null,
+                        )
+                    }
                 },
         )
     }
 
     private fun ColorBundle.toColorOptionViewModel(
         context: Context,
-        selectedColor: Int?
+        selectedColorId: String?
     ): ColorOptionViewModel {
         val primaryColor = previewInfo.resolvePrimaryColor(context.resources)
         val secondaryColor = previewInfo.resolveSecondaryColor(context.resources)
@@ -232,12 +226,18 @@
             color3 = secondaryColor,
             contentDescription = getContentDescription(context).toString(),
             title = context.getString(R.string.default_theme_title),
-            isSelected = selectedColor == null,
+            isSelected = selectedColorId == null,
             onClick =
-                if (selectedColor == null) {
+                if (selectedColorId == null) {
                     null
                 } else {
-                    { clockPickerInteractor.setClockColor(null) }
+                    {
+                        clockPickerInteractor.setClockColor(
+                            selectedColorId = null,
+                            colorToneProgress = ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS,
+                            seedColor = null,
+                        )
+                    }
                 },
         )
     }
@@ -277,21 +277,18 @@
         }
 
     companion object {
-        // TODO (b/241966062) The color integers here are temporary for dev purposes. We need to
-        //                    finalize the overridden colors.
-        val COLOR_LIST_HSL =
-            listOf(
-                arrayOf(225f, 0.65f, 0.74f).toFloatArray(),
-                arrayOf(30f, 0.65f, 0.74f).toFloatArray(),
-                arrayOf(249f, 0.65f, 0.74f).toFloatArray(),
-                arrayOf(144f, 0.65f, 0.74f).toFloatArray(),
+        private val helperColorLab: DoubleArray by lazy { DoubleArray(3) }
+
+        fun blendColorWithTone(color: Int, colorTone: Double): Int {
+            ColorUtils.colorToLAB(color, helperColorLab)
+            return ColorUtils.LABToColor(
+                colorTone,
+                helperColorLab[1],
+                helperColorLab[2],
             )
+        }
 
         const val COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS: Long = 100
-
-        fun getSelectedColorPosition(selectedColorHsl: FloatArray): Int {
-            return COLOR_LIST_HSL.withIndex().minBy { abs(it.value[0] - selectedColorHsl[0]) }.index
-        }
     }
 
     class Factory(
diff --git a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
index 0b197b4..700439b 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
@@ -79,7 +79,7 @@
                     carouselView = carouselView,
                     singleClockView = singleClockView,
                     viewModel = clockCarouselViewModel,
-                    clockViewFactory = { clockId -> clockViewFactory.getView(clockId) },
+                    clockViewFactory = clockViewFactory,
                     lifecycleOwner = lifecycleOwner,
                 )
             onScreenSwitched(
diff --git a/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt b/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt
index 6291c21..5953937 100644
--- a/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt
+++ b/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt
@@ -32,13 +32,17 @@
 class FakeGridRepository(
     private val scope: CoroutineScope,
     initialOptionCount: Int,
+    var available: Boolean = true
 ) : GridRepository {
     private val _optionChanges =
         MutableSharedFlow<Unit>(
             replay = 1,
             onBufferOverflow = BufferOverflow.DROP_OLDEST,
         )
-    override val optionChanges: Flow<Unit> = _optionChanges.asSharedFlow()
+
+    override suspend fun isAvailable(): Boolean = available
+
+    override fun getOptionChanges(): Flow<Unit> = _optionChanges.asSharedFlow()
 
     private val selectedOptionIndex = MutableStateFlow(0)
     private var options: GridOptionItemsModel = createOptions(count = initialOptionCount)
diff --git a/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt b/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt
index 20dd300..f73d5a3 100644
--- a/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt
+++ b/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt
@@ -135,4 +135,12 @@
             // External updates do not record a new snapshot with the undo system.
             assertThat(store.retrieve()).isEqualTo(storedSnapshot)
         }
+
+    @Test
+    fun unavailableRepository_emptyOptions() =
+        testScope.runTest {
+            repository.available = false
+            val options = collectLastValue(underTest.options)
+            assertThat(options()).isNull()
+        }
 }
diff --git a/tests/src/com/android/customization/picker/clock/data/repository/FakeClockPickerRepository.kt b/tests/src/com/android/customization/picker/clock/data/repository/FakeClockPickerRepository.kt
index b2cb452..2ef4e97 100644
--- a/tests/src/com/android/customization/picker/clock/data/repository/FakeClockPickerRepository.kt
+++ b/tests/src/com/android/customization/picker/clock/data/repository/FakeClockPickerRepository.kt
@@ -15,6 +15,9 @@
  */
 package com.android.customization.picker.clock.data.repository
 
+import android.graphics.Color
+import androidx.annotation.ColorInt
+import androidx.annotation.IntRange
 import com.android.customization.picker.clock.data.repository.FakeClockPickerRepository.Companion.fakeClocks
 import com.android.customization.picker.clock.shared.ClockSize
 import com.android.customization.picker.clock.shared.model.ClockMetadataModel
@@ -24,17 +27,30 @@
 import kotlinx.coroutines.flow.combine
 
 /** By default [FakeClockPickerRepository] uses [fakeClocks]. */
-open class FakeClockPickerRepository(private val clocks: List<ClockMetadataModel> = fakeClocks) :
+open class FakeClockPickerRepository(clocks: List<ClockMetadataModel> = fakeClocks) :
     ClockPickerRepository {
     override val allClocks: Flow<List<ClockMetadataModel>> = MutableStateFlow(clocks).asStateFlow()
 
     private val selectedClockId = MutableStateFlow(fakeClocks[0].clockId)
-    private val clockColor = MutableStateFlow<Int?>(null)
+    @ColorInt private val selectedColorId = MutableStateFlow<String?>(null)
+    private val colorTone = MutableStateFlow(ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS)
+    @ColorInt private val seedColor = MutableStateFlow<Int?>(null)
     override val selectedClock: Flow<ClockMetadataModel> =
-        combine(selectedClockId, clockColor) { selectedClockId, clockColor ->
+        combine(
+            selectedClockId,
+            selectedColorId,
+            colorTone,
+            seedColor,
+        ) { selectedClockId, selectedColor, colorTone, seedColor ->
             val selectedClock = fakeClocks.find { clock -> clock.clockId == selectedClockId }
             checkNotNull(selectedClock)
-            ClockMetadataModel(selectedClock.clockId, selectedClock.name, clockColor)
+            ClockMetadataModel(
+                selectedClock.clockId,
+                selectedClock.name,
+                selectedColor,
+                colorTone,
+                seedColor,
+            )
         }
 
     private val _selectedClockSize = MutableStateFlow(ClockSize.SMALL)
@@ -44,8 +60,14 @@
         selectedClockId.value = clockId
     }
 
-    override fun setClockColor(color: Int?) {
-        clockColor.value = color
+    override fun setClockColor(
+        selectedColorId: String?,
+        @IntRange(from = 0, to = 100) colorToneProgress: Int,
+        @ColorInt seedColor: Int?,
+    ) {
+        this.selectedColorId.value = selectedColorId
+        this.colorTone.value = colorToneProgress
+        this.seedColor.value = seedColor
     }
 
     override suspend fun setClockSize(size: ClockSize) {
@@ -55,10 +77,13 @@
     companion object {
         val fakeClocks =
             listOf(
-                ClockMetadataModel("clock0", "clock0", null),
-                ClockMetadataModel("clock1", "clock1", null),
-                ClockMetadataModel("clock2", "clock2", null),
-                ClockMetadataModel("clock3", "clock3", null),
+                ClockMetadataModel("clock0", "clock0", null, 50, null),
+                ClockMetadataModel("clock1", "clock1", null, 50, null),
+                ClockMetadataModel("clock2", "clock2", null, 50, null),
+                ClockMetadataModel("clock3", "clock3", null, 50, null),
             )
+        const val CLOCK_COLOR_ID = "RED"
+        const val CLOCK_COLOR_TONE_PROGRESS = 87
+        const val SEED_COLOR = Color.RED
     }
 }
diff --git a/tests/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractorTest.kt b/tests/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractorTest.kt
index 883d68b..cd41d7d 100644
--- a/tests/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractorTest.kt
+++ b/tests/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractorTest.kt
@@ -37,6 +37,14 @@
     }
 
     @Test
+    fun setSelectedClock() = runTest {
+        val observedSelectedClockId = collectLastValue(underTest.selectedClockId)
+        underTest.setSelectedClock(FakeClockPickerRepository.fakeClocks[1].clockId)
+        Truth.assertThat(observedSelectedClockId())
+            .isEqualTo(FakeClockPickerRepository.fakeClocks[1].clockId)
+    }
+
+    @Test
     fun setClockSize() = runTest {
         val observedClockSize = collectLastValue(underTest.selectedClockSize)
         underTest.setClockSize(ClockSize.DYNAMIC)
@@ -45,4 +53,21 @@
         underTest.setClockSize(ClockSize.SMALL)
         Truth.assertThat(observedClockSize()).isEqualTo(ClockSize.SMALL)
     }
+
+    @Test
+    fun setColor() = runTest {
+        val observedSelectedColor = collectLastValue(underTest.selectedColorId)
+        val observedColorToneProgress = collectLastValue(underTest.colorToneProgress)
+        val observedSeedColor = collectLastValue(underTest.seedColor)
+        underTest.setClockColor(
+            FakeClockPickerRepository.CLOCK_COLOR_ID,
+            FakeClockPickerRepository.CLOCK_COLOR_TONE_PROGRESS,
+            FakeClockPickerRepository.SEED_COLOR,
+        )
+        Truth.assertThat(observedSelectedColor())
+            .isEqualTo(FakeClockPickerRepository.CLOCK_COLOR_ID)
+        Truth.assertThat(observedColorToneProgress())
+            .isEqualTo(FakeClockPickerRepository.CLOCK_COLOR_TONE_PROGRESS)
+        Truth.assertThat(observedSeedColor()).isEqualTo(FakeClockPickerRepository.SEED_COLOR)
+    }
 }
diff --git a/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModelTest.kt b/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModelTest.kt
index 35c3518..63f77bd 100644
--- a/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModelTest.kt
+++ b/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModelTest.kt
@@ -42,7 +42,13 @@
     private val repositoryWithSingleClock by lazy {
         FakeClockPickerRepository(
             listOf(
-                ClockMetadataModel("clock0", "clock0", null),
+                ClockMetadataModel(
+                    "clock0",
+                    "clock0",
+                    null,
+                    ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS,
+                    null,
+                ),
             )
         )
     }
diff --git a/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModelTest.kt b/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModelTest.kt
new file mode 100644
index 0000000..61976ad
--- /dev/null
+++ b/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModelTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 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.customization.picker.clock.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.customization.picker.clock.data.repository.FakeClockPickerRepository
+import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
+import com.android.customization.picker.clock.shared.ClockSize
+import com.android.customization.picker.clock.shared.model.ClockMetadataModel
+import com.android.wallpaper.testing.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class ClockSectionViewModelTest {
+
+    private lateinit var clockColorMap: Map<String, ClockColorViewModel>
+    private lateinit var interactor: ClockPickerInteractor
+    private lateinit var underTest: ClockSectionViewModel
+
+    @Before
+    fun setUp() {
+        val testDispatcher = StandardTestDispatcher()
+        Dispatchers.setMain(testDispatcher)
+        val context = InstrumentationRegistry.getInstrumentation().targetContext
+        clockColorMap = ClockColorViewModel.getPresetColorMap(context.resources)
+        interactor = ClockPickerInteractor(FakeClockPickerRepository())
+        underTest =
+            ClockSectionViewModel(
+                context,
+                interactor,
+            )
+    }
+
+    @After
+    fun tearDown() {
+        Dispatchers.resetMain()
+    }
+
+    @Test
+    fun setSelectedClock() = runTest {
+        val colorRed = clockColorMap.values.first()
+        val observedSelectedClockColorAndSizeText =
+            collectLastValue(underTest.selectedClockColorAndSizeText)
+        interactor.setClockColor(
+            colorRed.colorId,
+            ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS,
+            ClockSettingsViewModel.blendColorWithTone(
+                colorRed.color,
+                colorRed.getColorTone(ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS),
+            )
+        )
+        interactor.setClockSize(ClockSize.DYNAMIC)
+        assertThat(observedSelectedClockColorAndSizeText()).isEqualTo("Red, dynamic")
+    }
+}
diff --git a/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsTabViewModelTest.kt b/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModelTest.kt
similarity index 71%
rename from tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsTabViewModelTest.kt
rename to tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModelTest.kt
index 8f61d8b..d53288d 100644
--- a/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsTabViewModelTest.kt
+++ b/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModelTest.kt
@@ -6,12 +6,12 @@
 import com.android.customization.picker.clock.data.repository.FakeClockPickerRepository
 import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
 import com.android.customization.picker.clock.shared.ClockSize
+import com.android.customization.picker.clock.shared.model.ClockMetadataModel
 import com.android.customization.picker.color.data.repository.FakeColorPickerRepository
 import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
 import com.android.customization.picker.color.domain.interactor.ColorPickerSnapshotRestorer
 import com.android.wallpaper.testing.FakeSnapshotStore
 import com.android.wallpaper.testing.collectLastValue
-import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -32,10 +32,10 @@
 @RunWith(JUnit4::class)
 class ClockSettingsViewModelTest {
 
-    private lateinit var underTest: ClockSettingsViewModel
-    private lateinit var colorPickerInteractor: ColorPickerInteractor
-    private lateinit var store: FakeSnapshotStore
     private lateinit var context: Context
+    private lateinit var colorPickerInteractor: ColorPickerInteractor
+    private lateinit var underTest: ClockSettingsViewModel
+    private lateinit var colorMap: Map<String, ClockColorViewModel>
 
     @Before
     fun setUp() {
@@ -47,7 +47,7 @@
                 repository = FakeColorPickerRepository(context = context),
                 snapshotRestorer = {
                     ColorPickerSnapshotRestorer(interactor = colorPickerInteractor).apply {
-                        runBlocking { setUpSnapshotRestorer(store = store) }
+                        runBlocking { setUpSnapshotRestorer(store = FakeSnapshotStore()) }
                     }
                 },
             )
@@ -58,6 +58,7 @@
                     colorPickerInteractor = colorPickerInteractor,
                 )
                 .create(ClockSettingsViewModel::class.java)
+        colorMap = ClockColorViewModel.getPresetColorMap(context.resources)
     }
 
     @After
@@ -66,43 +67,79 @@
     }
 
     @Test
-    fun setClockColor() = runTest {
+    fun clickOnColorSettingsTab() = runTest {
+        val tabs = collectLastValue(underTest.tabs)
+        assertThat(tabs()?.get(0)?.name).isEqualTo("Color")
+        assertThat(tabs()?.get(0)?.isSelected).isTrue()
+        assertThat(tabs()?.get(1)?.name).isEqualTo("Size")
+        assertThat(tabs()?.get(1)?.isSelected).isFalse()
+
+        tabs()?.get(1)?.onClicked?.invoke()
+        assertThat(tabs()?.get(0)?.isSelected).isFalse()
+        assertThat(tabs()?.get(1)?.isSelected).isTrue()
+    }
+
+    @Test
+    fun setSelectedColor() = runTest {
         val observedClockColorOptions = collectLastValue(underTest.colorOptions)
+        val observedSelectedColorOptionPosition =
+            collectLastValue(underTest.selectedColorOptionPosition)
+        val observedSliderProgress = collectLastValue(underTest.sliderProgress)
+        val observedSeedColor = collectLastValue(underTest.seedColor)
         // Advance COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from colorOptions
         advanceTimeBy(ClockSettingsViewModel.COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
         assertThat(observedClockColorOptions()!![0].isSelected).isTrue()
         assertThat(observedClockColorOptions()!![0].onClick).isNull()
+        assertThat(observedSelectedColorOptionPosition()).isEqualTo(0)
 
         observedClockColorOptions()!![1].onClick?.invoke()
         // Advance COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from colorOptions
         advanceTimeBy(ClockSettingsViewModel.COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
         assertThat(observedClockColorOptions()!![1].isSelected).isTrue()
         assertThat(observedClockColorOptions()!![1].onClick).isNull()
+        assertThat(observedSelectedColorOptionPosition()).isEqualTo(1)
+        assertThat(observedSliderProgress())
+            .isEqualTo(ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS)
+        val expectedSelectedColorModel = colorMap.values.first() // RED
+        assertThat(observedSeedColor())
+            .isEqualTo(
+                ClockSettingsViewModel.blendColorWithTone(
+                    expectedSelectedColorModel.color,
+                    expectedSelectedColorModel.getColorTone(
+                        ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS
+                    ),
+                )
+            )
     }
 
     @Test
-    fun setClockSaturation() = runTest {
+    fun setColorTone() = runTest {
         val observedClockColorOptions = collectLastValue(underTest.colorOptions)
         val observedIsSliderEnabled = collectLastValue(underTest.isSliderEnabled)
         val observedSliderProgress = collectLastValue(underTest.sliderProgress)
+        val observedSeedColor = collectLastValue(underTest.seedColor)
         // Advance COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from colorOptions
         advanceTimeBy(ClockSettingsViewModel.COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
+        assertThat(observedClockColorOptions()!![0].isSelected).isTrue()
         assertThat(observedIsSliderEnabled()).isFalse()
-        assertThat(observedSliderProgress()).isNull()
 
         observedClockColorOptions()!![1].onClick?.invoke()
+
         // Advance COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from colorOptions
         advanceTimeBy(ClockSettingsViewModel.COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
         assertThat(observedIsSliderEnabled()).isTrue()
-        val targetProgress = 99
-        underTest.onSliderProgressChanged(targetProgress)
-        advanceTimeBy(ClockSettingsViewModel.COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
-        assertThat(observedClockColorOptions()!![1].isSelected).isTrue()
-        assertThat(observedSliderProgress())
-            .isIn(
-                Range.closed(
-                    targetProgress - 1,
-                    targetProgress + 1,
+        val targetProgress1 = 99
+        underTest.onSliderProgressChanged(targetProgress1)
+        assertThat(observedSliderProgress()).isEqualTo(targetProgress1)
+        val targetProgress2 = 55
+        underTest.onSliderProgressStop(targetProgress2)
+        assertThat(observedSliderProgress()).isEqualTo(targetProgress2)
+        val expectedSelectedColorModel = colorMap.values.first() // RED
+        assertThat(observedSeedColor())
+            .isEqualTo(
+                ClockSettingsViewModel.blendColorWithTone(
+                    expectedSelectedColorModel.color,
+                    expectedSelectedColorModel.getColorTone(targetProgress2),
                 )
             )
     }
@@ -116,17 +153,4 @@
         underTest.setClockSize(ClockSize.SMALL)
         assertThat(observedClockSize()).isEqualTo(ClockSize.SMALL)
     }
-
-    @Test
-    fun `Click on a picker tab`() = runTest {
-        val tabs = collectLastValue(underTest.tabs)
-        assertThat(tabs()?.get(0)?.name).isEqualTo("Color")
-        assertThat(tabs()?.get(0)?.isSelected).isTrue()
-        assertThat(tabs()?.get(1)?.name).isEqualTo("Size")
-        assertThat(tabs()?.get(1)?.isSelected).isFalse()
-
-        tabs()?.get(1)?.onClicked?.invoke()
-        assertThat(tabs()?.get(0)?.isSelected).isFalse()
-        assertThat(tabs()?.get(1)?.isSelected).isTrue()
-    }
 }