Merge "Import translations. DO NOT MERGE ANYWHERE" into main
diff --git a/src/com/android/customization/model/color/ColorProvider.kt b/src/com/android/customization/model/color/ColorProvider.kt
index ef5e3d6..5c2b891 100644
--- a/src/com/android/customization/model/color/ColorProvider.kt
+++ b/src/com/android/customization/model/color/ColorProvider.kt
@@ -117,16 +117,19 @@
         }
 
         scope.launch {
+            // Wait for the previous preset color loading job to finish before evaluating whether to
+            // start a new one
             loaderJob?.join()
             if (presetColorBundles == null || reload) {
-                try {
-                    loaderJob = launch { loadPreset(isNewPickerUi) }
-                } catch (e: Throwable) {
-                    colorsAvailable = false
-                    callback?.onError(e)
-                    return@launch
+                loaderJob = launch {
+                    try {
+                        loadPreset(isNewPickerUi)
+                        callback?.onOptionsLoaded(buildFinalList(isNewPickerUi))
+                    } catch (e: Throwable) {
+                        colorsAvailable = false
+                        callback?.onError(e)
+                    }
                 }
-                callback?.onOptionsLoaded(buildFinalList(isNewPickerUi))
             } else {
                 callback?.onOptionsLoaded(buildFinalList(isNewPickerUi))
             }
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
index 27bc42c..dccef6e 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -467,15 +467,16 @@
                 it.pivotY = it.height / 2F
             }
 
-            val controller = clockViewFactory.getController(clockId)
-            if (isMiddleView) {
-                clockScaleView.scaleX = 1f
-                clockScaleView.scaleY = 1f
-                controller.largeClock.animations.onPickerCarouselSwiping(1F)
-            } else {
-                clockScaleView.scaleX = clockViewScale
-                clockScaleView.scaleY = clockViewScale
-                controller.largeClock.animations.onPickerCarouselSwiping(0F)
+            clockViewFactory.getController(clockId)?.let { controller ->
+                if (isMiddleView) {
+                    clockScaleView.scaleX = 1f
+                    clockScaleView.scaleY = 1f
+                    controller.largeClock.animations.onPickerCarouselSwiping(1F)
+                } else {
+                    clockScaleView.scaleX = clockViewScale
+                    clockScaleView.scaleY = clockViewScale
+                    controller.largeClock.animations.onPickerCarouselSwiping(0F)
+                }
             }
         }
 
diff --git a/src/com/android/customization/picker/clock/ui/view/ThemePickerClockViewFactory.kt b/src/com/android/customization/picker/clock/ui/view/ThemePickerClockViewFactory.kt
index 73ebb0f..c48d810 100644
--- a/src/com/android/customization/picker/clock/ui/view/ThemePickerClockViewFactory.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ThemePickerClockViewFactory.kt
@@ -43,7 +43,7 @@
 class ThemePickerClockViewFactory
 @Inject
 constructor(
-    activity: Activity,
+    private val activity: Activity,
     private val wallpaperManager: WallpaperManager,
     private val registry: ClockRegistry,
 ) : ClockViewFactory {
@@ -55,9 +55,9 @@
     private val clockControllers: ConcurrentHashMap<String, ClockController> = ConcurrentHashMap()
     private val smallClockFrames: HashMap<String, FrameLayout> = HashMap()
 
-    override fun getController(clockId: String): ClockController {
+    override fun getController(clockId: String): ClockController? {
         return clockControllers[clockId]
-            ?: initClockController(clockId).also { clockControllers[clockId] = it }
+            ?: initClockController(clockId)?.also { clockControllers[clockId] = it }
     }
 
     /**
@@ -66,10 +66,10 @@
      */
     override fun getLargeView(clockId: String): View {
         assert(!Flags.newCustomizationPickerUi())
-        return getController(clockId).largeClock.let {
+        return getController(clockId)?.largeClock?.let {
             it.animations.onPickerCarouselSwiping(1F)
             it.view
-        }
+        } ?: FrameLayout(activity)
     }
 
     /**
@@ -83,9 +83,9 @@
                 (layoutParams as FrameLayout.LayoutParams).topMargin = getSmallClockTopMargin()
                 (layoutParams as FrameLayout.LayoutParams).marginStart = getSmallClockStartPadding()
             }
-                ?: createSmallClockFrame().also {
-                    it.addView(getController(clockId).smallClock.view)
-                    smallClockFrames[clockId] = it
+                ?: createSmallClockFrame().also { frame ->
+                    getController(clockId)?.let { frame.addView(it.smallClock.view) }
+                    smallClockFrames[clockId] = frame
                 }
         smallClockFrame.translationX = 0F
         smallClockFrame.translationY = 0F
@@ -130,14 +130,14 @@
     }
 
     override fun updateColor(clockId: String, @ColorInt seedColor: Int?) {
-        getController(clockId).let {
+        getController(clockId)?.let {
             it.largeClock.run { events.onThemeChanged(theme.copy(seedColor = seedColor)) }
             it.smallClock.run { events.onThemeChanged(theme.copy(seedColor = seedColor)) }
         }
     }
 
     override fun updateFontAxes(clockId: String, settings: List<ClockFontAxisSetting>) {
-        getController(clockId).let { it.events.onFontAxesChanged(settings) }
+        getController(clockId)?.let { it.events.onFontAxesChanged(settings) }
     }
 
     override fun updateRegionDarkness() {
@@ -155,8 +155,8 @@
 
     override fun updateTimeFormat(clockId: String) {
         getController(clockId)
-            .events
-            .onTimeFormatChanged(android.text.format.DateFormat.is24HourFormat(appContext))
+            ?.events
+            ?.onTimeFormatChanged(android.text.format.DateFormat.is24HourFormat(appContext))
     }
 
     override fun registerTimeTicker(owner: LifecycleOwner) {
@@ -190,33 +190,32 @@
         }
     }
 
-    private fun initClockController(clockId: String): ClockController {
+    private fun initClockController(clockId: String): ClockController? {
         val isWallpaperDark = isLockscreenWallpaperDark()
-        val controller =
-            registry.createExampleClock(clockId).also { it?.initialize(isWallpaperDark, 0f, 0f) }
-        checkNotNull(controller)
+        return registry.createExampleClock(clockId)?.also { controller ->
+            controller.initialize(isWallpaperDark, 0f, 0f)
 
-        // Initialize large clock
-        controller.largeClock.events.onFontSettingChanged(
-            resources
-                .getDimensionPixelSize(
-                    com.android.systemui.customization.R.dimen.large_clock_text_size
-                )
-                .toFloat()
-        )
-        controller.largeClock.events.onTargetRegionChanged(getLargeClockRegion())
+            // Initialize large clock
+            controller.largeClock.events.onFontSettingChanged(
+                resources
+                    .getDimensionPixelSize(
+                        com.android.systemui.customization.R.dimen.large_clock_text_size
+                    )
+                    .toFloat()
+            )
+            controller.largeClock.events.onTargetRegionChanged(getLargeClockRegion())
 
-        // Initialize small clock
-        controller.smallClock.events.onFontSettingChanged(
-            resources
-                .getDimensionPixelSize(
-                    com.android.systemui.customization.R.dimen.small_clock_text_size
-                )
-                .toFloat()
-        )
-        controller.smallClock.events.onTargetRegionChanged(getSmallClockRegion())
-        controller.events.onWeatherDataChanged(WeatherData.getPlaceholderWeatherData())
-        return controller
+            // Initialize small clock
+            controller.smallClock.events.onFontSettingChanged(
+                resources
+                    .getDimensionPixelSize(
+                        com.android.systemui.customization.R.dimen.small_clock_text_size
+                    )
+                    .toFloat()
+            )
+            controller.smallClock.events.onTargetRegionChanged(getSmallClockRegion())
+            controller.events.onWeatherDataChanged(WeatherData.getPlaceholderWeatherData())
+        }
     }
 
     /**
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 28f58c4..874b49d 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
@@ -32,6 +32,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
@@ -57,14 +58,22 @@
             .mapLatest { allClocks ->
                 // Delay to avoid the case that the full list of clocks is not initiated.
                 delay(CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
-                allClocks.map {
-                    val contentDescription =
-                        resources.getString(
-                            R.string.select_clock_action_description,
-                            clockViewFactory.getController(it.clockId).config.description
-                        )
-                    ClockCarouselItemViewModel(it.clockId, it.isSelected, contentDescription)
-                }
+                allClocks
+                    .map { model ->
+                        clockViewFactory.getController(model.clockId)?.let { clock ->
+                            val contentDescription =
+                                resources.getString(
+                                    R.string.select_clock_action_description,
+                                    clock.config.description,
+                                )
+                            ClockCarouselItemViewModel(
+                                model.clockId,
+                                model.isSelected,
+                                contentDescription,
+                            )
+                        }
+                    }
+                    .filterNotNull()
             }
             // makes sure that the operations above this statement are executed on I/O dispatcher
             // while parallelism limits the number of threads this can run on which makes sure that
@@ -126,6 +135,7 @@
             .mapNotNull { it }
 
     private var setSelectedClockJob: Job? = null
+
     fun setSelectedClock(clockId: String) {
         setSelectedClockJob?.cancel()
         setSelectedClockJob =
diff --git a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
index 43d510c..7e63834 100644
--- a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl.kt
@@ -33,7 +33,6 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.suspendCancellableCoroutine
 
 // TODO (b/262924623): refactor to remove dependency on ColorCustomizationManager & ColorOption
@@ -58,68 +57,61 @@
 
     override val colorOptions: Flow<Map<ColorType, List<ColorOptionModel>>> =
         combine(homeWallpaperColors, lockWallpaperColors) { homeColors, lockColors ->
-                homeColors to lockColors
-            }
-            .map { (homeColors, lockColors) ->
-                suspendCancellableCoroutine { continuation ->
-                    if (
-                        homeColors is WallpaperColorsModel.Loading ||
-                            lockColors is WallpaperColorsModel.Loading
-                    ) {
-                        continuation.resumeWith(
-                            Result.success(
-                                mapOf(
-                                    ColorType.WALLPAPER_COLOR to listOf(),
-                                    ColorType.PRESET_COLOR to listOf(),
-                                )
+            suspendCancellableCoroutine { continuation ->
+                if (
+                    homeColors is WallpaperColorsModel.Loading ||
+                        lockColors is WallpaperColorsModel.Loading
+                ) {
+                    continuation.resumeWith(
+                        Result.success(
+                            mapOf(
+                                ColorType.WALLPAPER_COLOR to listOf(),
+                                ColorType.PRESET_COLOR to listOf(),
                             )
                         )
-                        return@suspendCancellableCoroutine
-                    }
-                    val homeColorsLoaded = homeColors as WallpaperColorsModel.Loaded
-                    val lockColorsLoaded = lockColors as WallpaperColorsModel.Loaded
-                    colorManager.setWallpaperColors(
-                        homeColorsLoaded.colors,
-                        lockColorsLoaded.colors,
                     )
-                    colorManager.fetchOptions(
-                        object : CustomizationManager.OptionsFetchedListener<ColorOption?> {
-                            override fun onOptionsLoaded(options: MutableList<ColorOption?>?) {
-                                val wallpaperColorOptions: MutableList<ColorOptionModel> =
-                                    mutableListOf()
-                                val presetColorOptions: MutableList<ColorOptionModel> =
-                                    mutableListOf()
-                                options?.forEach { option ->
-                                    when ((option as ColorOptionImpl).type) {
-                                        ColorType.WALLPAPER_COLOR ->
-                                            wallpaperColorOptions.add(option.toModel())
-                                        ColorType.PRESET_COLOR ->
-                                            presetColorOptions.add(option.toModel())
-                                    }
-                                }
-                                continuation.resumeWith(
-                                    Result.success(
-                                        mapOf(
-                                            ColorType.WALLPAPER_COLOR to wallpaperColorOptions,
-                                            ColorType.PRESET_COLOR to presetColorOptions,
-                                        )
-                                    )
-                                )
-                            }
-
-                            override fun onError(throwable: Throwable?) {
-                                Log.e(TAG, "Error loading theme bundles", throwable)
-                                continuation.resumeWith(
-                                    Result.failure(
-                                        throwable ?: Throwable("Error loading theme bundles")
-                                    )
-                                )
-                            }
-                        },
-                        /* reload= */ false,
-                    )
+                    return@suspendCancellableCoroutine
                 }
+                val homeColorsLoaded = homeColors as WallpaperColorsModel.Loaded
+                val lockColorsLoaded = lockColors as WallpaperColorsModel.Loaded
+                colorManager.setWallpaperColors(homeColorsLoaded.colors, lockColorsLoaded.colors)
+                colorManager.fetchOptions(
+                    object : CustomizationManager.OptionsFetchedListener<ColorOption?> {
+                        override fun onOptionsLoaded(options: MutableList<ColorOption?>?) {
+                            val wallpaperColorOptions: MutableList<ColorOptionModel> =
+                                mutableListOf()
+                            val presetColorOptions: MutableList<ColorOptionModel> = mutableListOf()
+                            options?.forEach { option ->
+                                when ((option as ColorOptionImpl).type) {
+                                    ColorType.WALLPAPER_COLOR ->
+                                        wallpaperColorOptions.add(option.toModel())
+                                    ColorType.PRESET_COLOR ->
+                                        presetColorOptions.add(option.toModel())
+                                }
+                            }
+                            continuation.resumeWith(
+                                Result.success(
+                                    mapOf(
+                                        ColorType.WALLPAPER_COLOR to wallpaperColorOptions,
+                                        ColorType.PRESET_COLOR to presetColorOptions,
+                                    )
+                                )
+                            )
+                        }
+
+                        override fun onError(throwable: Throwable?) {
+                            Log.e(TAG, "Error loading theme bundles", throwable)
+                            continuation.resumeWith(
+                                Result.failure(
+                                    throwable ?: Throwable("Error loading theme bundles")
+                                )
+                            )
+                        }
+                    },
+                    /* reload= */ false,
+                )
             }
+        }
 
     override suspend fun select(colorOptionModel: ColorOptionModel) {
         _isApplyingSystemColor.value = true
diff --git a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl2.kt b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl2.kt
index 96f9368..c925748 100644
--- a/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl2.kt
+++ b/src/com/android/customization/picker/color/data/repository/ColorPickerRepositoryImpl2.kt
@@ -78,8 +78,7 @@
             .map { (_, colors) -> colors }
 
     override val colorOptions: Flow<Map<ColorType, List<ColorOption>>> =
-        combine(homeWallpaperColors, lockWallpaperColors, ::Pair)
-            .map { (homeColors, lockColors) ->
+        combine(homeWallpaperColors, lockWallpaperColors) { homeColors, lockColors ->
                 suspendCancellableCoroutine { continuation ->
                     colorManager.setWallpaperColors(homeColors, lockColors)
                     colorManager.fetchOptions(
diff --git a/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
index bac650f..c2109ae 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
@@ -42,6 +42,7 @@
 import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter2
 import java.lang.ref.WeakReference
 import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 object ColorsFloatingSheetBinder {
@@ -52,7 +53,8 @@
         colorUpdateViewModel: ColorUpdateViewModel,
         lifecycleOwner: LifecycleOwner,
     ) {
-        val viewModel = optionsViewModel.colorPickerViewModel2
+        val colorsViewModel = optionsViewModel.colorPickerViewModel2
+        val darkModeViewModel = optionsViewModel.darkModeViewModel
         val isFloatingSheetActive = { optionsViewModel.selectedOption.value == COLORS }
 
         ColorUpdateBinder.bind(
@@ -119,7 +121,7 @@
 
         DarkModeBinder.bind(
             darkModeToggle = view.findViewById(R.id.dark_mode_toggle),
-            viewModel = optionsViewModel.darkModeViewModel,
+            viewModel = darkModeViewModel,
             colorUpdateViewModel = colorUpdateViewModel,
             shouldAnimateColor = isFloatingSheetActive,
             lifecycleOwner = lifecycleOwner,
@@ -127,12 +129,12 @@
 
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
-                launch { viewModel.colorTypeTabs.collect { tabAdapter.submitList(it) } }
+                launch { colorsViewModel.colorTypeTabs.collect { tabAdapter.submitList(it) } }
 
-                launch { viewModel.colorTypeTabSubheader.collect { subhead.text = it } }
+                launch { colorsViewModel.colorTypeTabSubheader.collect { subhead.text = it } }
 
                 launch {
-                    viewModel.colorOptions.collect { colorOptions ->
+                    colorsViewModel.colorOptions.collect { colorOptions ->
                         colorsAdapter.setItems(colorOptions) {
                             var indexToFocus = colorOptions.indexOfFirst { it.isSelected.value }
                             indexToFocus = if (indexToFocus < 0) 0 else indexToFocus
@@ -143,14 +145,27 @@
                 }
 
                 launch {
-                    viewModel.previewingColorOption.collect { colorOption ->
-                        if (colorOption != null) {
-                            colorUpdateViewModel.previewColors(
-                                colorOption.seedColor,
-                                colorOption.style,
-                            )
-                        } else colorUpdateViewModel.resetPreview()
-                    }
+                    combine(
+                            colorsViewModel.previewingColorOption,
+                            colorsViewModel.selectedColorOption,
+                            darkModeViewModel.overridingIsDarkMode,
+                            ::Triple,
+                        )
+                        .collect { (previewColor, selectedColor, overridingIsDarkMode) ->
+                            if (previewColor != null || overridingIsDarkMode != null) {
+                                val previewColorOption = previewColor ?: selectedColor
+                                val previewIsDarkMode =
+                                    overridingIsDarkMode
+                                        ?: view.resources.configuration.isNightModeActive
+                                previewColorOption?.let {
+                                    colorUpdateViewModel.previewColors(
+                                        previewColorOption.seedColor,
+                                        previewColorOption.style,
+                                        previewIsDarkMode,
+                                    )
+                                }
+                            } else colorUpdateViewModel.resetPreview()
+                        }
                 }
             }
         }
diff --git a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
index b44b152..9ce7b61 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
@@ -418,7 +418,7 @@
                             clockHostView.removeAllViews()
                             // For new customization picker, we should get views from clocklayout
                             if (Flags.newCustomizationPickerUi()) {
-                                clockViewFactory.getController(clock.clockId).let { clockController
+                                clockViewFactory.getController(clock.clockId)?.let { clockController
                                     ->
                                     val udfpsTop =
                                         clockPickerViewModel.getUdfpsLocation()?.let {
diff --git a/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt b/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
index 9fc59c7..1f2a0f3 100644
--- a/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
+++ b/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
@@ -146,14 +146,14 @@
                             )
                             .collect { (previewingClock, previewingClockSize) ->
                                 val hideSmartspace =
-                                    clockViewFactory.getController(previewingClock.clockId).let {
+                                    clockViewFactory.getController(previewingClock.clockId)?.let {
                                         when (previewingClockSize) {
                                             ClockSize.DYNAMIC ->
                                                 it.largeClock.config.hasCustomWeatherDataDisplay
                                             ClockSize.SMALL ->
                                                 it.smallClock.config.hasCustomWeatherDataDisplay
                                         }
-                                    }
+                                    } ?: false
                                 workspaceCallback.sendMessage(
                                     MESSAGE_ID_HIDE_SMART_SPACE,
                                     Bundle().apply {
diff --git a/tests/robotests/src/com/android/customization/picker/clock/ui/FakeClockViewFactory.kt b/tests/robotests/src/com/android/customization/picker/clock/ui/FakeClockViewFactory.kt
index 418b439..d6286a4 100644
--- a/tests/robotests/src/com/android/customization/picker/clock/ui/FakeClockViewFactory.kt
+++ b/tests/robotests/src/com/android/customization/picker/clock/ui/FakeClockViewFactory.kt
@@ -35,7 +35,7 @@
         override fun dump(pw: PrintWriter) = TODO("Not yet implemented")
     }
 
-    override fun getController(clockId: String): ClockController = clockControllers[clockId]!!
+    override fun getController(clockId: String): ClockController? = clockControllers[clockId]
 
     override fun getLargeView(clockId: String): View {
         TODO("Not yet implemented")