summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2023-09-05 18:35:32 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2023-09-05 18:35:32 +0000
commitba9a5307bb6ab7a8790ab35a2df3cb23e260c3f1 (patch)
tree7f2b8592fce48a4878ce293ed417b79acf926612
parente7eb12e4574daf9d6cac4ed5a2b6d4f34fd41f29 (diff)
parent88ba5e005052ac167d9c1c00987fe152b9508905 (diff)
downloadThemePicker-ba9a5307bb6ab7a8790ab35a2df3cb23e260c3f1.tar.gz
Merge "Merge UP1A.230905.019" into aosp-main-future
-rw-r--r--res/layout/clock_carousel.xml15
-rw-r--r--res/values/config.xml4
-rwxr-xr-xres/values/strings.xml21
-rw-r--r--src/com/android/customization/module/CustomizationInjector.kt3
-rw-r--r--src/com/android/customization/module/ThemePickerInjector.kt8
-rw-r--r--src/com/android/customization/picker/clock/ui/binder/CarouselAccessibilityDelegate.kt71
-rw-r--r--src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt34
-rw-r--r--src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt50
-rw-r--r--src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselItemViewModel.kt35
-rw-r--r--src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt16
-rw-r--r--src/com/android/customization/picker/clock/utils/ClockDescriptionUtils.kt28
-rw-r--r--src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt26
-rw-r--r--src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt5
13 files changed, 287 insertions, 29 deletions
diff --git a/res/layout/clock_carousel.xml b/res/layout/clock_carousel.xml
index a43e8044..e93a6971 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/config.xml b/res/values/config.xml
index d992bf3b..ad81e67c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -19,4 +19,8 @@
<item type="id" name="option_tile" />
<!-- ID for the label of an option tile -->
<item type="id" name="option_label" />
+
+ <!-- ID for the a11y actions on carousel -->
+ <item type="id" name="action_scroll_forward" />
+ <item type="id" name="action_scroll_backward" />
</resources> \ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 40142d1f..680947d9 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>
@@ -504,4 +507,22 @@
[CHAR LIMIT=NONE].
-->
<string name="content_description_color_option">Color option <xliff:g name="color_number" example="1">%1$d</xliff:g></string>
+
+ <!--
+ Accessibility label for forward scrolling in the carousel of clock faces.
+ [CHAR LIMIT=128].
+ -->
+ <string name="scroll_forward_and_select">Swipe left to choose a different clock face</string>
+
+ <!--
+ Accessibility label for backward scrolling in the carousel of clock faces.
+ [CHAR LIMIT=128].
+ -->
+ <string name="scroll_backward_and_select">Swipe right to choose a different clock face</string>
+
+ <!--
+ Accessibility label for the carousel of clock faces.
+ [CHAR LIMIT=128].
+ -->
+ <string name="custom_clocks_label">Custom Clocks</string>
</resources>
diff --git a/src/com/android/customization/module/CustomizationInjector.kt b/src/com/android/customization/module/CustomizationInjector.kt
index 8b0d90f1..5f8f9d3c 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.view.ClockViewFactory
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 @@ interface CustomizationInjector : Injector {
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 d00ed28e..cceb8965 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.view.ClockViewFactory
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 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
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 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
.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/CarouselAccessibilityDelegate.kt b/src/com/android/customization/picker/clock/ui/binder/CarouselAccessibilityDelegate.kt
new file mode 100644
index 00000000..5e3c26ee
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/binder/CarouselAccessibilityDelegate.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.binder
+
+import android.content.Context
+import android.os.Bundle
+import android.view.View
+import android.view.accessibility.AccessibilityNodeInfo
+import com.android.wallpaper.R
+
+class CarouselAccessibilityDelegate(
+ private val context: Context,
+ private val scrollForwardCallback: () -> Unit,
+ private val scrollBackwardCallback: () -> Unit
+) : View.AccessibilityDelegate() {
+
+ var contentDescriptionOfSelectedClock = ""
+
+ private val ACTION_SCROLL_BACKWARD = R.id.action_scroll_backward
+ private val ACTION_SCROLL_FORWARD = R.id.action_scroll_forward
+
+ override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ info?.isScrollable = true
+ info?.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ ACTION_SCROLL_FORWARD,
+ context.getString(R.string.scroll_forward_and_select)
+ )
+ )
+ info?.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ ACTION_SCROLL_BACKWARD,
+ context.getString(R.string.scroll_backward_and_select)
+ )
+ )
+ info?.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS)
+ // We need to specifically set the content description since for some reason the talkback
+ // service does not go to children of the clock carousel in the view hierarchy
+ info?.contentDescription = contentDescriptionOfSelectedClock
+ }
+
+ override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
+ when (action) {
+ ACTION_SCROLL_BACKWARD -> {
+ scrollBackwardCallback.invoke()
+ return true
+ }
+ ACTION_SCROLL_FORWARD -> {
+ scrollForwardCallback.invoke()
+ return true
+ }
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+}
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 8f964cf1..89fac894 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -15,6 +15,7 @@
*/
package com.android.customization.picker.clock.ui.binder
+import android.content.Context
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.core.view.isVisible
@@ -27,6 +28,7 @@ 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 com.android.wallpaper.picker.customization.ui.section.ScreenPreviewClickView
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -34,8 +36,10 @@ object ClockCarouselViewBinder {
@JvmStatic
fun bind(
+ context: Context,
carouselView: ClockCarouselView,
singleClockView: ViewGroup,
+ screenPreviewClickView: ScreenPreviewClickView,
viewModel: ClockCarouselViewModel,
clockViewFactory: ClockViewFactory,
lifecycleOwner: LifecycleOwner,
@@ -43,6 +47,20 @@ object ClockCarouselViewBinder {
) {
carouselView.setClockViewFactory(clockViewFactory)
clockViewFactory.updateRegionDarkness()
+ val carouselAccessibilityDelegate =
+ CarouselAccessibilityDelegate(
+ context,
+ scrollForwardCallback = {
+ // Callback code for scrolling forward
+ carouselView.transitionToNext()
+ },
+ scrollBackwardCallback = {
+ // Callback code for scrolling backward
+ carouselView.transitionToPrevious()
+ }
+ )
+ screenPreviewClickView.accessibilityDelegate = carouselAccessibilityDelegate
+
val singleClockHostView =
singleClockView.requireViewById<FrameLayout>(R.id.single_clock_host_view)
lifecycleOwner.lifecycleScope.launch {
@@ -50,25 +68,29 @@ object ClockCarouselViewBinder {
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) }
}
}
launch {
viewModel.selectedIndex.collect { selectedIndex ->
+ carouselAccessibilityDelegate.contentDescriptionOfSelectedClock =
+ carouselView.getContentDescription(selectedIndex)
carouselView.setSelectedClockIndex(selectedIndex)
}
}
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 56d4dea2..8764e541 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -16,6 +16,7 @@
package com.android.customization.picker.clock.ui.view
import android.content.Context
+import android.content.res.Resources
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
@@ -28,6 +29,7 @@ import androidx.core.view.doOnPreDraw
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
@@ -59,6 +61,7 @@ class ClockCarouselView(
val clockCarousel = LayoutInflater.from(context).inflate(R.layout.clock_carousel, this)
carousel = clockCarousel.requireViewById(R.id.carousel)
motionLayout = clockCarousel.requireViewById(R.id.motion_container)
+ motionLayout.contentDescription = context.getString(R.string.custom_clocks_label)
}
/**
@@ -68,17 +71,35 @@ class ClockCarouselView(
clockViewFactory = factory
}
+ fun transitionToNext() {
+ val index = (carousel.currentIndex + 1) % carousel.count
+ if (index < carousel.count && index > 0) {
+ carousel.transitionToIndex(index, 0)
+ }
+ }
+
+ fun transitionToPrevious() {
+ val index = (carousel.currentIndex - 1) % carousel.count
+ if (index < carousel.count && index > 0) {
+ carousel.transitionToIndex(index, 0)
+ }
+ }
+
+ fun getContentDescription(index: Int): String {
+ return adapter.getContentDescription(index, resources)
+ }
+
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 +139,11 @@ class ClockCarouselView(
}
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 +319,17 @@ class ClockCarouselView(
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 {
+ fun getContentDescription(index: Int, resources: Resources): String {
+ return clocks[index].getContentDescription(resources)
+ }
+
override fun count(): Int {
- return clockIds.size
+ return clocks.size
}
override fun populate(view: View?, index: Int) {
@@ -318,7 +343,7 @@ class ClockCarouselView(
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 +358,11 @@ class ClockCarouselView(
clockHostView.addView(clockView)
val isMiddleView = isMiddleView(viewRoot.id)
+
+ // Accessibility
+ viewRoot.contentDescription = getContentDescription(index, view.resources)
+ viewRoot.isSelected = isMiddleView
+
when (clockSize) {
ClockSize.DYNAMIC ->
initializeDynamicClockView(
@@ -411,7 +441,7 @@ class ClockCarouselView(
}
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 00000000..708fa2f9
--- /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 781b48c0..a4f9cc4a 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 @@ constructor(
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 @@ constructor(
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 @@ constructor(
// 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 00000000..9a0b66f1
--- /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 00000000..43f19b39
--- /dev/null
+++ b/src/com/android/customization/picker/clock/utils/ThemePickerClockDescriptionUtils.kt
@@ -0,0 +1,26 @@
+/*
+ * 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
+import com.android.wallpaper.R
+
+class ThemePickerClockDescriptionUtils : ClockDescriptionUtils {
+ @StringRes
+ override fun getDescriptionResId(clockId: String): Int {
+ return R.string.clock_title
+ }
+}
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 43220091..8e917987 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
@@ -46,6 +46,7 @@ import com.android.wallpaper.model.WallpaperPreviewNavigator
import com.android.wallpaper.module.CurrentWallpaperInfoFactory
import com.android.wallpaper.module.CustomizationSections
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
+import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewClickView
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewSectionController
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewView
import com.android.wallpaper.util.DisplayUtils
@@ -100,6 +101,8 @@ class PreviewWithClockCarouselSectionController(
override fun createView(context: Context): ScreenPreviewView {
val view = super.createView(context)
if (screen == CustomizationSections.Screen.LOCK_SCREEN) {
+ val screenPreviewClickView: ScreenPreviewClickView =
+ view.findViewById(R.id.screen_preview_click_view)
val clockColorAndSizeButtonStub: ViewStub =
view.requireViewById(R.id.clock_color_and_size_button)
clockColorAndSizeButtonStub.layoutResource = R.layout.clock_color_and_size_button
@@ -162,8 +165,10 @@ class PreviewWithClockCarouselSectionController(
bindJob =
lifecycleOwner.lifecycleScope.launch {
ClockCarouselViewBinder.bind(
+ context = context,
carouselView = carouselView,
singleClockView = singleClockView,
+ screenPreviewClickView = screenPreviewClickView,
viewModel = viewModel,
clockViewFactory = clockViewFactory,
lifecycleOwner = lifecycleOwner,