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

Commit 4d504ec8 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "a11y for controls management" into rvc-dev

parents 4ba34ed6 b3c393f7
Loading
Loading
Loading
Loading
+14 −0
Original line number Original line Diff line number Diff line
@@ -2698,6 +2698,20 @@
        <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> controls added.</item>
        <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> controls added.</item>
    </plurals>
    </plurals>


    <!-- Removed control in management screen [CHAR LIMIT=20] -->
    <string name="controls_removed">Removed</string>

    <!-- a11y state description for a control that is currently favorited [CHAR LIMIT=NONE] -->
    <string name="accessibility_control_favorite">Favorited</string>
    <!-- a11y state description for a control that is currently favorited with its position [CHAR LIMIT=NONE] -->
    <string name="accessibility_control_favorite_position">Favorited, position <xliff:g id="number" example="1">%d</xliff:g></string>
    <!-- a11y state description for a control that is currently not favorited [CHAR LIMIT=NONE] -->
    <string name="accessibility_control_not_favorite">Unfavorited</string>
    <!-- a11y action to favorite a control. It will read as "Double-tap to favorite" in screen readers [CHAR LIMIT=NONE] -->
    <string name="accessibility_control_change_favorite">favorite</string>
    <!-- a11y action to unfavorite a control. It will read as "Double-tap to unfavorite" in screen readers [CHAR LIMIT=NONE] -->
    <string name="accessibility_control_change_unfavorite">unfavorite</string>

    <!-- Controls management controls screen default title [CHAR LIMIT=30] -->
    <!-- Controls management controls screen default title [CHAR LIMIT=30] -->
    <string name="controls_favorite_default_title">Controls</string>
    <string name="controls_favorite_default_title">Controls</string>
    <!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
    <!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
+71 −4
Original line number Original line Diff line number Diff line
@@ -23,9 +23,14 @@ import android.service.controls.DeviceTypes
import android.view.LayoutInflater
import android.view.LayoutInflater
import android.view.View
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.CheckBox
import android.widget.CheckBox
import android.widget.ImageView
import android.widget.ImageView
import android.widget.Switch
import android.widget.TextView
import android.widget.TextView
import androidx.core.view.AccessibilityDelegateCompat
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R
import com.android.systemui.R
@@ -72,7 +77,8 @@ class ControlAdapter(
                        elevation = this@ControlAdapter.elevation
                        elevation = this@ControlAdapter.elevation
                        background = parent.context.getDrawable(
                        background = parent.context.getDrawable(
                                R.drawable.control_background_ripple)
                                R.drawable.control_background_ripple)
                    }
                    },
                    model is FavoritesModel // Indicates that position information is needed
                ) { id, favorite ->
                ) { id, favorite ->
                    model?.changeFavoriteStatus(id, favorite)
                    model?.changeFavoriteStatus(id, favorite)
                }
                }
@@ -175,8 +181,14 @@ private class ZoneHolder(view: View) : Holder(view) {
 */
 */
internal class ControlHolder(
internal class ControlHolder(
    view: View,
    view: View,
    val withPosition: Boolean,
    val favoriteCallback: ModelFavoriteChanger
    val favoriteCallback: ModelFavoriteChanger
) : Holder(view) {
) : Holder(view) {
    private val favoriteStateDescription =
        itemView.context.getString(R.string.accessibility_control_favorite)
    private val notFavoriteStateDescription =
        itemView.context.getString(R.string.accessibility_control_not_favorite)

    private val icon: ImageView = itemView.requireViewById(R.id.icon)
    private val icon: ImageView = itemView.requireViewById(R.id.icon)
    private val title: TextView = itemView.requireViewById(R.id.title)
    private val title: TextView = itemView.requireViewById(R.id.title)
    private val subtitle: TextView = itemView.requireViewById(R.id.subtitle)
    private val subtitle: TextView = itemView.requireViewById(R.id.subtitle)
@@ -185,15 +197,38 @@ internal class ControlHolder(
        visibility = View.VISIBLE
        visibility = View.VISIBLE
    }
    }


    private val accessibilityDelegate = ControlHolderAccessibilityDelegate(this::stateDescription)

    init {
        ViewCompat.setAccessibilityDelegate(itemView, accessibilityDelegate)
    }

    // Determine the stateDescription based on favorite state and maybe position
    private fun stateDescription(favorite: Boolean): CharSequence? {
        if (!favorite) {
            return notFavoriteStateDescription
        } else if (!withPosition) {
            return favoriteStateDescription
        } else {
            val position = layoutPosition + 1
            return itemView.context.getString(
                R.string.accessibility_control_favorite_position, position)
        }
    }

    override fun bindData(wrapper: ElementWrapper) {
    override fun bindData(wrapper: ElementWrapper) {
        wrapper as ControlInterface
        wrapper as ControlInterface
        val renderInfo = getRenderInfo(wrapper.component, wrapper.deviceType)
        val renderInfo = getRenderInfo(wrapper.component, wrapper.deviceType)
        title.text = wrapper.title
        title.text = wrapper.title
        subtitle.text = wrapper.subtitle
        subtitle.text = wrapper.subtitle
        favorite.isChecked = wrapper.favorite
        updateFavorite(wrapper.favorite)
        removed.text = if (wrapper.removed) "Removed" else ""
        removed.text = if (wrapper.removed) {
            itemView.context.getText(R.string.controls_removed)
        } else {
            ""
        }
        itemView.setOnClickListener {
        itemView.setOnClickListener {
            favorite.isChecked = !favorite.isChecked
            updateFavorite(!favorite.isChecked)
            favoriteCallback(wrapper.controlId, favorite.isChecked)
            favoriteCallback(wrapper.controlId, favorite.isChecked)
        }
        }
        applyRenderInfo(renderInfo)
        applyRenderInfo(renderInfo)
@@ -201,6 +236,8 @@ internal class ControlHolder(


    override fun updateFavorite(favorite: Boolean) {
    override fun updateFavorite(favorite: Boolean) {
        this.favorite.isChecked = favorite
        this.favorite.isChecked = favorite
        accessibilityDelegate.isFavorite = favorite
        itemView.stateDescription = stateDescription(favorite)
    }
    }


    private fun getRenderInfo(
    private fun getRenderInfo(
@@ -219,6 +256,36 @@ internal class ControlHolder(
    }
    }
}
}


private class ControlHolderAccessibilityDelegate(
    val stateRetriever: (Boolean) -> CharSequence?
) : AccessibilityDelegateCompat() {

    var isFavorite = false

    override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
        super.onInitializeAccessibilityNodeInfo(host, info)

        // Change the text for the double-tap action
        val clickActionString = if (isFavorite) {
            host.context.getString(R.string.accessibility_control_change_unfavorite)
        } else {
            host.context.getString(R.string.accessibility_control_change_favorite)
        }
        val click = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
            AccessibilityNodeInfo.ACTION_CLICK,
            // “favorite/unfavorite”
            clickActionString)
        info.addAction(click)

        // Determine the stateDescription based on the holder information
        info.stateDescription = stateRetriever(isFavorite)
        // Remove the information at the end indicating row and column.
        info.setCollectionItemInfo(null)

        info.className = Switch::class.java.name
    }
}

class MarginItemDecorator(
class MarginItemDecorator(
    private val topMargin: Int,
    private val topMargin: Int,
    private val sideMargins: Int
    private val sideMargins: Int
+12 −1
Original line number Original line Diff line number Diff line
@@ -191,7 +191,18 @@ class ControlsEditingActivity @Inject constructor(


        recyclerView.apply {
        recyclerView.apply {
            this.adapter = adapter
            this.adapter = adapter
            layoutManager = GridLayoutManager(recyclerView.context, 2).apply {
            layoutManager = object : GridLayoutManager(recyclerView.context, 2) {

                // This will remove from the announcement the row corresponding to the divider,
                // as it's not something that should be announced.
                override fun getRowCountForAccessibility(
                    recycler: RecyclerView.Recycler,
                    state: RecyclerView.State
                ): Int {
                    val initial = super.getRowCountForAccessibility(recycler, state)
                    return if (initial > 0) initial - 1 else initial
                }
            }.apply {
                spanSizeLookup = adapter.spanSizeLookup
                spanSizeLookup = adapter.spanSizeLookup
            }
            }
            addItemDecoration(itemDecorator)
            addItemDecoration(itemDecorator)