Updating the behaviour of accessibility text iterators.

1. Iterators were skipping content on reversing direction.

2. The cursor was positioned at the beginning of the next text segment
   when moving forward and at end of the previous text segment when moving
   backwards. This is incorrect and now the cursor is positioned at the
   end of the segment when moving forward and at the beginning when moving
   backward.

3. The cursor position was not properly set when reaching the end/start
   of the text.

4. The iterators were reporting strictly the next/previous segment even
   if the cursor is within such a segment. Thus, when traversing some
   content may be skipped. Now moving forward moves the selection to
   the next segment end and the start position is either the old index
   if it was within a segment or the start of the segment. Same in
   reverse.

bug:6575099

Change-Id: Ib48a649cec53910339baf831a75e26440be6e576
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 20eef11..49efea3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1596,7 +1596,7 @@
     /**
      * @hide
      */
-    private int mAccessibilityCursorPosition = -1;
+    private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
 
     /**
      * The view's tag.
@@ -2468,6 +2468,11 @@
     public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004;
 
     /**
+     * The undefined cursor position.
+     */
+    private static final int ACCESSIBILITY_CURSOR_POSITION_UNDEFINED = -1;
+
+    /**
      * Indicates that the screen has changed state and is now off.
      *
      * @see #onScreenStateChanged(int)
@@ -6202,7 +6207,7 @@
             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
             notifyAccessibilityStateChanged();
             // Clear the text navigation state.
-            setAccessibilityCursorPosition(-1);
+            setAccessibilityCursorPosition(ACCESSIBILITY_CURSOR_POSITION_UNDEFINED);
         }
         // Clear the global reference of accessibility focus if this
         // view or any of its descendants had accessibility focus.
@@ -6252,6 +6257,7 @@
     void clearAccessibilityFocusNoCallbacks() {
         if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
             mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
+            setAccessibilityCursorPosition(ACCESSIBILITY_CURSOR_POSITION_UNDEFINED);
             invalidate();
         }
     }
@@ -6681,12 +6687,11 @@
         final int current = getAccessibilityCursorPosition();
         final int[] range = iterator.following(current);
         if (range == null) {
-            setAccessibilityCursorPosition(-1);
             return false;
         }
         final int start = range[0];
         final int end = range[1];
-        setAccessibilityCursorPosition(start);
+        setAccessibilityCursorPosition(end);
         sendViewTextTraversedAtGranularityEvent(
                 AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
                 granularity, start, end);
@@ -6702,16 +6707,26 @@
         if (iterator == null) {
             return false;
         }
-        final int selectionStart = getAccessibilityCursorPosition();
-        final int current = selectionStart >= 0 ? selectionStart : text.length() + 1;
+        int current = getAccessibilityCursorPosition();
+        if (current == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) {
+            current = text.length();
+        } else if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) {
+            // When traversing by character we always put the cursor after the character
+            // to ease edit and have to compensate before asking the for previous segment.
+            current--;
+        }
         final int[] range = iterator.preceding(current);
         if (range == null) {
-            setAccessibilityCursorPosition(-1);
             return false;
         }
         final int start = range[0];
         final int end = range[1];
-        setAccessibilityCursorPosition(end);
+        // Always put the cursor after the character to ease edit.
+        if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) {
+            setAccessibilityCursorPosition(end);
+        } else {
+            setAccessibilityCursorPosition(start);
+        }
         sendViewTextTraversedAtGranularityEvent(
                 AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
                 granularity, start, end);