Handle padding in wrap content

This CL fixes a bug in GridLayoutManager and StaggeredGridLM where
they were ignoring padding in non-scroll direction when calculating
the content wrapping. This would eventually cause clipped children.

I've also fixed some code-style errors.

Change-Id: I7dca4a30007596ae52bf367e5f7af9a10e387954
Bug: 26769843
diff --git a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
index c1c0d6d..7f541fe 100644
--- a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
@@ -279,15 +279,17 @@
             super.setMeasuredDimension(childrenBounds, wSpec, hSpec);
         }
         final int width, height;
+        final int horizontalPadding = getPaddingLeft() + getPaddingRight();
+        final int verticalPadding = getPaddingTop() + getPaddingBottom();
         if (mOrientation == VERTICAL) {
-            int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
+            final int usedHeight = childrenBounds.height() + verticalPadding;
             height = chooseSize(hSpec, usedHeight, getMinimumHeight());
-            width = chooseSize(wSpec, mCachedBorders[mCachedBorders.length - 1],
+            width = chooseSize(wSpec, mCachedBorders[mCachedBorders.length - 1] + horizontalPadding,
                     getMinimumWidth());
         } else {
-            int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
+            final int usedWidth = childrenBounds.width() + horizontalPadding;
             width = chooseSize(wSpec, usedWidth, getMinimumWidth());
-            height = chooseSize(hSpec, mCachedBorders[mCachedBorders.length - 1],
+            height = chooseSize(hSpec, mCachedBorders[mCachedBorders.length - 1] + verticalPadding,
                     getMinimumHeight());
         }
         setMeasuredDimension(width, height);
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
index fc8c523..34e1450 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
@@ -16,6 +16,8 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.RecyclerView.NO_POSITION;
+
 import android.content.Context;
 import android.graphics.PointF;
 import android.os.Parcel;
@@ -33,8 +35,6 @@
 
 import java.util.List;
 
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
 /**
  * A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides
  * similar functionality to {@link android.widget.ListView}.
diff --git a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
index a121ec9..7b92de5 100644
--- a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
@@ -16,6 +16,12 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_HEAD;
+import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_TAIL;
+import static android.support.v7.widget.LayoutState.LAYOUT_END;
+import static android.support.v7.widget.LayoutState.LAYOUT_START;
+import static android.support.v7.widget.RecyclerView.NO_POSITION;
+
 import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -37,12 +43,6 @@
 import java.util.BitSet;
 import java.util.List;
 
-import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_HEAD;
-import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_TAIL;
-import static android.support.v7.widget.LayoutState.LAYOUT_END;
-import static android.support.v7.widget.LayoutState.LAYOUT_START;
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
 /**
  * A LayoutManager that lays out children in a staggered grid formation.
  * It supports horizontal & vertical layout as well as an ability to layout children in reverse.
@@ -569,14 +569,18 @@
     public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
         // we don't like it to wrap content in our non-scroll direction.
         final int width, height;
+        final int horizontalPadding = getPaddingLeft() + getPaddingRight();
+        final int verticalPadding = getPaddingTop() + getPaddingBottom();
         if (mOrientation == VERTICAL) {
-            int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
+            final int usedHeight = childrenBounds.height() + verticalPadding;
             height = chooseSize(hSpec, usedHeight, getMinimumHeight());
-            width = chooseSize(wSpec, mSizePerSpan * mSpanCount, getMinimumWidth());
+            width = chooseSize(wSpec, mSizePerSpan * mSpanCount + horizontalPadding,
+                    getMinimumWidth());
         } else {
-            int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
+            final int usedWidth = childrenBounds.width() + horizontalPadding;
             width = chooseSize(wSpec, usedWidth, getMinimumWidth());
-            height = chooseSize(hSpec, mSizePerSpan * mSpanCount, getMinimumHeight());
+            height = chooseSize(hSpec, mSizePerSpan * mSpanCount + verticalPadding,
+                    getMinimumHeight());
         }
         setMeasuredDimension(width, height);
     }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java
index 23aa5d5..c085244 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java
@@ -15,6 +15,12 @@
  */
 package android.support.v7.widget;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.Rect;
@@ -31,12 +37,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
 /**
  * Class to test any generic wrap content behavior.
  * It does so by running the same view scenario twice. Once with match parent setup to record all
@@ -146,21 +146,27 @@
         recyclerView.setLayoutManager(createLayoutManager());
         recyclerView.setAdapter(adapter);
         recyclerView.setLayoutParams(lp);
+        Rect padding = mWrapContentConfig.padding;
+        recyclerView.setPadding(padding.left, padding.top,
+                padding.right, padding.bottom);
         setRecyclerView(recyclerView);
         recyclerView.waitUntilLayout();
         Snapshot snapshot = takeSnapshot();
         int index = 0;
+        Rect tmp = new Rect();
         for (BaseWrapContentWithAspectRatioTest.MeasureBehavior behavior : adapter.behaviors) {
+            tmp.set(expected[index]);
+            tmp.offset(padding.left, padding.top);
             assertThat("behavior " + index, snapshot.mChildCoordinates.get(behavior.getId()),
-                    is(expected[index]));
+                    is(tmp));
             index ++;
         }
         Rect boundingBox = new Rect(0, 0, 0, 0);
         for (Rect rect : expected) {
             boundingBox.union(rect);
         }
-        assertThat(recyclerView.getWidth(), is(width));
-        assertThat(recyclerView.getHeight(), is(height));
+        assertThat(recyclerView.getWidth(), is(width + padding.left + padding.right));
+        assertThat(recyclerView.getHeight(), is(height + padding.top + padding.bottom));
     }
 
 
@@ -469,14 +475,21 @@
 
     static class WrapContentConfig {
 
-        private boolean unlimitedWidth;
-        private boolean unlimitedHeight;
+        public boolean unlimitedWidth;
+        public boolean unlimitedHeight;
+        public Rect padding = new Rect(0, 0, 0, 0);
 
         public WrapContentConfig(boolean unlimitedWidth, boolean unlimitedHeight) {
             this.unlimitedWidth = unlimitedWidth;
             this.unlimitedHeight = unlimitedHeight;
         }
 
+        public WrapContentConfig(boolean unlimitedWidth, boolean unlimitedHeight, Rect padding) {
+            this.unlimitedWidth = unlimitedWidth;
+            this.unlimitedHeight = unlimitedHeight;
+            this.padding.set(padding);
+        }
+
         public boolean isUnlimitedWidth() {
             return unlimitedWidth;
         }
@@ -497,10 +510,11 @@
 
         @Override
         public String toString() {
-            return "WrapContentConfig{" +
-                    "unlimitedWidth=" + unlimitedWidth +
-                    ", unlimitedHeight=" + unlimitedHeight +
-                    '}';
+            return "WrapContentConfig{"
+                    + "unlimitedWidth=" + unlimitedWidth
+                    + ", unlimitedHeight=" + unlimitedHeight
+                    + ", padding=" + padding
+                    + '}';
         }
 
         public TestedFrameLayout.FullControlLayoutParams toLayoutParams(int wDim, int hDim) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java
index c8fdbeb..fd409c6 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java
@@ -15,31 +15,70 @@
  */
 package android.support.v7.widget;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.AspectRatioMeasureBehavior;
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.MeasureBehavior;
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.WrapContentAdapter;
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
-import android.graphics.Color;
 import android.graphics.Rect;
 import android.view.Gravity;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.*;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
-@RunWith(JUnit4.class)
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(Parameterized.class)
 public class GridLayoutManagerWrapContentTest extends BaseWrapContentTest {
+    private boolean mHorizontal = false;
+    private int mSpanCount = 3;
+    public GridLayoutManagerWrapContentTest(Rect padding) {
+        super(new WrapContentConfig(false, false, padding));
+    }
 
-    public GridLayoutManagerWrapContentTest() {
-        super(new WrapContentConfig(false, false));
+    @Parameterized.Parameters(name = "paddingRect={0}")
+    public static List<Rect> params() {
+        return Arrays.asList(
+                new Rect(0, 0, 0, 0),
+                new Rect(5, 0, 0, 0),
+                new Rect(0, 3, 0, 0),
+                new Rect(0, 0, 2, 0),
+                new Rect(0, 0, 0, 7),
+                new Rect(3, 5, 7, 11)
+        );
     }
 
     @Override
     RecyclerView.LayoutManager createLayoutManager() {
-        return new GridLayoutManager(getActivity(), 3);
+        GridLayoutManager lm = new GridLayoutManager(getActivity(), mSpanCount);
+        lm.setOrientation(mHorizontal ? HORIZONTAL : VERTICAL);
+        return lm;
+    }
+
+    @Test
+    public void testHorizontal() throws Throwable {
+        mHorizontal = true;
+        mSpanCount = 2;
+        TestedFrameLayout.FullControlLayoutParams lp =
+                mWrapContentConfig.toLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+        WrapContentAdapter adapter = new WrapContentAdapter(
+                new MeasureBehavior(10, 10, WRAP_CONTENT, WRAP_CONTENT),
+                new MeasureBehavior(10, 10, WRAP_CONTENT, WRAP_CONTENT),
+                new MeasureBehavior(10, 10, WRAP_CONTENT, WRAP_CONTENT),
+                new MeasureBehavior(20, 10, WRAP_CONTENT, WRAP_CONTENT)
+        );
+        Rect[] expected = new Rect[] {
+                new Rect(0, 0, 10, 10),
+                new Rect(0, 10, 10, 20),
+                new Rect(10, 0, 30, 10),
+                new Rect(10, 10, 30, 20)
+        };
+        layoutAndCheck(lp, adapter, expected, 30, 20);
     }
 
     @Test
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
index 52c27bb..2a411b3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
@@ -16,20 +16,22 @@
 
 package android.support.v7.widget;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import static android.support.v7.widget.BaseLinearLayoutManagerTest.Config;
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
 
+import android.graphics.Rect;
 import android.support.v4.view.ViewCompat;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.view.Gravity;
 
-import java.util.ArrayList;
-import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
-import static android.support.v7.widget.BaseLinearLayoutManagerTest.Config;
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 @RunWith(Parameterized.class)
 @MediumTest
@@ -100,22 +102,32 @@
     @Parameterized.Parameters(name = "{0} {1}")
     public static Iterable<Object[]> data() {
         List<Object[]> params = new ArrayList<>();
-        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
-            for (boolean reverseLayout : new boolean[]{false, true}) {
-                for (boolean stackFromBottom : new boolean[]{false, true}) {
-                    params.add(
-                            new Object[]{
-                                    new Config(orientation, reverseLayout, stackFromBottom),
-                                    new WrapContentConfig(false, false)
-                            }
-                    );
-                    params.add(
-                            new Object[]{
-                                    new Config(orientation, reverseLayout, stackFromBottom),
-                                    new WrapContentConfig(HORIZONTAL == orientation,
-                                            VERTICAL == orientation)
-                            }
-                    );
+        List<Rect> paddings = Arrays.asList(
+                new Rect(0, 0, 0, 0),
+                new Rect(5, 0, 0, 0),
+                new Rect(0, 6, 0, 0),
+                new Rect(0, 0, 7, 0),
+                new Rect(0, 0, 0, 8),
+                new Rect(3, 5, 7, 11)
+        );
+        for (Rect padding : paddings) {
+            for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
+                for (boolean reverseLayout : new boolean[]{false, true}) {
+                    for (boolean stackFromBottom : new boolean[]{false, true}) {
+                        params.add(
+                                new Object[]{
+                                        new Config(orientation, reverseLayout, stackFromBottom),
+                                        new WrapContentConfig(false, false, new Rect(padding))
+                                }
+                        );
+                        params.add(
+                                new Object[]{
+                                        new Config(orientation, reverseLayout, stackFromBottom),
+                                        new WrapContentConfig(HORIZONTAL == orientation,
+                                                VERTICAL == orientation, new Rect(padding))
+                                }
+                        );
+                    }
                 }
             }
         }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java
index 5b04302..6b44e4f 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java
@@ -15,24 +15,41 @@
  */
 
 package android.support.v7.widget;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.MeasureBehavior;
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.WrapContentAdapter;
+import static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 import android.graphics.Rect;
 import android.view.Gravity;
 import android.view.View;
 
-import static android.support.v7.widget.StaggeredGridLayoutManager.VERTICAL;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.*;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
-@RunWith(JUnit4.class)
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(Parameterized.class)
 public class StaggeredGridLayoutManagerWrapContentTest extends BaseWrapContentTest {
+    int mOrientation = StaggeredGridLayoutManager.VERTICAL;
+    public StaggeredGridLayoutManagerWrapContentTest(Rect padding) {
+        super(new WrapContentConfig(false, false, padding));
+    }
 
-    public StaggeredGridLayoutManagerWrapContentTest() {
-        super(new WrapContentConfig(false, false));
+    @Parameterized.Parameters(name = "paddingRect={0}")
+    public static List<Rect> params() {
+        return Arrays.asList(
+                new Rect(0, 0, 0, 0),
+                new Rect(5, 0, 0, 0),
+                new Rect(0, 3, 0, 0),
+                new Rect(0, 0, 2, 0),
+                new Rect(0, 0, 0, 7),
+                new Rect(3, 5, 7, 11)
+        );
     }
 
     @Test
@@ -55,6 +72,26 @@
     }
 
     @Test
+    public void testSimpleHorizontal() throws Throwable {
+        mOrientation = HORIZONTAL;
+        TestedFrameLayout.FullControlLayoutParams lp =
+                mWrapContentConfig.toLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+        WrapContentAdapter adapter = new WrapContentAdapter(
+                new MeasureBehavior(10, 10, WRAP_CONTENT, WRAP_CONTENT),
+                new MeasureBehavior(15, 10, WRAP_CONTENT, WRAP_CONTENT),
+                new MeasureBehavior(20, 10, WRAP_CONTENT, WRAP_CONTENT),
+                new MeasureBehavior(10, 20, WRAP_CONTENT, WRAP_CONTENT)
+        );
+        Rect[] expected = new Rect[] {
+                new Rect(0, 0, 10, 10),
+                new Rect(0, 20, 15, 30),
+                new Rect(0, 40, 20, 50),
+                new Rect(10, 0, 20, 20)
+        };
+        layoutAndCheck(lp, adapter, expected, 20, 60);
+    }
+
+    @Test
     public void testUnspecifiedWidth() throws Throwable {
         TestedFrameLayout.FullControlLayoutParams lp =
                 mWrapContentConfig.toLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
@@ -96,7 +133,7 @@
 
     @Override
     RecyclerView.LayoutManager createLayoutManager() {
-        return new StaggeredGridLayoutManager(3, VERTICAL);
+        return new StaggeredGridLayoutManager(3, mOrientation);
     }
 
     @Override