Merge "Fix child view ordering for accessibility." into lmp-dev
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b586caa..4116b6b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7106,7 +7106,7 @@
                     ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child);
                     holders.add(holder);
                 }
-                Collections.sort(holders);
+                sort(holders);
                 for (int i = 0; i < childCount; i++) {
                     ViewLocationHolder holder = holders.get(i);
                     children.set(i, holder.mView);
@@ -7116,6 +7116,23 @@
             }
         }
 
+        private void sort(ArrayList<ViewLocationHolder> holders) {
+            // This is gross but the least risky solution. The current comparison
+            // strategy breaks transitivity but produces very good results. Coming
+            // up with a new strategy requires time which we do not have, so ...
+            try {
+                ViewLocationHolder.setComparisonStrategy(
+                        ViewLocationHolder.COMPARISON_STRATEGY_STRIPE);
+                Collections.sort(holders);
+            } catch (IllegalArgumentException iae) {
+                // Note that in practice this occurs extremely rarely in a couple
+                // of pathological cases.
+                ViewLocationHolder.setComparisonStrategy(
+                        ViewLocationHolder.COMPARISON_STRATEGY_LOCATION);
+                Collections.sort(holders);
+            }
+        }
+
         private void clear() {
             mChildren.clear();
         }
@@ -7134,6 +7151,12 @@
         private static final SynchronizedPool<ViewLocationHolder> sPool =
                 new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE);
 
+        public static final int COMPARISON_STRATEGY_STRIPE = 1;
+
+        public static final int COMPARISON_STRATEGY_LOCATION = 2;
+
+        private static int sComparisonStrategy = COMPARISON_STRATEGY_STRIPE;
+
         private final Rect mLocation = new Rect();
 
         public View mView;
@@ -7149,6 +7172,10 @@
             return holder;
         }
 
+        public static void setComparisonStrategy(int strategy) {
+            sComparisonStrategy = strategy;
+        }
+
         public void recycle() {
             clear();
             sPool.release(this);
@@ -7160,6 +7187,18 @@
             if (another == null) {
                 return 1;
             }
+
+            if (sComparisonStrategy == COMPARISON_STRATEGY_STRIPE) {
+                // First is above second.
+                if (mLocation.bottom - another.mLocation.top <= 0) {
+                    return -1;
+                }
+                // First is below second.
+                if (mLocation.top - another.mLocation.bottom >= 0) {
+                    return 1;
+                }
+            }
+
             // We are ordering left-to-right, top-to-bottom.
             if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
                 final int leftDifference = mLocation.left - another.mLocation.left;