Merge "Accessibility focus traversal of lists inconsistent." into jb-dev
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index dae9c6a..edffb5e9 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -57,7 +57,6 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewParent;
-import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -1331,43 +1330,42 @@
 
     @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY
-                && (direction == ACCESSIBILITY_FOCUS_FORWARD
-                        || direction == ACCESSIBILITY_FOCUS_BACKWARD)) {
-            if (canTakeAccessibilityFocusFromHover()) {
-                views.add(this);
+        if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
+            switch(direction) {
+                case ACCESSIBILITY_FOCUS_BACKWARD: {
+                    View focusable = (getChildCount() > 0) ? getChildAt(getChildCount() - 1) : this;
+                    if (focusable.canTakeAccessibilityFocusFromHover()) {
+                        views.add(focusable);
+                    }
+                } return;
+                case ACCESSIBILITY_FOCUS_FORWARD: {
+                    if (canTakeAccessibilityFocusFromHover()) {
+                        views.add(this);
+                    }
+                } return;
             }
-        } else {
             super.addFocusables(views, direction, focusableMode);
         }
     }
 
     @Override
     public View focusSearch(int direction) {
-        return focusSearch(null, direction);
+        return focusSearch(this, direction);
     }
 
     @Override
     public View focusSearch(View focused, int direction) {
         switch (direction) {
             case ACCESSIBILITY_FOCUS_FORWARD: {
-                ViewRootImpl viewRootImpl = getViewRootImpl();
-                if (viewRootImpl == null) {
-                    return null;
-                }
-                View currentFocus = viewRootImpl.getAccessibilityFocusedHost();
-                if (currentFocus == null) {
-                    return super.focusSearch(this, direction);
-                }
-                // If we have the focus try giving it to the first child.
-                if (currentFocus == this) {
+                // If we are the focused view try giving it to the first child.
+                if (focused == this) {
                     if (getChildCount() > 0) {
                         return getChildAt(0);
                     }
                     return super.focusSearch(this, direction);
                 }
-                // Find the item that has accessibility focus.
-                final int currentPosition = getPositionForView(currentFocus);
+                // Find the item that has the focused view.
+                final int currentPosition = getPositionForView(focused);
                 if (currentPosition < 0 || currentPosition >= getCount()) {
                     return super.focusSearch(this, direction);
                 }
@@ -1376,9 +1374,9 @@
                 if (currentItem instanceof ViewGroup) {
                     ViewGroup currentItemGroup = (ViewGroup) currentItem;
                     View nextFocus = FocusFinder.getInstance().findNextFocus(currentItemGroup,
-                                currentFocus, direction);
+                                focused, direction);
                     if (nextFocus != null && nextFocus != currentItemGroup
-                            && nextFocus != currentFocus) {
+                            && nextFocus != focused) {
                         return nextFocus;
                     }
                 }
@@ -1386,50 +1384,54 @@
                 final int nextPosition = currentPosition - getFirstVisiblePosition() + 1;
                 if (nextPosition < getChildCount()) {
                     return getChildAt(nextPosition);
-                } else {
-                    return super.focusSearch(this, direction);
                 }
+                // No next item start searching from the list.
+                return super.focusSearch(this, direction);
             }
             case ACCESSIBILITY_FOCUS_BACKWARD: {
-                ViewRootImpl viewRootImpl = getViewRootImpl();
-                if (viewRootImpl == null) {
-                    return null;
-                }
-                View currentFocus = viewRootImpl.getAccessibilityFocusedHost();
-                if (currentFocus == null) {
-                    return super.focusSearch(this, direction);
-                }
-                // If we have the focus do a generic search.
-                if (currentFocus == this) {
-                    final int lastChildIndex = getChildCount() - 1;
-                    if (lastChildIndex >= 0) {
-                        return getChildAt(lastChildIndex);
+                // If we are the focused search from the view that is
+                // as closer to the bottom as possible.
+                if (focused == this) {
+                    final int childCount = getChildCount();
+                    if (childCount > 0) {
+                        return super.focusSearch(getChildAt(childCount - 1), direction);
                     }
                     return super.focusSearch(this, direction);
                 }
-                // Find the item that has accessibility focus.
-                final int currentPosition = getPositionForView(currentFocus);
+                // Find the item that has the focused view.
+                final int currentPosition = getPositionForView(focused);
                 if (currentPosition < 0 || currentPosition >= getCount()) {
                     return super.focusSearch(this, direction);
                 }
-                // Try to advance focus in the current item.
+
                 View currentItem = getChildAt(currentPosition - getFirstVisiblePosition());
+
+                // If a list item is the focused view we try to find a view
+                // in the previous item since in reverse the item contents
+                // get accessibility focus before the item itself.
+                if (currentItem == focused) {
+                    // This list gets accessibility focus after the last first item.
+                    final int previoustPosition = currentPosition - getFirstVisiblePosition() - 1;
+                    if (previoustPosition < 0) {
+                        return this;
+                    }
+                    currentItem = getChildAt(previoustPosition);
+                    focused = null;
+                }
+
+                // Search for into the item.
                 if (currentItem instanceof ViewGroup) {
                     ViewGroup currentItemGroup = (ViewGroup) currentItem;
                     View nextFocus = FocusFinder.getInstance().findNextFocus(currentItemGroup,
-                                currentFocus, direction);
+                                focused, direction);
                     if (nextFocus != null && nextFocus != currentItemGroup
-                            && nextFocus != currentFocus) {
+                            && nextFocus != focused) {
                         return nextFocus;
                     }
                 }
-                // Try to move focus to the previous item.
-                final int nextPosition = currentPosition - getFirstVisiblePosition() - 1;
-                if (nextPosition >= 0) {
-                    return getChildAt(nextPosition);
-                } else {
-                    return super.focusSearch(this, direction);
-                }
+
+                // If not item content wants focus we give it to the item.
+                return currentItem;
             }
         }
         return super.focusSearch(focused, direction);