Merge "Import translations. DO NOT MERGE ANYWHERE" into udc-qpr-dev
diff --git a/res/layout/clock_carousel.xml b/res/layout/clock_carousel.xml
index a43e804..e93a697 100644
--- a/res/layout/clock_carousel.xml
+++ b/res/layout/clock_carousel.xml
@@ -43,7 +43,8 @@
             android:layout_width="@dimen/screen_preview_width"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
-            android:clipChildren="false">
+            android:clipChildren="false"
+            android:importantForAccessibility="noHideDescendants">
             <com.android.customization.picker.clock.ui.view.ClockHostView
                 android:id="@+id/clock_host_view_0"
                 android:layout_width="match_parent"
@@ -74,7 +75,8 @@
             android:layout_width="@dimen/screen_preview_width"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
-            android:clipChildren="false">
+            android:clipChildren="false"
+            android:importantForAccessibility="noHideDescendants">
             <com.android.customization.picker.clock.ui.view.ClockHostView
                 android:id="@+id/clock_host_view_1"
                 android:layout_width="match_parent"
@@ -107,7 +109,8 @@
             android:layout_width="@dimen/screen_preview_width"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
-            android:clipChildren="false">
+            android:clipChildren="false"
+            android:importantForAccessibility="noHideDescendants">
             <com.android.customization.picker.clock.ui.view.ClockHostView
                 android:id="@+id/clock_host_view_2"
                 android:layout_width="match_parent"
@@ -138,7 +141,8 @@
             android:layout_width="@dimen/screen_preview_width"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
-            android:clipChildren="false">
+            android:clipChildren="false"
+            android:importantForAccessibility="noHideDescendants">
             <com.android.customization.picker.clock.ui.view.ClockHostView
                 android:id="@+id/clock_host_view_3"
                 android:layout_width="match_parent"
@@ -169,7 +173,8 @@
             android:layout_width="@dimen/screen_preview_width"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
-            android:clipChildren="false">
+            android:clipChildren="false"
+            android:importantForAccessibility="noHideDescendants">
             <com.android.customization.picker.clock.ui.view.ClockHostView
                 android:id="@+id/clock_host_view_4"
                 android:layout_width="match_parent"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 40142d1..b7574a3 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -33,6 +33,9 @@
     <!-- The content description of clock entry. [CHAR LIMIT=NONE] -->
     <string name="clock_picker_entry_content_description">Change a custom clock</string>
 
+    <!-- action description for announcing selected Clock [CHAR LIMIT=NONE]-->
+    <string name="select_clock_action_description">Clock face option <xliff:g name="clock_face_description">%1$s</xliff:g></string>
+
     <!-- Title of a section of the customization picker where the user can configure Clock face. [CHAR LIMIT=19] -->
     <string name="clock_settings_title">Clock color &amp; size</string>
 
diff --git a/src/com/android/customization/module/CustomizationInjector.kt b/src/com/android/customization/module/CustomizationInjector.kt
index 8b0d90f..5f8f9d3 100644
--- a/src/com/android/customization/module/CustomizationInjector.kt
+++ b/src/com/android/customization/module/CustomizationInjector.kt
@@ -26,6 +26,7 @@
 import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
 import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
 import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel
+import com.android.customization.picker.clock.utils.ClockDescriptionUtils
 import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
 import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel
 import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
@@ -76,4 +77,6 @@
         wallpaperColorsViewModel: WallpaperColorsViewModel,
         clockViewFactory: ClockViewFactory,
     ): ClockSettingsViewModel.Factory
+
+    fun getClockDescriptionUtils(): ClockDescriptionUtils
 }
diff --git a/src/com/android/customization/module/ThemePickerInjector.kt b/src/com/android/customization/module/ThemePickerInjector.kt
index d00ed28..cceb896 100644
--- a/src/com/android/customization/module/ThemePickerInjector.kt
+++ b/src/com/android/customization/module/ThemePickerInjector.kt
@@ -51,6 +51,8 @@
 import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
 import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
 import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel
+import com.android.customization.picker.clock.utils.ClockDescriptionUtils
+import com.android.customization.picker.clock.utils.ThemePickerClockDescriptionUtils
 import com.android.customization.picker.color.data.repository.ColorPickerRepositoryImpl
 import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
 import com.android.customization.picker.color.domain.interactor.ColorPickerSnapshotRestorer
@@ -115,6 +117,7 @@
     private var themedIconSnapshotRestorer: ThemedIconSnapshotRestorer? = null
     private var themedIconInteractor: ThemedIconInteractor? = null
     private var clockSettingsViewModelFactory: ClockSettingsViewModel.Factory? = null
+    private var clockDescriptionUtils: ClockDescriptionUtils? = null
     private var gridInteractor: GridInteractor? = null
     private var gridSnapshotRestorer: GridSnapshotRestorer? = null
     private var gridScreenViewModelFactory: GridScreenViewModel.Factory? = null
@@ -534,6 +537,11 @@
                 .also { clockSettingsViewModelFactory = it }
     }
 
+    override fun getClockDescriptionUtils(): ClockDescriptionUtils {
+        return clockDescriptionUtils
+            ?: ThemePickerClockDescriptionUtils().also { clockDescriptionUtils = it }
+    }
+
     fun getGridScreenViewModelFactory(
         context: Context,
     ): ViewModelProvider.Factory {
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 8f964cf..13c80c8 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -50,20 +50,22 @@
                 launch { viewModel.isCarouselVisible.collect { carouselView.isVisible = it } }
 
                 launch {
-                    combine(viewModel.selectedClockSize, viewModel.allClockIds, ::Pair).collect {
-                        (size, allClockIds) ->
+                    combine(viewModel.selectedClockSize, viewModel.allClocks, ::Pair).collect {
+                        (size, allClocks) ->
                         carouselView.setUpClockCarouselView(
                             clockSize = size,
-                            clockIds = allClockIds,
-                            onClockSelected = { clockId -> viewModel.setSelectedClock(clockId) },
+                            clocks = allClocks,
+                            onClockSelected = { clock ->
+                                viewModel.setSelectedClock(clock.clockId)
+                            },
                             isTwoPaneAndSmallWidth = isTwoPaneAndSmallWidth,
                         )
                     }
                 }
 
                 launch {
-                    viewModel.allClockIds.collect {
-                        it.forEach { clockId -> clockViewFactory.updateTimeFormat(clockId) }
+                    viewModel.allClocks.collect {
+                        it.forEach { clock -> clockViewFactory.updateTimeFormat(clock.clockId) }
                     }
                 }
 
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 56d4dea..73d5508 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -28,6 +28,7 @@
 import androidx.core.view.get
 import androidx.core.view.isNotEmpty
 import com.android.customization.picker.clock.shared.ClockSize
+import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselItemViewModel
 import com.android.systemui.plugins.ClockController
 import com.android.wallpaper.R
 import com.android.wallpaper.picker.FixedWidthDisplayRatioFrameLayout
@@ -70,15 +71,15 @@
 
     fun setUpClockCarouselView(
         clockSize: ClockSize,
-        clockIds: List<String>,
-        onClockSelected: (clockId: String) -> Unit,
+        clocks: List<ClockCarouselItemViewModel>,
+        onClockSelected: (clock: ClockCarouselItemViewModel) -> Unit,
         isTwoPaneAndSmallWidth: Boolean,
     ) {
         if (isTwoPaneAndSmallWidth) {
             overrideScreenPreviewWidth()
         }
 
-        adapter = ClockCarouselAdapter(clockSize, clockIds, clockViewFactory, onClockSelected)
+        adapter = ClockCarouselAdapter(clockSize, clocks, clockViewFactory, onClockSelected)
         carousel.setAdapter(adapter)
         carousel.refresh()
         motionLayout.setTransitionListener(
@@ -118,11 +119,11 @@
                 }
 
                 private fun prepareDynamicClockView(motionLayout: MotionLayout, endId: Int) {
-                    val scalingDownClockId = adapter.clockIds[carousel.currentIndex]
+                    val scalingDownClockId = adapter.clocks[carousel.currentIndex].clockId
                     val scalingUpIdx =
                         if (endId == R.id.next) (carousel.currentIndex + 1) % adapter.count()
                         else (carousel.currentIndex - 1 + adapter.count()) % adapter.count()
-                    val scalingUpClockId = adapter.clockIds[scalingUpIdx]
+                    val scalingUpClockId = adapter.clocks[scalingUpIdx].clockId
                     offCenterClockController = clockViewFactory.getController(scalingDownClockId)
                     toCenterClockController = clockViewFactory.getController(scalingUpClockId)
                     offCenterClockScaleView = motionLayout.findViewById(R.id.clock_scale_view_2)
@@ -298,13 +299,13 @@
 
     private class ClockCarouselAdapter(
         val clockSize: ClockSize,
-        val clockIds: List<String>,
+        val clocks: List<ClockCarouselItemViewModel>,
         private val clockViewFactory: ClockViewFactory,
-        private val onClockSelected: (clockId: String) -> Unit
+        private val onClockSelected: (clock: ClockCarouselItemViewModel) -> Unit
     ) : Carousel.Adapter {
 
         override fun count(): Int {
-            return clockIds.size
+            return clocks.size
         }
 
         override fun populate(view: View?, index: Int) {
@@ -318,7 +319,7 @@
             val clockHostView =
                 getClockHostViewId(viewRoot.id)?.let { viewRoot.findViewById(it) as? ClockHostView }
                     ?: return
-            val clockId = clockIds[index]
+            val clockId = clocks[index].clockId
 
             // Add the clock view to the cloc host view
             clockHostView.removeAllViews()
@@ -333,6 +334,11 @@
             clockHostView.addView(clockView)
 
             val isMiddleView = isMiddleView(viewRoot.id)
+
+            // Accessibility
+            viewRoot.contentDescription = clocks[index].getContentDescription(view.resources)
+            viewRoot.isSelected = isMiddleView
+
             when (clockSize) {
                 ClockSize.DYNAMIC ->
                     initializeDynamicClockView(
@@ -411,7 +417,7 @@
         }
 
         override fun onNewItem(index: Int) {
-            onClockSelected.invoke(clockIds[index])
+            onClockSelected.invoke(clocks[index])
         }
     }
 
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt
new file mode 100644
index 0000000..708fa2f
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.content.res.Resources
+import com.android.customization.module.CustomizationInjector
+import com.android.wallpaper.R
+import com.android.wallpaper.module.InjectorProvider
+
+class ClockCarouselItemViewModel(val clockId: String) {
+
+    /** Description for accessibility purposes when a clock is selected. */
+    fun getContentDescription(resources: Resources): String {
+        val clockContent =
+            (InjectorProvider.getInjector() as? CustomizationInjector)
+                ?.getClockDescriptionUtils()
+                ?.getDescriptionResId(clockId)
+                ?.let { resources.getString(it) }
+                ?: ""
+        return resources.getString(R.string.select_clock_action_description, clockContent)
+    }
+}
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 781b48c..a4f9cc4 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
@@ -45,12 +45,12 @@
     private val backgroundDispatcher: CoroutineDispatcher,
 ) : ViewModel() {
     @OptIn(ExperimentalCoroutinesApi::class)
-    val allClockIds: StateFlow<List<String>> =
+    val allClocks: StateFlow<List<ClockCarouselItemViewModel>> =
         interactor.allClocks
             .mapLatest { allClocks ->
                 // Delay to avoid the case that the full list of clocks is not initiated.
                 delay(CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
-                allClocks.map { it.clockId }
+                allClocks.map { ClockCarouselItemViewModel(it.clockId) }
             }
             .stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
 
@@ -58,14 +58,14 @@
 
     val seedColor: Flow<Int?> = interactor.seedColor
 
-    val isCarouselVisible: Flow<Boolean> = allClockIds.map { it.size > 1 }.distinctUntilChanged()
+    val isCarouselVisible: Flow<Boolean> = allClocks.map { it.size > 1 }.distinctUntilChanged()
 
     @OptIn(ExperimentalCoroutinesApi::class)
     val selectedIndex: Flow<Int> =
-        allClockIds
+        allClocks
             .flatMapLatest { allClockIds ->
                 interactor.selectedClockId.map { selectedClockId ->
-                    val index = allClockIds.indexOf(selectedClockId)
+                    val index = allClockIds.indexOfFirst { it.clockId == selectedClockId }
                     /** Making sure there is no active [setSelectedClockJob] */
                     val isSetClockIdJobActive = setSelectedClockJob?.isActive == true
                     if (index >= 0 && !isSetClockIdJobActive) {
@@ -79,11 +79,11 @@
 
     // Handle the case when there is only one clock in the carousel
     val isSingleClockViewVisible: Flow<Boolean> =
-        allClockIds.map { it.size == 1 }.distinctUntilChanged()
+        allClocks.map { it.size == 1 }.distinctUntilChanged()
 
     val clockId: Flow<String> =
-        allClockIds
-            .map { allClockIds -> if (allClockIds.size == 1) allClockIds[0] else null }
+        allClocks
+            .map { allClockIds -> if (allClockIds.size == 1) allClockIds[0].clockId else null }
             .mapNotNull { it }
 
     private var setSelectedClockJob: Job? = null
diff --git a/src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt b/src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt
new file mode 100644
index 0000000..9a0b66f
--- /dev/null
+++ b/src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.utils
+
+import androidx.annotation.StringRes
+
+/** Provides clock description for accessibility purposes. */
+interface ClockDescriptionUtils {
+
+    /**
+     * TODO (b/287507746) : Migrate description res ID to system UI or a shared library, instead of
+     * preserving the clock description at the Wallpaper Picker side.
+     */
+    @StringRes fun getDescriptionResId(clockId: String): Int
+}
diff --git a/src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt b/src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt
new file mode 100644
index 0000000..6b3b405
--- /dev/null
+++ b/src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.utils
+
+import androidx.annotation.StringRes
+
+class ThemePickerClockDescriptionUtils : ClockDescriptionUtils {
+    @StringRes
+    override fun getDescriptionResId(clockId: String): Int {
+        return -1
+    }
+}