am f8d4f950: am 0e05841e: resolved conflicts for merge of bcb644f8 to lmp-mr1-dev-plus-aosp

* commit 'f8d4f9500fdfd661501483884d4fc5ba18dd043e':
  ISSUE-144101: change children size calculation
diff --git a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
index f64dee4..1a523a9 100644
--- a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
@@ -48,9 +48,11 @@
     boolean mPendingSpanCountChange = false;
     int mSpanCount = DEFAULT_SPAN_COUNT;
     /**
-     * The size of each span
+     * Right borders for each span.
+     * <p>For <b>i-th</b> item start is {@link #mCachedBorders}[i-1] + 1
+     * and end is {@link #mCachedBorders}[i].
      */
-    int mSizePerSpan;
+    int [] mCachedBorders;
     /**
      * Temporary array to keep views in layoutChunk method
      */
@@ -252,7 +254,29 @@
         } else {
             totalSpace = getHeight() - getPaddingBottom() - getPaddingTop();
         }
-        mSizePerSpan = totalSpace / mSpanCount;
+        calculateItemBorders(totalSpace);
+    }
+
+    private void calculateItemBorders(int totalSpace) {
+        if (mCachedBorders == null || mCachedBorders.length != mSpanCount + 1
+                || mCachedBorders[mCachedBorders.length - 1] != totalSpace) {
+            mCachedBorders = new int[mSpanCount + 1];
+        }
+        mCachedBorders[0] = 0;
+        int sizePerSpan = totalSpace / mSpanCount;
+        int sizePerSpanRemainder = totalSpace % mSpanCount;
+        int consumedPixels = 0;
+        int additionalSize = 0;
+        for (int i = 1; i <= mSpanCount; i++) {
+            int itemSize = sizePerSpan;
+            additionalSize += sizePerSpanRemainder;
+            if (additionalSize > 0 && (mSpanCount - additionalSize) < sizePerSpanRemainder) {
+                itemSize += 1;
+                additionalSize -= mSpanCount;
+            }
+            consumedPixels += itemSize;
+            mCachedBorders[i] = consumedPixels;
+        }
     }
 
     @Override
@@ -393,10 +417,11 @@
                 }
             }
 
-            int spanSize = getSpanSize(recycler, state, getPosition(view));
-            final int spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize,
-                    View.MeasureSpec.EXACTLY);
             final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            final int spec = View.MeasureSpec.makeMeasureSpec(
+                    mCachedBorders[lp.mSpanIndex + lp.mSpanSize] -
+                            mCachedBorders[lp.mSpanIndex],
+                    View.MeasureSpec.EXACTLY);
             if (mOrientation == VERTICAL) {
                 measureChildWithDecorationsAndMargin(view, spec, getMainDirSpec(lp.height));
             } else {
@@ -413,8 +438,10 @@
         for (int i = 0; i < count; i ++) {
             final View view = mSet[i];
             if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) {
-                int spanSize = getSpanSize(recycler, state, getPosition(view));
-                final int spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize,
+                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+                final int spec = View.MeasureSpec.makeMeasureSpec(
+                        mCachedBorders[lp.mSpanIndex + lp.mSpanSize] -
+                                mCachedBorders[lp.mSpanIndex],
                         View.MeasureSpec.EXACTLY);
                 if (mOrientation == VERTICAL) {
                     measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec);
@@ -448,10 +475,10 @@
             View view = mSet[i];
             LayoutParams params = (LayoutParams) view.getLayoutParams();
             if (mOrientation == VERTICAL) {
-                left = getPaddingLeft() + mSizePerSpan * params.mSpanIndex;
+                left = getPaddingLeft() + mCachedBorders[params.mSpanIndex];
                 right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
             } else {
-                top = getPaddingTop() + mSizePerSpan * params.mSpanIndex;
+                top = getPaddingTop() + mCachedBorders[params.mSpanIndex];
                 bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
             }
             // We calculate everything with View's bounding box (which includes decor and margins)
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
index fa31494..d01c4db 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
@@ -188,6 +188,49 @@
         checkForMainThreadException();
     }
 
+    public void testCachedBorders() throws Throwable {
+        List<Config> testConfigurations = new ArrayList<Config>(mBaseVariations);
+        testConfigurations.addAll(cachedBordersTestConfigs());
+        for (Config config : testConfigurations) {
+            gridCachedBorderstTest(config);
+        }
+    }
+
+    private void gridCachedBorderstTest(Config config) throws Throwable {
+        RecyclerView recyclerView = setupBasic(config);
+        waitForFirstLayout(recyclerView);
+        final boolean vertical = config.mOrientation == GridLayoutManager.VERTICAL;
+        final int expectedSizeSum = vertical ? recyclerView.getWidth() : recyclerView.getHeight();
+        final int lastVisible = mGlm.findLastVisibleItemPosition();
+        for (int i = 0; i < lastVisible; i += config.mSpanCount) {
+            if ((i+1)*config.mSpanCount - 1 < lastVisible) {
+                int childrenSizeSum = 0;
+                for (int j = 0; j < config.mSpanCount; j++) {
+                    View child = recyclerView.getChildAt(i * config.mSpanCount + j);
+                    childrenSizeSum += vertical ? child.getWidth() : child.getHeight();
+                }
+                assertEquals(expectedSizeSum, childrenSizeSum);
+            }
+        }
+        removeRecyclerView();
+    }
+
+    private List<Config> cachedBordersTestConfigs() {
+        ArrayList<Config> configs = new ArrayList<Config>();
+        final int [] spanCounts = new int[]{88, 279, 741};
+        final int [] spanPerItem = new int[]{11, 9, 13};
+        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
+            for (boolean reverseLayout : new boolean[]{false, true}) {
+                for (int i = 0 ; i < spanCounts.length; i++) {
+                    Config config = new Config(spanCounts[i], orientation, reverseLayout);
+                    config.mSpanPerItem = spanPerItem[i];
+                    configs.add(config);
+                }
+            }
+        }
+        return configs;
+    }
+
     public void testLayoutParams() throws Throwable {
         layoutParamsTest(GridLayoutManager.HORIZONTAL);
         removeRecyclerView();
@@ -727,6 +770,7 @@
         int mSpanCount;
         int mOrientation = GridLayoutManager.VERTICAL;
         int mItemCount = 1000;
+        int mSpanPerItem = 1;
         boolean mReverseLayout = false;
 
         Config(int spanCount, int itemCount) {
@@ -759,11 +803,17 @@
     class GridTestAdapter extends TestAdapter {
 
         Set<Integer> mFullSpanItems = new HashSet<Integer>();
+        int mSpanPerItem = 1;
 
         GridTestAdapter(int count) {
             super(count);
         }
 
+        GridTestAdapter(int count, int spanPerItem) {
+            super(count);
+            mSpanPerItem = spanPerItem;
+        }
+
         void setFullSpan(int... items) {
             for (int i : items) {
                 mFullSpanItems.add(i);
@@ -774,7 +824,7 @@
             glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                 @Override
                 public int getSpanSize(int position) {
-                    return mFullSpanItems.contains(position) ? glm.getSpanCount() : 1;
+                    return mFullSpanItems.contains(position) ? glm.getSpanCount() : mSpanPerItem;
                 }
             });
         }