Merge "Fix inability to select default clock color option" into main
diff --git a/res/layout/clock_host_view.xml b/res/layout/clock_host_view.xml
index 3f768e2..33cca01 100644
--- a/res/layout/clock_host_view.xml
+++ b/res/layout/clock_host_view.xml
@@ -19,4 +19,5 @@
     android:importantForAccessibility="noHideDescendants"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_gravity="center" />
\ No newline at end of file
+    android:layout_gravity="center"
+    android:clipChildren="false"/>
\ No newline at end of file
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index c708289..fbd03b9 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="1647136562008520313">"ផ្ទាំងរូបភាព និងរចនាប័ទ្ម"</string>
+    <string name="app_name" msgid="1647136562008520313">"ផ្ទាំងរូបភាព និងរចនាបថ"</string>
     <string name="theme_title" msgid="2144932106319405101">"រចនាប័ទ្ម"</string>
     <string name="clock_title" msgid="1974314575211361352">"នាឡិកាផ្ទាល់ខ្លួន"</string>
     <string name="clock_description" msgid="3563839327378948">"ជ្រើសរើសនាឡិកាផ្ទាល់ខ្លួន"</string>
diff --git a/src/com/android/customization/module/logging/ThemesUserEventLoggerImpl.kt b/src/com/android/customization/module/logging/ThemesUserEventLoggerImpl.kt
index b28086b..0a639fb 100644
--- a/src/com/android/customization/module/logging/ThemesUserEventLoggerImpl.kt
+++ b/src/com/android/customization/module/logging/ThemesUserEventLoggerImpl.kt
@@ -56,7 +56,6 @@
 import com.android.wallpaper.module.logging.UserEventLogger.EffectStatus
 import com.android.wallpaper.module.logging.UserEventLogger.SetWallpaperEntryPoint
 import com.android.wallpaper.module.logging.UserEventLogger.WallpaperDestination
-import com.android.wallpaper.util.ActivityUtils
 import com.android.wallpaper.util.LaunchSourceUtils
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -124,7 +123,7 @@
         effect: String,
         @EffectStatus status: Int,
         timeElapsedMillis: Long,
-        resultCode: Int
+        resultCode: Int,
     ) {
         SysUiStatsLogger(WALLPAPER_EFFECT_APPLIED)
             .setAppSessionId(appSessionId.getId())
@@ -146,7 +145,7 @@
     override fun logEffectForegroundDownload(
         effect: String,
         @EffectStatus status: Int,
-        timeElapsedMillis: Long
+        timeElapsedMillis: Long,
     ) {
         SysUiStatsLogger(WALLPAPER_EFFECT_FG_DOWNLOAD)
             .setAppSessionId(appSessionId.getId())
@@ -164,11 +163,7 @@
         SysUiStatsLogger(WALLPAPER_EXPLORE).setAppSessionId(appSessionId.getId()).log()
     }
 
-    override fun logThemeColorApplied(
-        @ColorSource source: Int,
-        style: Int,
-        seedColor: Int,
-    ) {
+    override fun logThemeColorApplied(@ColorSource source: Int, style: Int, seedColor: Int) {
         SysUiStatsLogger(THEME_COLOR_APPLIED)
             .setAppSessionId(appSessionId.getId())
             .setColorSource(source)
@@ -251,10 +246,9 @@
                 LaunchSourceUtils.LAUNCH_SOURCE_TIPS -> LAUNCHED_TIPS
                 LaunchSourceUtils.LAUNCH_SOURCE_DEEP_LINK -> LAUNCHED_DEEP_LINK
                 LaunchSourceUtils.LAUNCH_SOURCE_KEYGUARD -> LAUNCHED_KEYGUARD
+                LaunchSourceUtils.LAUNCH_SOURCE_SETTINGS_SEARCH -> LAUNCHED_SETTINGS_SEARCH
                 else -> LAUNCHED_PREFERENCE_UNSPECIFIED
             }
-        } else if (ActivityUtils.isLaunchedFromSettingsSearch(this)) {
-            LAUNCHED_SETTINGS_SEARCH
         } else if (action != null && action == WallpaperManager.ACTION_CROP_AND_SET_WALLPAPER) {
             LAUNCHED_CROP_AND_SET_ACTION
         } else if (categories != null && categories.contains(Intent.CATEGORY_LAUNCHER)) {
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockHostView2.kt b/src/com/android/customization/picker/clock/ui/view/ClockHostView2.kt
index d5b317e..be2e53d 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockHostView2.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockHostView2.kt
@@ -63,8 +63,7 @@
     private fun updatePivotAndScale() {
         when (clockSize) {
             ClockSize.DYNAMIC -> {
-                pivotX = (width / 2).toFloat()
-                pivotY = (height / 2).toFloat()
+                resetPivot()
             }
             ClockSize.SMALL -> {
                 pivotX = getCenteredHostViewPivotX(this)
diff --git a/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt b/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt
index 52df31a..61a648f 100644
--- a/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt
+++ b/src/com/android/customization/picker/color/ui/viewmodel/ColorPickerViewModel.kt
@@ -202,6 +202,6 @@
     }
 
     companion object {
-        private const val COLOR_SECTION_OPTION_SIZE = 5
+        private const val COLOR_SECTION_OPTION_SIZE = 6
     }
 }
diff --git a/src/com/android/customization/picker/settings/data/repository/ColorContrastSectionRepository.kt b/src/com/android/customization/picker/settings/data/repository/ColorContrastSectionRepository.kt
index 85cf307..6d5b0bc 100644
--- a/src/com/android/customization/picker/settings/data/repository/ColorContrastSectionRepository.kt
+++ b/src/com/android/customization/picker/settings/data/repository/ColorContrastSectionRepository.kt
@@ -17,6 +17,7 @@
 package com.android.customization.picker.settings.data.repository
 
 import android.app.UiModeManager
+import android.app.UiModeManager.ContrastUtils
 import com.android.wallpaper.picker.di.modules.BackgroundDispatcher
 import com.android.wallpaper.system.UiModeManagerWrapper
 import java.util.concurrent.Executor
@@ -35,16 +36,18 @@
     uiModeManager: UiModeManagerWrapper,
     @BackgroundDispatcher bgDispatcher: CoroutineDispatcher,
 ) {
-    var contrast: Flow<Float> = callbackFlow {
+    var contrast: Flow<Int> = callbackFlow {
         val executor: Executor = bgDispatcher.asExecutor()
         val listener =
             UiModeManager.ContrastChangeListener { contrast ->
                 // Emit the new contrast value whenever it changes
-                trySend(contrast)
+                trySend(ContrastUtils.toContrastLevel(contrast))
             }
 
         // Emit the current contrast value immediately
-        uiModeManager.getContrast()?.let { currentContrast -> trySend(currentContrast) }
+        uiModeManager.getContrast()?.let { currentContrast ->
+            trySend(ContrastUtils.toContrastLevel(currentContrast))
+        }
 
         uiModeManager.addContrastChangeListener(executor, listener)
 
diff --git a/src/com/android/customization/picker/settings/domain/interactor/ColorContrastSectionInteractor.kt b/src/com/android/customization/picker/settings/domain/interactor/ColorContrastSectionInteractor.kt
index 003d4d0..c4ccfb3 100644
--- a/src/com/android/customization/picker/settings/domain/interactor/ColorContrastSectionInteractor.kt
+++ b/src/com/android/customization/picker/settings/domain/interactor/ColorContrastSectionInteractor.kt
@@ -25,5 +25,5 @@
 class ColorContrastSectionInteractor
 @Inject
 constructor(colorContrastSectionRepository: ColorContrastSectionRepository) {
-    val contrast: Flow<Float> = colorContrastSectionRepository.contrast
+    val contrast: Flow<Int> = colorContrastSectionRepository.contrast
 }
diff --git a/src/com/android/customization/picker/settings/ui/viewmodel/ColorContrastSectionViewModel.kt b/src/com/android/customization/picker/settings/ui/viewmodel/ColorContrastSectionViewModel.kt
index ecbe9d1..3ea63cb 100644
--- a/src/com/android/customization/picker/settings/ui/viewmodel/ColorContrastSectionViewModel.kt
+++ b/src/com/android/customization/picker/settings/ui/viewmodel/ColorContrastSectionViewModel.kt
@@ -16,6 +16,10 @@
 
 package com.android.customization.picker.settings.ui.viewmodel
 
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD
+import android.util.Log
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import com.android.customization.picker.settings.domain.interactor.ColorContrastSectionInteractor
@@ -28,62 +32,54 @@
 import kotlinx.coroutines.flow.map
 
 class ColorContrastSectionViewModel
-private constructor(
-    colorContrastSectionInteractor: ColorContrastSectionInteractor,
-) : ViewModel() {
+private constructor(colorContrastSectionInteractor: ColorContrastSectionInteractor) : ViewModel() {
 
     val summary: Flow<ColorContrastSectionDataViewModel> =
         colorContrastSectionInteractor.contrast.map { contrastValue ->
             when (contrastValue) {
-                ContrastValue.STANDARD.value ->
+                CONTRAST_LEVEL_STANDARD ->
                     ColorContrastSectionDataViewModel(
                         Text.Resource(R.string.color_contrast_default_title),
                         Icon.Resource(
                             res = R.drawable.ic_contrast_standard,
                             contentDescription = null,
-                        )
+                        ),
                     )
-                ContrastValue.MEDIUM.value ->
+                CONTRAST_LEVEL_MEDIUM ->
                     ColorContrastSectionDataViewModel(
                         Text.Resource(R.string.color_contrast_medium_title),
                         Icon.Resource(
                             res = R.drawable.ic_contrast_medium,
                             contentDescription = null,
-                        )
+                        ),
                     )
-                ContrastValue.HIGH.value ->
+                CONTRAST_LEVEL_HIGH ->
                     ColorContrastSectionDataViewModel(
                         Text.Resource(R.string.color_contrast_high_title),
-                        Icon.Resource(
-                            res = R.drawable.ic_contrast_high,
-                            contentDescription = null,
-                        )
+                        Icon.Resource(res = R.drawable.ic_contrast_high, contentDescription = null),
                     )
                 else -> {
-                    println("Invalid contrast value: $contrastValue")
-                    throw IllegalArgumentException("Invalid contrast value")
+                    Log.e(TAG, "Invalid contrast value: $contrastValue")
+                    throw IllegalArgumentException("Invalid contrast value: $contrastValue")
                 }
             }
         }
 
-    enum class ContrastValue(val value: Float) {
-        STANDARD(0f),
-        MEDIUM(0.5f),
-        HIGH(1f)
-    }
-
     @Singleton
     class Factory
     @Inject
-    constructor(
-        private val colorContrastSectionInteractor: ColorContrastSectionInteractor,
-    ) : ViewModelProvider.Factory {
+    constructor(private val colorContrastSectionInteractor: ColorContrastSectionInteractor) :
+        ViewModelProvider.Factory {
         override fun <T : ViewModel> create(modelClass: Class<T>): T {
             @Suppress("UNCHECKED_CAST")
             return ColorContrastSectionViewModel(
-                colorContrastSectionInteractor = colorContrastSectionInteractor,
+                colorContrastSectionInteractor = colorContrastSectionInteractor
             )
                 as T
         }
     }
+
+    companion object {
+        private const val TAG = "ColorContrastSectionViewModel"
+    }
 }
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ShapeAndGridPickerViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ShapeAndGridPickerViewModel.kt
index a13a652..f319c04 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ShapeAndGridPickerViewModel.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ShapeAndGridPickerViewModel.kt
@@ -36,6 +36,7 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
 
 class ShapeAndGridPickerViewModel
@@ -47,33 +48,34 @@
 ) {
     // The currently-set system grid option
     val selectedGridOption =
-        interactor.selectedGridOption.filterNotNull().map { toOptionItemViewModel(it) }
-    private val _previewingGridOptionKey = MutableStateFlow<String?>(null)
+        interactor.selectedGridOption
+            .filterNotNull()
+            .map { toOptionItemViewModel(it) }
+            .shareIn(scope = viewModelScope, started = SharingStarted.Lazily, replay = 1)
+    private val overridingGridOptionKey = MutableStateFlow<String?>(null)
     // If the previewing key is null, use the currently-set system grid option
     val previewingGridOptionKey =
-        combine(selectedGridOption, _previewingGridOptionKey) {
-            currentlySetGridOption,
-            previewingGridOptionKey ->
-            previewingGridOptionKey ?: currentlySetGridOption.key.value
+        combine(overridingGridOptionKey, selectedGridOption) {
+            overridingGridOptionKey,
+            selectedGridOption ->
+            overridingGridOptionKey ?: selectedGridOption.key.value
         }
 
     fun resetPreview() {
-        _previewingGridOptionKey.tryEmit(null)
+        overridingGridOptionKey.value = null
     }
 
     val optionItems: Flow<List<OptionItemViewModel<GridIconViewModel>>> =
-        interactor.gridOptions.filterNotNull().map { gridOptions ->
-            gridOptions.map { toOptionItemViewModel(it) }
-        }
+        interactor.gridOptions
+            .filterNotNull()
+            .map { gridOptions -> gridOptions.map { toOptionItemViewModel(it) } }
+            .shareIn(scope = viewModelScope, started = SharingStarted.Lazily, replay = 1)
 
     val onApply: Flow<(suspend () -> Unit)?> =
-        combine(selectedGridOption, _previewingGridOptionKey) {
-            selectedGridOption,
-            previewingGridOptionKey ->
-            if (
-                previewingGridOptionKey == null ||
-                    previewingGridOptionKey == selectedGridOption.key.value
-            ) {
+        combine(previewingGridOptionKey, selectedGridOption) {
+            previewingGridOptionKey,
+            selectedGridOption ->
+            if (previewingGridOptionKey == selectedGridOption.key.value) {
                 null
             } else {
                 { interactor.applySelectedOption(previewingGridOptionKey) }
@@ -93,17 +95,11 @@
                     )
             )
         val isSelected =
-            _previewingGridOptionKey
-                .map {
-                    if (it == null) {
-                        option.isCurrent
-                    } else {
-                        it == option.key
-                    }
-                }
+            previewingGridOptionKey
+                .map { it == option.key }
                 .stateIn(
                     scope = viewModelScope,
-                    started = SharingStarted.Eagerly,
+                    started = SharingStarted.Lazily,
                     initialValue = false,
                 )
 
@@ -116,7 +112,7 @@
             onClicked =
                 isSelected.map {
                     if (!it) {
-                        { _previewingGridOptionKey.value = option.key }
+                        { overridingGridOptionKey.value = option.key }
                     } else {
                         null
                     }
diff --git a/tests/robotests/src/com/android/customization/model/picker/settings/data/repository/ColorContrastSectionRepositoryTest.kt b/tests/robotests/src/com/android/customization/model/picker/settings/data/repository/ColorContrastSectionRepositoryTest.kt
index cde597a..9d76b53 100644
--- a/tests/robotests/src/com/android/customization/model/picker/settings/data/repository/ColorContrastSectionRepositoryTest.kt
+++ b/tests/robotests/src/com/android/customization/model/picker/settings/data/repository/ColorContrastSectionRepositoryTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.customization.model.picker.settings.data.repository
 
+import android.app.UiModeManager.ContrastUtils
 import androidx.test.filters.SmallTest
 import com.android.customization.picker.settings.data.repository.ColorContrastSectionRepository
 import com.android.wallpaper.testing.FakeUiModeManager
@@ -61,8 +62,10 @@
     fun contrastFlowEmitsValues() =
         testScope.runTest {
             val nextContrastValues = listOf(0.5f, 0.7f, 0.8f)
+            val expectedContrastValues =
+                nextContrastValues.map { ContrastUtils.toContrastLevel(it) }
             // Set up a flow to collect all contrast values
-            val flowCollector = mutableListOf<Float>()
+            val flowCollector = mutableListOf<Int>()
             // Start collecting values from the flow, using an unconfined dispatcher to start
             // collecting from the flow right away (rather than explicitly calling `runCurrent`)
             // See https://developer.android.com/kotlin/flow/test#continuous-collection
@@ -74,6 +77,6 @@
 
             // Ignore the first contrast value from constructing the repository
             val collectedValues = flowCollector.drop(1)
-            assertThat(collectedValues).containsExactlyElementsIn(nextContrastValues)
+            assertThat(collectedValues).containsExactlyElementsIn(expectedContrastValues)
         }
 }
diff --git a/tests/robotests/src/com/android/customization/model/picker/settings/domain/interactor/ColorContrastSectionInteractorTest.kt b/tests/robotests/src/com/android/customization/model/picker/settings/domain/interactor/ColorContrastSectionInteractorTest.kt
index afa6427..d66cddf 100644
--- a/tests/robotests/src/com/android/customization/model/picker/settings/domain/interactor/ColorContrastSectionInteractorTest.kt
+++ b/tests/robotests/src/com/android/customization/model/picker/settings/domain/interactor/ColorContrastSectionInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.customization.model.picker.settings.domain.interactor
 
+import android.app.UiModeManager.ContrastUtils
 import androidx.test.filters.SmallTest
 import com.android.customization.picker.settings.domain.interactor.ColorContrastSectionInteractor
 import com.android.wallpaper.testing.FakeUiModeManager
@@ -47,11 +48,12 @@
 
     @Test
     fun contrastEmitCorrectValuesFromRepository() = runTest {
-        val expectedContrast = 1.5f
-        uiModeManager.setContrast(expectedContrast)
+        val contrastVal = 0.5f
+        val expectedContrastVal = ContrastUtils.toContrastLevel(contrastVal)
+        uiModeManager.setContrast(contrastVal)
 
         val result = interactor.contrast.first()
 
-        assertThat(result).isEqualTo(expectedContrast)
+        assertThat(result).isEqualTo(expectedContrastVal)
     }
 }
diff --git a/tests/robotests/src/com/android/customization/model/picker/settings/ui/viewmodel/ColorContrastSectionViewModelTest.kt b/tests/robotests/src/com/android/customization/model/picker/settings/ui/viewmodel/ColorContrastSectionViewModelTest.kt
index 0c420e0..2223282 100644
--- a/tests/robotests/src/com/android/customization/model/picker/settings/ui/viewmodel/ColorContrastSectionViewModelTest.kt
+++ b/tests/robotests/src/com/android/customization/model/picker/settings/ui/viewmodel/ColorContrastSectionViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.customization.model.picker.settings.ui.viewmodel
 
+import android.app.UiModeManager.ContrastUtils
 import com.android.customization.picker.settings.ui.viewmodel.ColorContrastSectionDataViewModel
 import com.android.customization.picker.settings.ui.viewmodel.ColorContrastSectionViewModel
 import com.android.themepicker.R
@@ -60,11 +61,13 @@
 
     @Test
     fun summaryEmitsCorrectDataValueForStandard() = runTest {
-        uiModeManager.setContrast(ColorContrastSectionViewModel.ContrastValue.STANDARD.value)
+        uiModeManager.setContrast(
+            ContrastUtils.fromContrastLevel(ContrastUtils.CONTRAST_LEVEL_STANDARD)
+        )
         val expected =
             ColorContrastSectionDataViewModel(
                 Text.Resource(R.string.color_contrast_default_title),
-                Icon.Resource(res = R.drawable.ic_contrast_standard, contentDescription = null)
+                Icon.Resource(res = R.drawable.ic_contrast_standard, contentDescription = null),
             )
 
         val result = viewModel.summary.first()
@@ -74,11 +77,13 @@
 
     @Test
     fun summaryEmitsCorrectDataValueForMedium() = runTest {
-        uiModeManager.setContrast(ColorContrastSectionViewModel.ContrastValue.MEDIUM.value)
+        uiModeManager.setContrast(
+            ContrastUtils.fromContrastLevel(ContrastUtils.CONTRAST_LEVEL_MEDIUM)
+        )
         val expected =
             ColorContrastSectionDataViewModel(
                 Text.Resource(R.string.color_contrast_medium_title),
-                Icon.Resource(res = R.drawable.ic_contrast_medium, contentDescription = null)
+                Icon.Resource(res = R.drawable.ic_contrast_medium, contentDescription = null),
             )
 
         val result = viewModel.summary.first()
@@ -88,11 +93,13 @@
 
     @Test
     fun summaryEmitsCorrectDataValueForHigh() = runTest {
-        uiModeManager.setContrast(ColorContrastSectionViewModel.ContrastValue.HIGH.value)
+        uiModeManager.setContrast(
+            ContrastUtils.fromContrastLevel(ContrastUtils.CONTRAST_LEVEL_HIGH)
+        )
         val expected =
             ColorContrastSectionDataViewModel(
                 Text.Resource(R.string.color_contrast_high_title),
-                Icon.Resource(res = R.drawable.ic_contrast_high, contentDescription = null)
+                Icon.Resource(res = R.drawable.ic_contrast_high, contentDescription = null),
             )
 
         val result = viewModel.summary.first()
diff --git a/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ShapeAndGridPickerViewModelTest.kt b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ShapeAndGridPickerViewModelTest.kt
index 02d3ce7..af9bc4e 100644
--- a/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ShapeAndGridPickerViewModelTest.kt
+++ b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ShapeAndGridPickerViewModelTest.kt
@@ -176,7 +176,7 @@
             )
         }
 
-    private fun assertOptionItem(
+    private fun TestScope.assertOptionItem(
         optionItem: OptionItemViewModel<GridIconViewModel>?,
         key: String,
         payload: GridIconViewModel?,
@@ -186,11 +186,11 @@
         isEnabled: Boolean,
     ) {
         checkNotNull(optionItem)
-        assertThat(optionItem.key.value).isEqualTo(key)
+        assertThat(collectLastValue(optionItem.key)()).isEqualTo(key)
         assertThat(optionItem.text).isEqualTo(text)
         assertThat(optionItem.payload).isEqualTo(payload)
         assertThat(optionItem.isTextUserVisible).isEqualTo(isTextUserVisible)
-        assertThat(optionItem.isSelected.value).isEqualTo(isSelected)
+        assertThat(collectLastValue(optionItem.isSelected)()).isEqualTo(isSelected)
         assertThat(optionItem.isEnabled).isEqualTo(isEnabled)
     }
 }