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

Commit dacc5097 authored by Anton Potapov's avatar Anton Potapov
Browse files

Rework ANC button click

Slice view would only be there as an "image" to show the button. Button
composable handles the actual click alongside the a11y stuff

Flag: aconfig new_volume_panel NEXTFOOD
Test: manual on the phone with a compatible headset and voice over. Open
Volume Panel and focus a11y button
Fixes: 338527799
Fixes: 338531016
Fixes: 338532731
Fixes: 338346889

Change-Id: I2053ba8dc5f882922d7fffdf5fe6d396e31d1ebc
parent cdecb49e
Loading
Loading
Loading
Loading
+34 −25
Original line number Diff line number Diff line
@@ -19,10 +19,13 @@ package com.android.systemui.volume.panel.component.anc.ui.composable
import android.view.Gravity
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -33,15 +36,15 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.LiveRegionMode
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.liveRegion
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.android.systemui.res.R
@@ -66,13 +69,6 @@ constructor(
            with(LocalDensity.current) { LocalConfiguration.current.screenWidthDp.dp.toPx() }
        var gravity by remember { mutableIntStateOf(Gravity.CENTER_HORIZONTAL) }
        val isClickable = viewModel.isClickable(slice)
        val onClick =
            if (isClickable) {
                { with(ancPopup) { show(null, gravity) } }
            } else {
                null
            }

        Column(
            modifier =
                modifier.onGloballyPositioned {
@@ -80,21 +76,34 @@ constructor(
                },
            verticalArrangement = Arrangement.spacedBy(12.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            Box(
                modifier = Modifier.height(64.dp),
            ) {
                SliceAndroidView(
                modifier =
                    Modifier.height(64.dp)
                        .fillMaxWidth()
                        .semantics {
                            role = Role.Button
                            contentDescription = label
                        }
                        .clip(RoundedCornerShape(28.dp)),
                    modifier = modifier.fillMaxSize(),
                    slice = slice,
                isEnabled = onClick != null,
                    onWidthChanged = viewModel::onButtonSliceWidthChanged,
                onClick = onClick,
                    enableAccessibility = false,
                )
                Button(
                    modifier =
                        modifier.fillMaxSize().padding(8.dp).semantics {
                            liveRegion = LiveRegionMode.Polite
                            contentDescription = label
                        },
                    enabled = isClickable,
                    onClick = { with(ancPopup) { show(null, gravity) } },
                    colors =
                        ButtonColors(
                            contentColor = Color.Transparent,
                            containerColor = Color.Transparent,
                            disabledContentColor = Color.Transparent,
                            disabledContainerColor = Color.Transparent,
                        )
                ) {}
            }

            Text(
                modifier = Modifier.clearAndSetSemantics {}.basicMarquee(),
                text = label,
+41 −27
Original line number Diff line number Diff line
@@ -16,11 +16,12 @@

package com.android.systemui.volume.panel.component.anc.ui.composable

import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.view.ContextThemeWrapper
import android.view.MotionEvent
import android.view.View
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
@@ -32,14 +33,13 @@ import com.android.systemui.res.R
fun SliceAndroidView(
    slice: Slice?,
    modifier: Modifier = Modifier,
    isEnabled: Boolean = true,
    onWidthChanged: ((Int) -> Unit)? = null,
    onClick: (() -> Unit)? = null,
    enableAccessibility: Boolean = true,
) {
    AndroidView(
        modifier = modifier,
        factory = { context: Context ->
            ClickableSliceView(
            ComposeSliceView(
                    ContextThemeWrapper(context, R.style.Widget_SliceView_VolumePanel),
                )
                .apply {
@@ -47,17 +47,18 @@ fun SliceAndroidView(
                    isScrollable = false
                    importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
                    setShowTitleItems(true)
                    if (onWidthChanged != null) {
                        addOnLayoutChangeListener(OnWidthChangedLayoutListener(onWidthChanged))
                    }
                }
        },
        update = { sliceView: ClickableSliceView ->
        update = { sliceView: ComposeSliceView ->
            sliceView.slice = slice
            sliceView.onClick = onClick
            sliceView.isEnabled = isEnabled
            sliceView.isClickable = isEnabled
        }
            sliceView.layoutListener = onWidthChanged?.let(::OnWidthChangedLayoutListener)
            sliceView.enableAccessibility = enableAccessibility
        },
        onRelease = { sliceView: ComposeSliceView ->
            sliceView.layoutListener = null
            sliceView.slice = null
            sliceView.enableAccessibility = true
        },
    )
}

@@ -83,26 +84,39 @@ class OnWidthChangedLayoutListener(private val widthChanged: (Int) -> Unit) :
    }
}

/**
 * [SliceView] that prioritises [onClick] when its clicked instead of passing the event to the slice
 * first.
 */
@SuppressLint("ViewConstructor") // only used in this class
private class ClickableSliceView(context: Context) : SliceView(context) {
private class ComposeSliceView(context: Context) : SliceView(context) {

    var onClick: (() -> Unit)? = null
    var enableAccessibility: Boolean = true
    var layoutListener: OnLayoutChangeListener? = null
        set(value) {
            field?.let { removeOnLayoutChangeListener(it) }
            field = value
            field?.let { addOnLayoutChangeListener(it) }
        }

    init {
        if (onClick != null) {
            setOnClickListener {}
    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
        if (enableAccessibility) {
            super.onInitializeAccessibilityNodeInfo(info)
        }
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        return (isSliceViewClickable && onClick != null) || super.onInterceptTouchEvent(ev)
    override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) {
        if (enableAccessibility) {
            super.onInitializeAccessibilityEvent(event)
        }
    }

    override fun onClick(v: View?) {
        onClick?.takeIf { isSliceViewClickable }?.let { it() } ?: super.onClick(v)
    override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
        return if (enableAccessibility) {
            super.performAccessibilityAction(action, arguments)
        } else {
            false
        }
    }

    override fun addChildrenForAccessibility(outChildren: ArrayList<View>?) {
        if (enableAccessibility) {
            super.addChildrenForAccessibility(outChildren)
        }
    }
}