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

Commit 84898a55 authored by Coco Duan's avatar Coco Duan
Browse files

Support selecting widget with TalkBack in edit mode

Set a11y semantics on the WidgetContent composable to enable focusing
and tapping to select widget. Widget label is announced when focused.
Since the widget and its descendants are non-interactive in edit mode,
setting a11y importance on the AndroidView will not set
:accessibilityFocus on the node in the a11y node tree.

Additionally, ignore a11y semantics of the HighlightedItem so the
widget configurator icon remains tappable.

Bug: b/337251820
Test: manual with TalkBack
Flag: ACONFIG com.android.systemui.communal_hub TEAMFOOD
Change-Id: I742926b13926287a203d1610a3567945f2f72596
parent 2172fdc7
Loading
Loading
Loading
Loading
+22 −8
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.style.TextAlign
@@ -841,19 +842,31 @@ private fun WidgetContent(
    widgetConfigurator: WidgetConfigurator?,
    modifier: Modifier = Modifier,
) {
    val context = LocalContext.current
    val isFocusable by viewModel.isFocusable.collectAsState(initial = false)

    val accessibilityLabel =
        remember(model, context) {
            model.providerInfo.loadLabel(context.packageManager).toString().trim()
        }
    val clickActionLabel = stringResource(R.string.accessibility_action_label_select_widget)
    Box(
        modifier =
            modifier.thenIf(!viewModel.isEditMode && model.inQuietMode) {
            modifier
                .thenIf(!viewModel.isEditMode && model.inQuietMode) {
                    Modifier.pointerInput(Unit) {
                    // consume tap to prevent the child view from triggering interactions with the
                    // app widget
                        // consume tap to prevent the child view from triggering interactions with
                        // the app widget
                        observeTaps(shouldConsume = true) { _ ->
                            viewModel.onOpenEnableWorkProfileDialog()
                        }
                    }
                }
                .thenIf(viewModel.isEditMode) {
                    Modifier.semantics {
                        contentDescription = accessibilityLabel
                        onClick(label = clickActionLabel, action = null)
                    }
                }
    ) {
        AndroidView(
            modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
@@ -865,6 +878,7 @@ private fun WidgetContent(
                        // Remove the extra padding applied to AppWidgetHostView to allow widgets to
                        // occupy the entire box.
                        setPadding(0)
                        accessibilityDelegate = viewModel.widgetAccessibilityDelegate
                    }
            },
            update = {
+4 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.toOffset
import androidx.compose.ui.unit.toSize
@@ -274,6 +275,9 @@ fun LazyGridItemScope.DraggableItem(
        AnimatedVisibility(
            modifier =
                Modifier.matchParentSize()
                    // Avoid taking focus away from the content when using explore-by-touch with
                    // accessibility tools.
                    .clearAndSetSemantics {}
                    // Do not consume motion events in the highlighted item and pass them down to
                    // the content.
                    .pointerInteropFilter { false },
+1 −0
Original line number Diff line number Diff line
@@ -140,6 +140,7 @@ class CommunalViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
        underTest =
            CommunalViewModel(
                testScope,
                context.resources,
                kosmos.keyguardTransitionInteractor,
                kosmos.communalInteractor,
                kosmos.communalTutorialInteractor,
+2 −0
Original line number Diff line number Diff line
@@ -1182,6 +1182,8 @@
    <string name="accessibility_action_label_edit_widgets">Customize widgets</string>
    <!-- Accessibility content description for communal hub. [CHAR LIMIT=NONE] -->
    <string name="accessibility_content_description_for_communal_hub">Widgets on lock screen</string>
    <!-- Label for accessibility action to select a widget in edit mode. [CHAR LIMIT=NONE] -->
    <string name="accessibility_action_label_select_widget">select widget</string>

    <!-- Related to user switcher --><skip/>

+5 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.communal.ui.viewmodel

import android.content.ComponentName
import android.os.UserHandle
import android.view.View
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
@@ -37,7 +38,7 @@ abstract class BaseCommunalViewModel(
) {
    val currentScene: Flow<SceneKey> = communalInteractor.desiredScene

    /** Whether communal hub can be focused by accessibility tools. */
    /** Whether communal hub should be focused by accessibility tools. */
    open val isFocusable: Flow<Boolean> = MutableStateFlow(false)

    /** Whether widgets are currently being re-ordered. */
@@ -49,6 +50,9 @@ abstract class BaseCommunalViewModel(
    val selectedKey: StateFlow<String?>
        get() = _selectedKey

    /** Accessibility delegate to be set on CommunalAppWidgetHostView. */
    open val widgetAccessibilityDelegate: View.AccessibilityDelegate? = null

    fun signalUserInteraction() {
        communalInteractor.signalUserInteraction()
    }
Loading