Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d095878 authored by Bhavuk Jain's avatar Bhavuk Jain Committed by Android Build Coastguard Worker
Browse files

Enabled a11y for clock carousel view

This CL adds changes in order to announce correct a11y label when the
clock carousel view is in focus by the user in a11y mode.

Bug: b/278649728
Test: Tested by building wallpaper picker on local, turning on talkback
and checking if the announcement for clock carousel is made and custom
actions are available or not
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:5898ba8b69628d56518da733de19c10ecb86f46c)
Merged-In: Ibaaee3ac11f46f066e8ba531f414cb5201ad55e9
Change-Id: Ibaaee3ac11f46f066e8ba531f414cb5201ad55e9
parent 2220ab49
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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
+18 −0
Original line number Diff line number Diff line
@@ -507,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>
+71 −0
Original line number Diff line number Diff line
/*
 * 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)
    }
}
+20 −0
Original line number Diff line number Diff line
@@ -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 {
@@ -71,6 +89,8 @@ object ClockCarouselViewBinder {

                launch {
                    viewModel.selectedIndex.collect { selectedIndex ->
                        carouselAccessibilityDelegate.contentDescriptionOfSelectedClock =
                            carouselView.getContentDescription(selectedIndex)
                        carouselView.setSelectedClockIndex(selectedIndex)
                    }
                }
+25 −1
Original line number Diff line number Diff line
@@ -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
@@ -60,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)
    }

    /**
@@ -69,6 +71,24 @@ 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,
        clocks: List<ClockCarouselItemViewModel>,
@@ -304,6 +324,10 @@ class ClockCarouselView(
        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 clocks.size
        }
@@ -336,7 +360,7 @@ class ClockCarouselView(
            val isMiddleView = isMiddleView(viewRoot.id)

            // Accessibility
            viewRoot.contentDescription = clocks[index].getContentDescription(view.resources)
            viewRoot.contentDescription = getContentDescription(index, view.resources)
            viewRoot.isSelected = isMiddleView

            when (clockSize) {
Loading