Set accessibility cursor position in non-editable views with content description.

While an accessibility service can set the cursor position in an editable
text field, it was not possible to set it for non-editable views with
content description. This patch enables that.

bug:8134469

Change-Id: I28b0ef1666b9e3ed5c0642718fbe63d4c9616569
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 11c80c26..adefa5a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5082,6 +5082,7 @@
         }
 
         if (mContentDescription != null && mContentDescription.length() > 0) {
+            info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
             info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
             info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
             info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
@@ -7022,6 +7023,24 @@
                     return previousAtGranularity(granularity, extendSelection);
                 }
             } break;
+            case AccessibilityNodeInfo.ACTION_SET_SELECTION: {
+                CharSequence text = getIterableTextForAccessibility();
+                if (text == null) {
+                    return false;
+                }
+                final int start = (arguments != null) ? arguments.getInt(
+                        AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, -1) : -1;
+                final int end = (arguments != null) ? arguments.getInt(
+                AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, -1) : -1;
+                // Only cursor position can be specified (selection length == 0)
+                if ((getAccessibilitySelectionStart() != start
+                        || getAccessibilitySelectionEnd() != end)
+                        && (start == end)) {
+                    setAccessibilitySelection(start, end);
+                    notifyAccessibilityStateChanged();
+                    return true;
+                }
+            } break;
         }
         return false;
     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2f02780..1579e79 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8030,28 +8030,30 @@
             } return false;
             case AccessibilityNodeInfo.ACTION_SET_SELECTION: {
                 if (isFocused() && canSelectText()) {
-                    final int start = (arguments != null) ? arguments.getInt(
-                            AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, -1) : -1;
-                    final int end = (arguments != null) ? arguments.getInt(
-                            AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, -1) : -1;
                     CharSequence text = getIterableTextForAccessibility();
                     if (text == null) {
                         return false;
                     }
-                    // No arguments clears the selection.
-                    if (start == end && end == -1) {
-                        Selection.removeSelection((Spannable) text);
-                        notifyAccessibilityStateChanged();
-                        return true;
-                    }
-                    if (start >= 0 && start <= end && end <= text.length()) {
-                        Selection.setSelection((Spannable) text, start, end);
-                        // Make sure selection mode is engaged.
-                        if (mEditor != null) {
-                            mEditor.startSelectionActionMode();
+                    final int start = (arguments != null) ? arguments.getInt(
+                            AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, -1) : -1;
+                    final int end = (arguments != null) ? arguments.getInt(
+                            AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, -1) : -1;
+                    if ((getSelectionStart() != start || getSelectionEnd() != end)) {
+                        // No arguments clears the selection.
+                        if (start == end && end == -1) {
+                            Selection.removeSelection((Spannable) text);
+                            notifyAccessibilityStateChanged();
+                            return true;
                         }
-                        notifyAccessibilityStateChanged();
-                        return true;
+                        if (start >= 0 && start <= end && end <= text.length()) {
+                            Selection.setSelection((Spannable) text, start, end);
+                            // Make sure selection mode is engaged.
+                            if (mEditor != null) {
+                                mEditor.startSelectionActionMode();
+                            }
+                            notifyAccessibilityStateChanged();
+                            return true;
+                        }
                     }
                 }
             } return false;