summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2020-05-18 13:13:54 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2020-05-18 13:13:54 +0000
commitdfc677f70776fec98bf1184d8ac1cb067a79f516 (patch)
tree413e691fc49b11e264eafbfddf90de7edf80c7f5
parent28e8a2c0ca7bbb75e7d7755b6aa8a54169090cb7 (diff)
parent93a6975a9eab50ed52349f39301d4140e1417cc7 (diff)
Merge "a11y for controls rearranging" into rvc-dev
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/res/values/ids.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt96
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt31
7 files changed, 155 insertions, 10 deletions
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e670f1f83a52..4510b87556c7 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -685,6 +685,7 @@
</activity>
<activity android:name=".controls.management.ControlsEditingActivity"
+ android:label="@string/controls_menu_edit"
android:theme="@style/Theme.ControlsManagement"
android:excludeFromRecents="true"
android:noHistory="true"
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 76ca385bd9d9..09918e764140 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -169,5 +169,8 @@
<item type="id" name="screen_recording_options" />
<item type="id" name="screen_recording_dialog_source_text" />
<item type="id" name="screen_recording_dialog_source_description" />
+
+ <item type="id" name="accessibility_action_controls_move_before" />
+ <item type="id" name="accessibility_action_controls_move_after" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8c10f61db7a0..8bbcfa0e7898 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2711,6 +2711,8 @@
<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>
+ <!-- a11y action to move a control to the position specified by the parameter [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_move">Move to position <xliff:g id="number" example="1">%d</xliff:g></string>
<!-- Controls management controls screen default title [CHAR LIMIT=30] -->
<string name="controls_favorite_default_title">Controls</string>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
index 175ed061c714..00a406e4dbc0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
@@ -48,6 +48,8 @@ class AllModel(
private var modified = false
+ override val moveHelper = null
+
override val favorites: List<ControlInfo>
get() = favoriteIds.mapNotNull { id ->
val control = controls.firstOrNull { it.control.controlId == id }?.control
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 4b283d607bb8..2f917107cc23 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -18,6 +18,7 @@ package com.android.systemui.controls.management
import android.content.ComponentName
import android.graphics.Rect
+import android.os.Bundle
import android.service.controls.Control
import android.service.controls.DeviceTypes
import android.view.LayoutInflater
@@ -78,7 +79,7 @@ class ControlAdapter(
background = parent.context.getDrawable(
R.drawable.control_background_ripple)
},
- model is FavoritesModel // Indicates that position information is needed
+ model?.moveHelper // Indicates that position information is needed
) { id, favorite ->
model?.changeFavoriteStatus(id, favorite)
}
@@ -176,12 +177,14 @@ private class ZoneHolder(view: View) : Holder(view) {
/**
* Holder for using with [ControlStatusWrapper] to display names of zones.
+ * @param moveHelper a helper interface to facilitate a11y rearranging. Null indicates no
+ * rearranging
* @param favoriteCallback this callback will be called whenever the favorite state of the
* [Control] this view represents changes.
*/
internal class ControlHolder(
view: View,
- val withPosition: Boolean,
+ val moveHelper: ControlsModel.MoveHelper?,
val favoriteCallback: ModelFavoriteChanger
) : Holder(view) {
private val favoriteStateDescription =
@@ -197,7 +200,11 @@ internal class ControlHolder(
visibility = View.VISIBLE
}
- private val accessibilityDelegate = ControlHolderAccessibilityDelegate(this::stateDescription)
+ private val accessibilityDelegate = ControlHolderAccessibilityDelegate(
+ this::stateDescription,
+ this::getLayoutPosition,
+ moveHelper
+ )
init {
ViewCompat.setAccessibilityDelegate(itemView, accessibilityDelegate)
@@ -207,7 +214,7 @@ internal class ControlHolder(
private fun stateDescription(favorite: Boolean): CharSequence? {
if (!favorite) {
return notFavoriteStateDescription
- } else if (!withPosition) {
+ } else if (moveHelper == null) {
return favoriteStateDescription
} else {
val position = layoutPosition + 1
@@ -256,15 +263,67 @@ internal class ControlHolder(
}
}
+/**
+ * Accessibility delegate for [ControlHolder].
+ *
+ * Provides the following functionality:
+ * * Sets the state description indicating whether the controls is Favorited or Unfavorited
+ * * Adds the position to the state description if necessary.
+ * * Adds context action for moving (rearranging) a control.
+ *
+ * @param stateRetriever function to determine the state description based on the favorite state
+ * @param positionRetriever function to obtain the position of this control. It only has to be
+ * correct in controls that are currently favorites (and therefore can
+ * be moved).
+ * @param moveHelper helper interface to determine if a control can be moved and actually move it.
+ */
private class ControlHolderAccessibilityDelegate(
- val stateRetriever: (Boolean) -> CharSequence?
+ val stateRetriever: (Boolean) -> CharSequence?,
+ val positionRetriever: () -> Int,
+ val moveHelper: ControlsModel.MoveHelper?
) : AccessibilityDelegateCompat() {
var isFavorite = false
+ companion object {
+ private val MOVE_BEFORE_ID = R.id.accessibility_action_controls_move_before
+ private val MOVE_AFTER_ID = R.id.accessibility_action_controls_move_after
+ }
+
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
+ info.isContextClickable = false
+ addClickAction(host, info)
+ maybeAddMoveBeforeAction(host, info)
+ maybeAddMoveAfterAction(host, info)
+
+ // 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
+ }
+
+ override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
+ if (super.performAccessibilityAction(host, action, args)) {
+ return true
+ }
+ return when (action) {
+ MOVE_BEFORE_ID -> {
+ moveHelper?.moveBefore(positionRetriever())
+ true
+ }
+ MOVE_AFTER_ID -> {
+ moveHelper?.moveAfter(positionRetriever())
+ true
+ }
+ else -> false
+ }
+ }
+
+ private fun addClickAction(host: View, info: AccessibilityNodeInfoCompat) {
// Change the text for the double-tap action
val clickActionString = if (isFavorite) {
host.context.getString(R.string.accessibility_control_change_unfavorite)
@@ -276,13 +335,30 @@ private class ControlHolderAccessibilityDelegate(
// “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)
+ private fun maybeAddMoveBeforeAction(host: View, info: AccessibilityNodeInfoCompat) {
+ if (moveHelper?.canMoveBefore(positionRetriever()) ?: false) {
+ val newPosition = positionRetriever() + 1 - 1
+ val moveBefore = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ MOVE_BEFORE_ID,
+ host.context.getString(R.string.accessibility_control_move, newPosition)
+ )
+ info.addAction(moveBefore)
+ info.isContextClickable = true
+ }
+ }
- info.className = Switch::class.java.name
+ private fun maybeAddMoveAfterAction(host: View, info: AccessibilityNodeInfoCompat) {
+ if (moveHelper?.canMoveAfter(positionRetriever()) ?: false) {
+ val newPosition = positionRetriever() + 1 + 1
+ val moveAfter = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ MOVE_AFTER_ID,
+ host.context.getString(R.string.accessibility_control_move, newPosition)
+ )
+ info.addAction(moveAfter)
+ info.isContextClickable = true
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
index 37b6d15c0afe..254395368bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
@@ -42,6 +42,8 @@ interface ControlsModel {
*/
val elements: List<ElementWrapper>
+ val moveHelper: MoveHelper?
+
/**
* Change the favorite status of a particular control.
*/
@@ -69,6 +71,34 @@ interface ControlsModel {
*/
fun onFirstChange()
}
+
+ /**
+ * Interface to facilitate moving controls from an [AccessibilityDelegate].
+ *
+ * All positions should be 0 based.
+ */
+ interface MoveHelper {
+
+ /**
+ * Whether the control in `position` can be moved to the position before it.
+ */
+ fun canMoveBefore(position: Int): Boolean
+
+ /**
+ * Whether the control in `position` can be moved to the position after it.
+ */
+ fun canMoveAfter(position: Int): Boolean
+
+ /**
+ * Move the control in `position` to the position before it.
+ */
+ fun moveBefore(position: Int)
+
+ /**
+ * Move the control in `position` to the position after it.
+ */
+ fun moveAfter(position: Int)
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
index 411170cb322c..524250134e9b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.controls.management
import android.content.ComponentName
+import android.util.Log
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.controls.ControlInterface
@@ -39,9 +40,39 @@ class FavoritesModel(
private val favoritesModelCallback: FavoritesModelCallback
) : ControlsModel {
+ companion object {
+ private const val TAG = "FavoritesModel"
+ }
+
private var adapter: RecyclerView.Adapter<*>? = null
private var modified = false
+ override val moveHelper = object : ControlsModel.MoveHelper {
+ override fun canMoveBefore(position: Int): Boolean {
+ return position > 0 && position < dividerPosition
+ }
+
+ override fun canMoveAfter(position: Int): Boolean {
+ return position >= 0 && position < dividerPosition - 1
+ }
+
+ override fun moveBefore(position: Int) {
+ if (!canMoveBefore(position)) {
+ Log.w(TAG, "Cannot move position $position before")
+ } else {
+ onMoveItem(position, position - 1)
+ }
+ }
+
+ override fun moveAfter(position: Int) {
+ if (!canMoveAfter(position)) {
+ Log.w(TAG, "Cannot move position $position after")
+ } else {
+ onMoveItem(position, position + 1)
+ }
+ }
+ }
+
override fun attachAdapter(adapter: RecyclerView.Adapter<*>) {
this.adapter = adapter
}