Merge "[RenderScript] Enable developers to only use support runtime and use black list for certain devices." into mnc-dev
diff --git a/build.gradle b/build.gradle
index c7479cb..0c9b958 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,10 +9,10 @@
     }
 }
 
-ext.supportVersion = '22.1.1'
+ext.supportVersion = '22.2.0'
 ext.extraVersion = 14
 ext.supportRepoOut = ''
-ext.buildToolsVersion = '21.0.0'
+ext.buildToolsVersion = '22.1.0'
 ext.buildNumber = Integer.toString(ext.extraVersion)
 
 /*
diff --git a/design/api/current.txt b/design/api/current.txt
index 3cfe257..6053b92 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -17,10 +17,18 @@
     method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, float, float, boolean);
     method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int[]);
     method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int, int);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout);
     method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, android.view.View, int);
     method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View);
   }
 
+  protected static class AppBarLayout.Behavior.SavedState extends android.view.View.BaseSavedState {
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcel);
+    ctor public AppBarLayout.Behavior.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.AppBarLayout.Behavior.SavedState> CREATOR;
+  }
+
   public static class AppBarLayout.LayoutParams extends android.widget.LinearLayout.LayoutParams {
     ctor public AppBarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
     ctor public AppBarLayout.LayoutParams(int, int);
@@ -127,6 +135,8 @@
     method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[]);
     method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int);
     method public void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+    method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, V, android.os.Parcelable);
+    method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, V);
     method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
     method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View);
     method public boolean onTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
@@ -150,6 +160,12 @@
     field public int keyline;
   }
 
+  protected static class CoordinatorLayout.SavedState extends android.view.View.BaseSavedState {
+    ctor public CoordinatorLayout.SavedState(android.os.Parcel);
+    ctor public CoordinatorLayout.SavedState(android.os.Parcelable);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.CoordinatorLayout.SavedState> CREATOR;
+  }
+
   public class FloatingActionButton extends android.widget.ImageView {
     ctor public FloatingActionButton(android.content.Context);
     ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
@@ -239,8 +255,6 @@
     method public void addTab(android.support.design.widget.TabLayout.Tab, int);
     method public void addTab(android.support.design.widget.TabLayout.Tab, boolean);
     method public void addTab(android.support.design.widget.TabLayout.Tab, int, boolean);
-    method public void addTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
-    method public android.support.v4.view.ViewPager.OnPageChangeListener createOnPageChangeListener();
     method public android.support.design.widget.TabLayout.Tab getTabAt(int);
     method public int getTabCount();
     method public int getTabGravity();
@@ -256,6 +270,8 @@
     method public void setTabMode(int);
     method public void setTabTextColors(android.content.res.ColorStateList);
     method public void setTabTextColors(int, int);
+    method public void setTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
+    method public void setupWithViewPager(android.support.v4.view.ViewPager);
     field public static final int GRAVITY_CENTER = 1; // 0x1
     field public static final int GRAVITY_FILL = 0; // 0x0
     field public static final int MODE_FIXED = 1; // 0x1
@@ -287,10 +303,23 @@
     field public static final int INVALID_POSITION = -1; // 0xffffffff
   }
 
+  public static class TabLayout.TabLayoutOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+    ctor public TabLayout.TabLayoutOnPageChangeListener(android.support.design.widget.TabLayout);
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, int);
+    method public void onPageSelected(int);
+  }
+
+  public static class TabLayout.ViewPagerOnTabSelectedListener implements android.support.design.widget.TabLayout.OnTabSelectedListener {
+    ctor public TabLayout.ViewPagerOnTabSelectedListener(android.support.v4.view.ViewPager);
+    method public void onTabReselected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabSelected(android.support.design.widget.TabLayout.Tab);
+    method public void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+  }
+
   public class TextInputLayout extends android.widget.LinearLayout {
     ctor public TextInputLayout(android.content.Context);
     ctor public TextInputLayout(android.content.Context, android.util.AttributeSet);
-    ctor public TextInputLayout(android.content.Context, android.util.AttributeSet, int);
     method public android.widget.EditText getEditText();
     method public void setError(java.lang.CharSequence);
     method public void setErrorEnabled(boolean);
diff --git a/design/res-public/values/public_attrs.xml b/design/res-public/values/public_attrs.xml
index b2ad8fd..51c7b4d 100644
--- a/design/res-public/values/public_attrs.xml
+++ b/design/res-public/values/public_attrs.xml
@@ -27,6 +27,7 @@
     <public type="attr" name="expandedTitleMarginBottom"/>
     <public type="attr" name="expandedTitleMarginEnd"/>
     <public type="attr" name="expandedTitleMarginStart"/>
+    <public type="attr" name="expandedTitleMarginTop"/>
     <public type="attr" name="expandedTitleTextAppearance"/>
     <public type="attr" name="fabSize"/>
     <public type="attr" name="headerLayout"/>
@@ -56,4 +57,5 @@
     <public type="attr" name="tabSelectedTextColor"/>
     <public type="attr" name="tabTextAppearance"/>
     <public type="attr" name="tabTextColor"/>
+    <public type="attr" name="toolbarId"/>
 </resources>
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index e2afe99..a89d330 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -205,11 +205,22 @@
     </declare-styleable>
 
     <declare-styleable name="CollapsingToolbarLayout">
+        <!--  Specifies extra space on the start, top, end and bottom
+              sides of the the expanded title text. Margin values should be positive. -->
         <attr name="expandedTitleMargin" format="dimension"/>
+        <!--  Specifies extra space on the start side of the the expanded title text.
+              Margin values should be positive. -->
         <attr name="expandedTitleMarginStart" format="dimension"/>
-        <attr name="expandedTitleMarginBottom" format="dimension"/>
+        <!--  Specifies extra space on the top side of the the expanded title text.
+              Margin values should be positive. -->
+        <attr name="expandedTitleMarginTop" format="dimension"/>
+        <!--  Specifies extra space on the end side of the the expanded title text.
+              Margin values should be positive. -->
         <attr name="expandedTitleMarginEnd" format="dimension"/>
-        <!-- The text appearance of the CollapsingToolbarLayouts title when it is fully
+        <!--  Specifies extra space on the bottom side of the the expanded title text.
+              Margin values should be positive. -->
+        <attr name="expandedTitleMarginBottom" format="dimension"/>
+        <!-- The text appearance of the CollapsingToolbarLayout's title when it is fully
              'expanded' -->
         <attr name="expandedTitleTextAppearance" format="reference"/>
         <!-- The text appearance of the CollapsingToolbarLayouts title when it is fully
@@ -222,6 +233,9 @@
              CollapsingToolbarLayout has been scrolled sufficiently off screen. Only works on
              Lollipop with the correct setup. -->
         <attr name="statusBarScrim" format="color" />
+        <!-- The id of the primary Toolbar child that you wish to use for the purpose of collapsing.
+             If you do not set this then the first Toolbar child found will be used. -->
+        <attr name="toolbarId" format="reference"/>
     </declare-styleable>
 
     <declare-styleable name="CollapsingAppBarLayout_LayoutParams">
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
index e784bd5..f84b567 100644
--- a/design/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -282,7 +282,7 @@
                     NavigationMenuItemView itemView = (NavigationMenuItemView) convertView;
                     itemView.setIconTintList(mIconTintList);
                     itemView.setTextColor(mTextColor);
-                    itemView.setBackground(mItemBackground);
+                    itemView.setBackgroundDrawable(mItemBackground);
                     itemView.initialize(item.getMenuItem(), 0);
                     break;
                 case VIEW_TYPE_SUBHEADER:
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index bb77821..b15c03a 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.support.annotation.IntDef;
 import android.support.design.R;
 import android.support.v4.view.ViewCompat;
@@ -579,7 +581,9 @@
      * scroll handling with offsetting.
      */
     public static class Behavior extends ViewOffsetBehavior<AppBarLayout> {
-        private int mLogicalOffsetTop;
+        private static final int INVALID_POSITION = -1;
+
+        private int mOffsetDelta;
 
         private boolean mSkipNestedPreScroll;
         private Runnable mFlingRunnable;
@@ -587,6 +591,10 @@
 
         private ValueAnimatorCompat mAnimator;
 
+        private int mOffsetToChildIndexOnLayout = INVALID_POSITION;
+        private boolean mOffsetToChildIndexOnLayoutIsMinHeight;
+        private float mOffsetToChildIndexOnLayoutPerc;
+
         public Behavior() {}
 
         public Behavior(Context context, AttributeSet attrs) {
@@ -682,7 +690,7 @@
                     }
                 }
 
-                if (mLogicalOffsetTop != targetScroll) {
+                if (getTopBottomOffsetForScrollingSibling() != targetScroll) {
                     animateOffsetTo(coordinatorLayout, child, targetScroll);
                     return true;
                 }
@@ -722,7 +730,7 @@
             }
 
             mScroller.fling(
-                    0, mLogicalOffsetTop, // curr
+                    0, getTopBottomOffsetForScrollingSibling(), // curr
                     0, Math.round(velocityY), // velocity.
                     0, 0, // x
                     minOffset, maxOffset); // y
@@ -758,12 +766,24 @@
         }
 
         @Override
-        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout child,
+        public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout appBarLayout,
                 int layoutDirection) {
-            boolean handled = super.onLayoutChild(parent, child, layoutDirection);
+            boolean handled = super.onLayoutChild(parent, appBarLayout, layoutDirection);
+
+            if (mOffsetToChildIndexOnLayout >= 0) {
+                View child = appBarLayout.getChildAt(mOffsetToChildIndexOnLayout);
+                int offset = -child.getBottom();
+                if (mOffsetToChildIndexOnLayoutIsMinHeight) {
+                    offset += ViewCompat.getMinimumHeight(child);
+                } else {
+                    offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc);
+                }
+                setTopAndBottomOffset(offset);
+                mOffsetToChildIndexOnLayout = INVALID_POSITION;
+            }
 
             // Make sure we update the elevation
-            dispatchOffsetUpdates(child);
+            dispatchOffsetUpdates(appBarLayout);
 
             return handled;
         }
@@ -771,7 +791,7 @@
         private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout,
                 int dy, int minOffset, int maxOffset) {
             return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout,
-                    mLogicalOffsetTop - dy, minOffset, maxOffset);
+                    getTopBottomOffsetForScrollingSibling() - dy, minOffset, maxOffset);
         }
 
         final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout,
@@ -782,7 +802,7 @@
 
         final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout,
                 AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) {
-            final int curOffset = mLogicalOffsetTop;
+            final int curOffset = getTopBottomOffsetForScrollingSibling();
             int consumed = 0;
 
             if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
@@ -791,15 +811,16 @@
                 newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
 
                 if (curOffset != newOffset) {
-                    boolean offsetChanged = setTopAndBottomOffset(
-                            appBarLayout.hasChildWithInterpolator()
-                                    ? interpolateOffset(appBarLayout, newOffset)
-                                    : newOffset);
+                    final int interpolatedOffset = appBarLayout.hasChildWithInterpolator()
+                            ? interpolateOffset(appBarLayout, newOffset)
+                            : newOffset;
+
+                    boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset);
 
                     // Update how much dy we have consumed
                     consumed = curOffset - newOffset;
                     // Update the stored sibling offset
-                    mLogicalOffsetTop = newOffset;
+                    mOffsetDelta = newOffset - interpolatedOffset;
 
                     if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) {
                         // If the offset hasn't changed and we're using an interpolated scroll
@@ -873,7 +894,84 @@
         }
 
         final int getTopBottomOffsetForScrollingSibling() {
-            return mLogicalOffsetTop;
+            return getTopAndBottomOffset() + mOffsetDelta;
+        }
+
+        @Override
+        public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout) {
+            final Parcelable superState = super.onSaveInstanceState(parent, appBarLayout);
+            final int offset = getTopAndBottomOffset();
+
+            // Try and find the first visible child...
+            for (int i = 0, count = appBarLayout.getChildCount(); i < count; i++) {
+                View child = appBarLayout.getChildAt(i);
+                final int visBottom = child.getBottom() + offset;
+
+                if (child.getTop() + offset <= 0 && visBottom >= 0) {
+                    final SavedState ss = new SavedState(superState);
+                    ss.firstVisibleChildIndex = i;
+                    ss.firstVisibileChildAtMinimumHeight =
+                            visBottom == ViewCompat.getMinimumHeight(child);
+                    ss.firstVisibileChildPercentageShown = visBottom / (float) child.getHeight();
+                    return ss;
+                }
+            }
+
+            // Else we'll just return the super state
+            return superState;
+        }
+
+        @Override
+        public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout,
+                Parcelable state) {
+            if (state instanceof SavedState) {
+                final SavedState ss = (SavedState) state;
+                super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState());
+                mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex;
+                mOffsetToChildIndexOnLayoutPerc = ss.firstVisibileChildPercentageShown;
+                mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibileChildAtMinimumHeight;
+            } else {
+                super.onRestoreInstanceState(parent, appBarLayout, state);
+                mOffsetToChildIndexOnLayout = INVALID_POSITION;
+            }
+        }
+
+        protected static class SavedState extends View.BaseSavedState {
+            int firstVisibleChildIndex;
+            float firstVisibileChildPercentageShown;
+            boolean firstVisibileChildAtMinimumHeight;
+
+            public SavedState(Parcel source) {
+                super(source);
+                firstVisibleChildIndex = source.readInt();
+                firstVisibileChildPercentageShown = source.readFloat();
+                firstVisibileChildAtMinimumHeight = source.readByte() != 0;
+            }
+
+            public SavedState(Parcelable superState) {
+                super(superState);
+            }
+
+            @Override
+            public void writeToParcel(Parcel dest, int flags) {
+                super.writeToParcel(dest, flags);
+                dest.writeInt(firstVisibleChildIndex);
+                dest.writeFloat(firstVisibileChildPercentageShown);
+                dest.writeByte((byte) (firstVisibileChildAtMinimumHeight ? 1 : 0));
+            }
+
+            public static final Parcelable.Creator<SavedState> CREATOR =
+                    new Parcelable.Creator<SavedState>() {
+                        @Override
+                        public SavedState createFromParcel(Parcel source) {
+                            return new SavedState(source);
+                        }
+
+                        @Override
+                        public SavedState[] newArray(int size) {
+                            return new SavedState[size];
+                        }
+                    };
         }
     }
 
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index e634322..474a0a8 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -22,6 +22,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
@@ -51,10 +52,15 @@
  * {@link #setTitle(CharSequence)}. The title appearance can be tweaked via the
  * {@code collapsedTextAppearance} and {@code expandedTextAppearance} attributes.
  *
- * <h3>Foreground scrim</h3>
+ * <h3>Content scrim</h3>
  * A full-bleed scrim which is show or hidden when the scroll position has hit a certain threshold.
  * You can change this via {@link #setContentScrim(Drawable)}.
  *
+ * <h3>Status bar scrim</h3>
+ * A scrim which is show or hidden behind the status bar when the scroll position has hit a certain
+ * threshold. You can change this via {@link #setStatusBarScrim(Drawable)}. This only works
+ * on {@link Build.VERSION_CODES#LOLLIPOP LOLLIPOP} devices when we set to fit system windows.
+ *
  * <h3>Parallax scrolling children</h3>
  * Child views can opt to be scrolled within this layout in a parallax fashion.
  * See {@link LayoutParams#COLLAPSE_MODE_PARALLAX} and
@@ -73,19 +79,23 @@
  * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
  * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
  * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_statusBarScrim
+ * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_toolbarId
  */
 public class CollapsingToolbarLayout extends FrameLayout {
 
     private static final int SCRIM_ANIMATION_DURATION = 600;
 
+    private boolean mRefreshToolbar = true;
+    private int mToolbarId;
     private Toolbar mToolbar;
     private View mDummyView;
 
     private int mExpandedMarginLeft;
+    private int mExpandedMarginTop;
     private int mExpandedMarginRight;
     private int mExpandedMarginBottom;
 
-    private final Rect mRect = new Rect();
+    private final Rect mTmpRect = new Rect();
     private final CollapsingTextHelper mCollapsingTextHelper;
 
     private Drawable mContentScrim;
@@ -119,7 +129,7 @@
                 R.styleable.CollapsingToolbarLayout, defStyleAttr,
                 R.style.Widget_Design_CollapsingToolbar);
 
-        mExpandedMarginLeft = mExpandedMarginRight = mExpandedMarginBottom =
+        mExpandedMarginLeft = mExpandedMarginTop = mExpandedMarginRight = mExpandedMarginBottom =
                 a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMargin, 0);
 
         final boolean isRtl = ViewCompat.getLayoutDirection(this)
@@ -142,6 +152,10 @@
                 mExpandedMarginRight = marginEnd;
             }
         }
+        if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginTop)) {
+            mExpandedMarginTop = a.getDimensionPixelSize(
+                    R.styleable.CollapsingToolbarLayout_expandedTitleMarginTop, 0);
+        }
         if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom)) {
             mExpandedMarginBottom = a.getDimensionPixelSize(
                     R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom, 0);
@@ -160,6 +174,8 @@
         setContentScrim(a.getDrawable(R.styleable.CollapsingToolbarLayout_contentScrim));
         setStatusBarScrim(a.getDrawable(R.styleable.CollapsingToolbarLayout_statusBarScrim));
 
+        mToolbarId = a.getResourceId(R.styleable.CollapsingToolbarLayout_toolbarId, -1);
+
         a.recycle();
 
         setWillNotDraw(false);
@@ -170,15 +186,7 @@
                     public WindowInsetsCompat onApplyWindowInsets(View v,
                             WindowInsetsCompat insets) {
                         mLastInsets = insets;
-
-                        for (int i = 0, z = getChildCount(); i < z; i++) {
-                            View child = getChildAt(i);
-                            LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-                            if (lp.height != LayoutParams.MATCH_PARENT) {
-                                lp.topMargin = insets.getSystemWindowInsetTop();
-                            }
-                        }
+                        requestLayout();
                         return insets.consumeSystemWindowInsets();
                     }
                 });
@@ -210,22 +218,12 @@
     }
 
     @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
-        super.addView(child, index, params);
-
-        if (child instanceof Toolbar) {
-            mToolbar = (Toolbar) child;
-            mDummyView = new View(getContext());
-            mToolbar.addView(mDummyView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-        }
-    }
-
-    @Override
     public void draw(Canvas canvas) {
         super.draw(canvas);
 
         // If we don't have a toolbar, the scrim will be not be drawn in drawChild() below.
         // Instead, we draw it here, before our collapsing text.
+        ensureToolbar();
         if (mToolbar == null && mContentScrim != null && mScrimAlpha > 0) {
             mContentScrim.mutate().setAlpha(mScrimAlpha);
             mContentScrim.draw(canvas);
@@ -251,6 +249,7 @@
         // This is a little weird. Our scrim needs to be behind the Toolbar (if it is present),
         // but in front of any other children which are behind it. To do this we intercept the
         // drawChild() call, and draw our scrim first when drawing the toolbar
+        ensureToolbar();
         if (child == mToolbar && mContentScrim != null && mScrimAlpha > 0) {
             mContentScrim.mutate().setAlpha(mScrimAlpha);
             mContentScrim.draw(canvas);
@@ -268,28 +267,91 @@
         }
     }
 
+    private void ensureToolbar() {
+        if (!mRefreshToolbar) {
+            return;
+        }
+
+        Toolbar fallback = null, selected = null;
+
+        for (int i = 0, count = getChildCount(); i < count; i++) {
+            final View child = getChildAt(i);
+            if (child instanceof Toolbar) {
+                if (mToolbarId != -1) {
+                    // There's a toolbar id set so try and find it...
+                    if (mToolbarId == child.getId()) {
+                        // We found the primary Toolbar, use it
+                        selected = (Toolbar) child;
+                        break;
+                    }
+                    if (fallback == null) {
+                        // We'll record the first Toolbar as our fallback
+                        fallback = (Toolbar) child;
+                    }
+                } else {
+                    // We don't have a id to check for so just use the first we come across
+                    selected = (Toolbar) child;
+                    break;
+                }
+            }
+        }
+
+        if (selected == null) {
+            // If we didn't find a primary Toolbar, use the fallback
+            selected = fallback;
+        }
+
+        if (selected != null) {
+            mToolbar = selected;
+            mDummyView = new View(getContext());
+            mToolbar.addView(mDummyView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+        } else {
+            mToolbar = null;
+            mDummyView = null;
+        }
+        mRefreshToolbar = false;
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
         // Update our child view offset helpers
         for (int i = 0, z = getChildCount(); i < z; i++) {
-            getViewOffsetHelper(getChildAt(i)).onViewLayout();
+            final View child = getChildAt(i);
+
+            if (mLastInsets != null && !ViewCompat.getFitsSystemWindows(child)) {
+                final int insetTop = mLastInsets.getSystemWindowInsetTop();
+                if (child.getTop() < insetTop) {
+                    // If the child isn't set to fit system windows but is drawing within the inset
+                    // offset it down
+                    child.offsetTopAndBottom(insetTop);
+                }
+            }
+
+            getViewOffsetHelper(child).onViewLayout();
         }
 
         // Now let the collapsing text helper update itself
         mCollapsingTextHelper.onLayout(changed, left, top, right, bottom);
 
+        ensureToolbar();
+
         // Update the collapsed bounds by getting it's transformed bounds
-        ViewGroupUtils.getDescendantRect(this, mDummyView, mRect);
-        mCollapsingTextHelper.setCollapsedBounds(mRect.left, bottom - mRect.height(),
-                mRect.right, bottom);
-        // Update the expanded bounds
-        mCollapsingTextHelper.setExpandedBounds(left + mExpandedMarginLeft, mDummyView.getBottom(),
-                right - mExpandedMarginRight, bottom - mExpandedMarginBottom);
+        if (mDummyView != null) {
+            ViewGroupUtils.getDescendantRect(this, mDummyView, mTmpRect);
+            mCollapsingTextHelper.setCollapsedBounds(mTmpRect.left, bottom - mTmpRect.height(),
+                    mTmpRect.right, bottom);
+            // Update the expanded bounds
+            mCollapsingTextHelper.setExpandedBounds(left + mExpandedMarginLeft,
+                    mTmpRect.bottom + mExpandedMarginTop, right - mExpandedMarginRight,
+                    bottom - mExpandedMarginBottom);
+        }
 
         // Finally, set our minimum height to enable proper AppBarLayout collapsing
-        setMinimumHeight(mToolbar.getHeight());
+        if (mToolbar != null) {
+            setMinimumHeight(mToolbar.getHeight());
+        }
     }
 
     private static ViewOffsetHelper getViewOffsetHelper(View view) {
@@ -325,6 +387,7 @@
     }
 
     private void animateScrim(int targetAlpha) {
+        ensureToolbar();
         if (mScrimAnimator == null) {
             mScrimAnimator = ViewUtils.createAnimator();
             mScrimAnimator.setDuration(SCRIM_ANIMATION_DURATION);
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index 0003daa..aa1bab8 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -26,6 +26,8 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.SystemClock;
 import android.support.design.R;
 import android.support.v4.content.ContextCompat;
@@ -37,6 +39,7 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
@@ -2043,6 +2046,44 @@
                 V child, WindowInsetsCompat insets) {
             return insets;
         }
+
+        /**
+         * Hook allowing a behavior to re-apply a representation of its internal state that had
+         * previously been generated by {@link #onSaveInstanceState}. This function will never
+         * be called with a null state.
+         *
+         * @param parent the parent CoordinatorLayout
+         * @param child child view to restore from
+         * @param state The frozen state that had previously been returned by
+         *        {@link #onSaveInstanceState}.
+         *
+         * @see #onSaveInstanceState()
+         */
+        public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
+            // no-op
+        }
+
+        /**
+         * Hook allowing a behavior to generate a representation of its internal state
+         * that can later be used to create a new instance with that same state.
+         * This state should only contain information that is not persistent or can
+         * not be reconstructed later.
+         *
+         * <p>Behavior state is only saved when both the parent {@link CoordinatorLayout} and
+         * a view using this behavior have valid IDs set.</p>
+         *
+         * @param parent the parent CoordinatorLayout
+         * @param child child view to restore from
+         *
+         * @return Returns a Parcelable object containing the behavior's current dynamic
+         *         state.
+         *
+         * @see #onRestoreInstanceState(android.os.Parcelable)
+         * @see View#onSaveInstanceState()
+         */
+        public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
+            return BaseSavedState.EMPTY_STATE;
+        }
     }
 
     /**
@@ -2413,4 +2454,106 @@
             return insets.consumeSystemWindowInsets();
         }
     }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        final SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+
+        final SparseArray<Parcelable> behaviorStates = ss.behaviorStates;
+
+        for (int i = 0, count = getChildCount(); i < count; i++) {
+            final View child = getChildAt(i);
+            final int childId = child.getId();
+            final LayoutParams lp = getResolvedLayoutParams(child);
+            final Behavior b = lp.getBehavior();
+
+            if (childId != NO_ID && b != null) {
+                Parcelable savedState = behaviorStates.get(childId);
+                if (savedState != null) {
+                    b.onRestoreInstanceState(this, child, savedState);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        final SavedState ss = new SavedState(super.onSaveInstanceState());
+
+        final SparseArray<Parcelable> behaviorStates = new SparseArray<>();
+        for (int i = 0, count = getChildCount(); i < count; i++) {
+            final View child = getChildAt(i);
+            final int childId = child.getId();
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            final Behavior b = lp.getBehavior();
+
+            if (childId != NO_ID && b != null) {
+                // If the child has an ID and a Behavior, let it save some state...
+                Parcelable state = b.onSaveInstanceState(this, child);
+                if (state != null) {
+                    behaviorStates.append(childId, state);
+                }
+            }
+        }
+        ss.behaviorStates = behaviorStates;
+        return ss;
+    }
+
+    protected static class SavedState extends BaseSavedState {
+        SparseArray<Parcelable> behaviorStates;
+
+        public SavedState(Parcel source) {
+            super(source);
+
+            final int size = source.readInt();
+
+            final int[] ids = new int[size];
+            source.readIntArray(ids);
+
+            final Parcelable[] states = source.readParcelableArray(
+                    CoordinatorLayout.class.getClassLoader());
+
+            behaviorStates = new SparseArray<>(size);
+            for (int i = 0; i < size; i++) {
+                behaviorStates.append(ids[i], states[i]);
+            }
+        }
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+
+            final int size = behaviorStates != null ? behaviorStates.size() : 0;
+            dest.writeInt(size);
+
+            final int[] ids = new int[size];
+            final Parcelable[] states = new Parcelable[size];
+
+            for (int i = 0; i < size; i++) {
+                ids[i] = behaviorStates.keyAt(i);
+                states[i] = behaviorStates.valueAt(i);
+            }
+            dest.writeIntArray(ids);
+            dest.writeParcelableArray(states, flags);
+
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    @Override
+                    public SavedState createFromParcel(Parcel source) {
+                        return new SavedState(source);
+                    }
+
+                    @Override
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
+    }
 }
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
index d743340..3e5cd97 100755
--- a/design/src/android/support/design/widget/TabLayout.java
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -52,6 +52,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Iterator;
 
@@ -71,14 +72,14 @@
  * notified when any tab's selection state has been changed.
  * <p>
  * If you're using a {@link android.support.v4.view.ViewPager} together
- * with this layout, you can use {@link #addTabsFromPagerAdapter(PagerAdapter)} which will populate
- * the tabs using the {@link PagerAdapter}'s page titles. You should also use a {@link
- * ViewPager.OnPageChangeListener} to forward the scroll and selection changes to this layout.
- * You can use the one returned {@link #createOnPageChangeListener()} for easy implementation:
+ * with this layout, you can use {@link #setTabsFromPagerAdapter(PagerAdapter)} which will populate
+ * the tabs using the given {@link PagerAdapter}'s page titles. You should also use a
+ * {@link TabLayoutOnPageChangeListener} to forward the scroll and selection changes to this
+ * layout like so:
  * <pre>
  * ViewPager viewPager = ...;
  * TabLayout tabLayout = ...;
- * viewPager.setOnPageChangeListener(tabLayout.createOnPageChangeListener());
+ * viewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(tabLayout));
  * </pre>
  *
  * @see <a href="http://www.google.com/design/spec/components/tabs.html">Tabs</a>
@@ -271,7 +272,7 @@
 
     /**
      * Set the scroll position of the tabs. This is useful for when the tabs are being displayed as
-     * part of a scrolling container such as {@link ViewPager}.
+     * part of a scrolling container such as {@link android.support.v4.view.ViewPager}.
      * <p>
      * Calling this method does not update the selected tab, it is only used for drawing purposes.
      *
@@ -298,51 +299,6 @@
     }
 
     /**
-     * Add new {@link Tab}s populated from a {@link PagerAdapter}. Each tab will have it's text set
-     * to the value returned from {@link PagerAdapter#getPageTitle(int)}.
-     *
-     * @param adapter the adapter to populate from
-     */
-    public void addTabsFromPagerAdapter(PagerAdapter adapter) {
-        for (int i = 0, count = adapter.getCount(); i < count; i++) {
-            addTab(newTab().setText(adapter.getPageTitle(i)));
-        }
-    }
-
-    /**
-     * Create a {@link ViewPager.OnPageChangeListener} which implements the
-     * necessary calls back to this layout so that the tabs position is kept in sync.
-     * <p>
-     * If you need to have a custom {@link ViewPager.OnPageChangeListener} for your own
-     * purposes, you can still use the instance returned from this method, but making sure to call
-     * through to all of the methods.
-     */
-    public ViewPager.OnPageChangeListener createOnPageChangeListener() {
-        return new ViewPager.OnPageChangeListener() {
-            private int mScrollState;
-
-            @Override
-            public void onPageScrollStateChanged(int state) {
-                mScrollState = state;
-            }
-
-            @Override
-            public void onPageScrolled(int position, float positionOffset,
-                    int positionOffsetPixels) {
-                // Update the scroll position, only update the text selection if we're being
-                // dragged
-                setScrollPosition(position, positionOffset,
-                        mScrollState == ViewPager.SCROLL_STATE_DRAGGING);
-            }
-
-            @Override
-            public void onPageSelected(int position) {
-                getTabAt(position).select();
-            }
-        };
-    }
-
-    /**
      * Add a tab to this layout. The tab will be added at the end of the list.
      * If this is the first tab to be added it will become the selected tab.
      *
@@ -401,7 +357,8 @@
     }
 
     /**
-     * Set the {@link android.support.design.widget.TabLayout.OnTabSelectedListener} that will handle switching to and from tabs.
+     * Set the {@link android.support.design.widget.TabLayout.OnTabSelectedListener} that will
+     * handle switching to and from tabs.
      *
      * @param onTabSelectedListener Listener to handle tab selection events
      */
@@ -497,7 +454,7 @@
      * <li>{@link #MODE_SCROLLABLE}: Scrollable tabs display a subset of tabs at any given moment,
      * and can contain longer tab labels and a larger number of tabs. They are best used for
      * browsing contexts in touch interfaces when users don’t need to directly compare the tab
-     * labels. This mode is commonly used with a {@link ViewPager}.</li>
+     * labels. This mode is commonly used with a {@link android.support.v4.view.ViewPager}.</li>
      * </ul>
      *
      * @param mode one of {@link #MODE_FIXED} or {@link #MODE_SCROLLABLE}.
@@ -565,6 +522,55 @@
         setTabTextColors(createColorStateList(normalColor, selectedColor));
     }
 
+    /**
+     * The one-stop shop for setting up this {@link TabLayout} with a {@link ViewPager}.
+     *
+     * <p>This method will:
+     * <ul>
+     *     <li>Add a {@link ViewPager.OnPageChangeListener} that will forward events to
+     *     this TabLayout.</li>
+     *     <li>Populate the TabLayout's tabs from the ViewPager's {@link PagerAdapter}.</li>
+     *     <li>Set our {@link TabLayout.OnTabSelectedListener} which will forward
+     *     selected events to the ViewPager</li>
+     * </ul>
+     * </p>
+     *
+     * @see #setTabsFromPagerAdapter(PagerAdapter)
+     * @see TabLayoutOnPageChangeListener
+     * @see ViewPagerOnTabSelectedListener
+     */
+    public void setupWithViewPager(ViewPager viewPager) {
+        final PagerAdapter adapter = viewPager.getAdapter();
+        if (adapter == null) {
+            throw new IllegalArgumentException("ViewPager does not have a PagerAdapter set");
+        }
+
+        // First we'll add Tabs, using the adapter's page titles
+        setTabsFromPagerAdapter(adapter);
+
+        // Now we'll add our page change listener to the ViewPager
+        viewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(this));
+
+        // Now we'll add a tab selected listener to set ViewPager's current item
+        setOnTabSelectedListener(new ViewPagerOnTabSelectedListener(viewPager));
+    }
+
+    /**
+     * Populate our tab content from the given {@link PagerAdapter}.
+     * <p>
+     * Any existing tabs will be removed first. Each tab will have it's text set to the value
+     * returned from {@link PagerAdapter#getPageTitle(int)}
+     * </p>
+     *
+     * @param adapter the adapter to populate from
+     */
+    public void setTabsFromPagerAdapter(PagerAdapter adapter) {
+        removeAllTabs();
+        for (int i = 0, count = adapter.getCount(); i < count; i++) {
+            addTab(newTab().setText(adapter.getPageTitle(i)));
+        }
+    }
+
     private void updateAllTabs() {
         for (int i = 0, z = mTabStrip.getChildCount(); i < z; i++) {
             updateTab(i);
@@ -1421,4 +1427,75 @@
         }
     }
 
+    /**
+     * A {@link ViewPager.OnPageChangeListener} class which contains the
+     * necessary calls back to the provided {@link TabLayout} so that the tab position is
+     * kept in sync.
+     *
+     * <p>This class stores the provided TabLayout weakly, meaning that you can use
+     * {@link ViewPager#addOnPageChangeListener(ViewPager.OnPageChangeListener)
+     * addOnPageChangeListener(OnPageChangeListener)} without removing the listener and
+     * not cause a leak.
+     */
+    public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener {
+        private final WeakReference<TabLayout> mTabLayoutRef;
+        private int mScrollState;
+
+        public TabLayoutOnPageChangeListener(TabLayout tabLayout) {
+            mTabLayoutRef = new WeakReference<>(tabLayout);
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            mScrollState = state;
+        }
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset,
+                int positionOffsetPixels) {
+            final TabLayout tabLayout = mTabLayoutRef.get();
+            if (tabLayout != null) {
+                // Update the scroll position, only update the text selection if we're being
+                // dragged
+                tabLayout.setScrollPosition(position, positionOffset,
+                        mScrollState == ViewPager.SCROLL_STATE_DRAGGING);
+            }
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            final TabLayout tabLayout = mTabLayoutRef.get();
+            if (tabLayout != null) {
+                tabLayout.getTabAt(position).select();
+            }
+        }
+    }
+
+    /**
+     * A {@link TabLayout.OnTabSelectedListener} class which contains the necessary calls back
+     * to the provided {@link ViewPager} so that the tab position is kept in sync.
+     */
+    public static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener {
+        private final ViewPager mViewPager;
+
+        public ViewPagerOnTabSelectedListener(ViewPager viewPager) {
+            mViewPager = viewPager;
+        }
+
+        @Override
+        public void onTabSelected(TabLayout.Tab tab) {
+            mViewPager.setCurrentItem(tab.getPosition());
+        }
+
+        @Override
+        public void onTabUnselected(TabLayout.Tab tab) {
+            // No-op
+        }
+
+        @Override
+        public void onTabReselected(TabLayout.Tab tab) {
+            // No-op
+        }
+    }
+
 }
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 364607b..83ed468 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -39,8 +39,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateInterpolator;
-import android.view.animation.Animation;
-import android.view.animation.Transformation;
 import android.widget.EditText;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -73,11 +71,7 @@
     }
 
     public TextInputLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
+        super(context, attrs);
 
         setOrientation(VERTICAL);
         setWillNotDraw(false);
@@ -100,7 +94,7 @@
         mCollapsingTextHelper.setCollapsedTextVerticalGravity(Gravity.TOP);
 
         final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);
+                R.styleable.TextInputLayout, 0, R.style.Widget_Design_TextInputLayout);
         mHint = a.getText(R.styleable.TextInputLayout_android_hint);
 
         final int hintAppearance = a.getResourceId(
diff --git a/design/src/android/support/design/widget/ViewOffsetBehavior.java b/design/src/android/support/design/widget/ViewOffsetBehavior.java
index a89db5c..3ffc744 100644
--- a/design/src/android/support/design/widget/ViewOffsetBehavior.java
+++ b/design/src/android/support/design/widget/ViewOffsetBehavior.java
@@ -17,7 +17,6 @@
 package android.support.design.widget;
 
 import android.content.Context;
-import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.view.View;
 
@@ -28,6 +27,9 @@
 
     private ViewOffsetHelper mViewOffsetHelper;
 
+    private int mTempTopBottomOffset = 0;
+    private int mTempLeftRightOffset = 0;
+
     public ViewOffsetBehavior() {}
 
     public ViewOffsetBehavior(Context context, AttributeSet attrs) {
@@ -44,12 +46,23 @@
         }
         mViewOffsetHelper.onViewLayout();
 
+        if (mTempTopBottomOffset != 0) {
+            mViewOffsetHelper.setTopAndBottomOffset(mTempTopBottomOffset);
+            mTempTopBottomOffset = 0;
+        }
+        if (mTempLeftRightOffset != 0) {
+            mViewOffsetHelper.setLeftAndRightOffset(mTempLeftRightOffset);
+            mTempLeftRightOffset = 0;
+        }
+
         return true;
     }
 
     public boolean setTopAndBottomOffset(int offset) {
         if (mViewOffsetHelper != null) {
             return mViewOffsetHelper.setTopAndBottomOffset(offset);
+        } else {
+            mTempTopBottomOffset = offset;
         }
         return false;
     }
@@ -57,6 +70,8 @@
     public boolean setLeftAndRightOffset(int offset) {
         if (mViewOffsetHelper != null) {
             return mViewOffsetHelper.setLeftAndRightOffset(offset);
+        } else {
+            mTempLeftRightOffset = offset;
         }
         return false;
     }
diff --git a/percent/Android.mk b/percent/Android.mk
new file mode 100644
index 0000000..5d4cf86
--- /dev/null
+++ b/percent/Android.mk
@@ -0,0 +1,48 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Build the resources using the current SDK version.
+# We do this here because the final static library must be compiled with an older
+# SDK version than the resources.  The resources library and the R class that it
+# contains will not be linked into the final static library.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-percent-res
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAR_EXCLUDE_FILES := none
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Here is the final static library that apps can link against.
+# The R class is automatically excluded from the generated library.
+# Applications that use this library must specify LOCAL_RESOURCE_DIR
+# in their makefiles to include the resources in their package.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-percent
+LOCAL_SDK_VERSION := 7
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES := android-support-percent-res \
+    android-support-v4
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# API Check
+# ---------------------------------------------
+support_module := $(LOCAL_MODULE)
+support_module_api_dir := $(LOCAL_PATH)/api
+support_module_src_files := $(LOCAL_SRC_FILES)
+support_module_java_libraries := $(LOCAL_JAVA_LIBRARIES)
+support_module_java_packages := android.support.percent
+include $(SUPPORT_API_CHECK)
diff --git a/percent/AndroidManifest.xml b/percent/AndroidManifest.xml
new file mode 100644
index 0000000..12a6568
--- /dev/null
+++ b/percent/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.percent">
+    <uses-sdk android:minSdkVersion="7"/>
+    <application />
+</manifest>
diff --git a/percent/api/current.txt b/percent/api/current.txt
new file mode 100644
index 0000000..34d0302
--- /dev/null
+++ b/percent/api/current.txt
@@ -0,0 +1,64 @@
+package android.support.percent {
+
+  public class PercentFrameLayout extends android.widget.FrameLayout {
+    ctor public PercentFrameLayout(android.content.Context);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public static class PercentFrameLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentFrameLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentFrameLayout.LayoutParams(int, int);
+    ctor public PercentFrameLayout.LayoutParams(int, int, int);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+    ctor public PercentFrameLayout.LayoutParams(android.support.percent.PercentFrameLayout.LayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public class PercentLayoutHelper {
+    ctor public PercentLayoutHelper(android.view.ViewGroup);
+    method public void adjustChildren(int, int);
+    method public static void fetchWidthAndHeight(android.view.ViewGroup.LayoutParams, android.content.res.TypedArray, int, int);
+    method public static android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo(android.content.Context, android.util.AttributeSet);
+    method public boolean handleMeasuredStateTooSmall();
+    method public void restoreOriginalParams();
+  }
+
+  public static class PercentLayoutHelper.PercentLayoutInfo {
+    ctor public PercentLayoutHelper.PercentLayoutInfo();
+    method public void fillLayoutParams(android.view.ViewGroup.LayoutParams, int, int);
+    method public void fillMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams, int, int);
+    method public void restoreLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public void restoreMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    field public float bottomMarginPercent;
+    field public float endMarginPercent;
+    field public float heightPercent;
+    field public float leftMarginPercent;
+    field public float rightMarginPercent;
+    field public float startMarginPercent;
+    field public float topMarginPercent;
+    field public float widthPercent;
+  }
+
+  public static abstract interface PercentLayoutHelper.PercentLayoutParams {
+    method public abstract android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+  public class PercentRelativeLayout extends android.widget.RelativeLayout {
+    ctor public PercentRelativeLayout(android.content.Context);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet, int);
+  }
+
+  public static class PercentRelativeLayout.LayoutParams extends android.widget.RelativeLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+    ctor public PercentRelativeLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+    ctor public PercentRelativeLayout.LayoutParams(int, int);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+    ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+    method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+  }
+
+}
+
diff --git a/percent/api/removed.txt b/percent/api/removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/percent/api/removed.txt
diff --git a/percent/build.gradle b/percent/build.gradle
new file mode 100644
index 0000000..28907b8
--- /dev/null
+++ b/percent/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'android-library'
+
+archivesBaseName = 'percent'
+
+dependencies {
+    compile project(':support-v4')
+}
+
+android {
+    compileSdkVersion 'current'
+
+    sourceSets {
+        main.manifest.srcFile 'AndroidManifest.xml'
+        main.java.srcDirs = ['src']
+        main.res.srcDir 'res'
+        main.assets.srcDir 'assets'
+        main.resources.srcDir 'src'
+
+        // this moves src/instrumentTest to tests so all folders follow:
+        // tests/java, tests/res, tests/assets, ...
+        // This is a *reset* so it replaces the default paths
+        androidTest.setRoot('tests')
+        androidTest.java.srcDir 'tests/src'
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
diff --git a/percent/res/values/attrs.xml b/percent/res/values/attrs.xml
new file mode 100644
index 0000000..31154cc
--- /dev/null
+++ b/percent/res/values/attrs.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+<resources>
+    <declare-styleable name="PercentLayout_Layout">
+        <attr name="layout_widthPercent" format="fraction"/>
+
+        <attr name="layout_heightPercent" format="fraction"/>
+
+        <attr name="layout_marginPercent" format="fraction"/>
+        <attr name="layout_marginLeftPercent" format="fraction"/>
+        <attr name="layout_marginTopPercent" format="fraction"/>
+        <attr name="layout_marginRightPercent" format="fraction"/>
+        <attr name="layout_marginBottomPercent" format="fraction"/>
+        <attr name="layout_marginStartPercent" format="fraction"/>
+        <attr name="layout_marginEndPercent" format="fraction"/>
+    </declare-styleable>
+</resources>
+
diff --git a/percent/src/.readme b/percent/src/.readme
new file mode 100644
index 0000000..4bcebad
--- /dev/null
+++ b/percent/src/.readme
@@ -0,0 +1,2 @@
+This hidden file is there to ensure there is an src folder.
+Once we support binary library this will go away.
\ No newline at end of file
diff --git a/percent/src/android/support/percent/PercentFrameLayout.java b/percent/src/android/support/percent/PercentFrameLayout.java
new file mode 100644
index 0000000..994be4c
--- /dev/null
+++ b/percent/src/android/support/percent/PercentFrameLayout.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.percent;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+/**
+ * Subclass of {@link android.widget.FrameLayout} that supports percentage based dimensions and
+ * margins.
+ *
+ * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow
+ * this example:
+ *
+ * <pre class="prettyprint">
+ * &lt;android.support.percent.PercentFrameLayout
+ *         xmlns:android="http://schemas.android.com/apk/res/android"
+ *         xmlns:app="http://schemas.android.com/apk/res-auto"
+ *         android:layout_width="match_parent"
+ *         android:layout_height="match_parent"/&gt
+ *     &lt;ImageView
+ *         app:layout_widthPercent="50%"
+ *         app:layout_heightPercent="50%"
+ *         app:layout_marginTopPercent="25%"
+ *         app:layout_marginLeftPercent="25%"/&gt
+ * &lt;/android.support.percent.PercentFrameLayout/&gt
+ * </pre>
+ *
+ * The attributes that you can use are:
+ * <ul>
+ *     <li>{@code layout_widthPercent}
+ *     <li>{@code layout_heightPercent}
+ *     <li>{@code layout_marginPercent}
+ *     <li>{@code layout_marginLeftPercent}
+ *     <li>{@code layout_marginTopPercent}
+ *     <li>{@code layout_marginRightPercent}
+ *     <li>{@code layout_marginBottomPercent}
+ *     <li>{@code layout_marginStartPercent}
+ *     <li>{@code layout_marginEndPercent}
+ * </ul>
+ *
+ * It is not necessary to specify {@code layout_width/height} if you specify {@code
+ * layout_widthPercent.} However, if you want the view to be able to take up more space than what
+ * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case
+ * if the percentage size is too small for the View's content, it will be resized using
+ * {@code wrap_content} rule.
+ */
+public class PercentFrameLayout extends FrameLayout {
+    private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
+
+    public PercentFrameLayout(Context context) {
+        super(context);
+    }
+
+    public PercentFrameLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public PercentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        if (mHelper.handleMeasuredStateTooSmall()) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        mHelper.restoreOriginalParams();
+    }
+
+    public static class LayoutParams extends FrameLayout.LayoutParams
+            implements PercentLayoutHelper.PercentLayoutParams {
+        private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        public LayoutParams(int width, int height, int gravity) {
+            super(width, height, gravity);
+        }
+
+        public LayoutParams(ViewGroup.LayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(MarginLayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(FrameLayout.LayoutParams source) {
+            super((MarginLayoutParams) source);
+            gravity = source.gravity;
+        }
+
+        public LayoutParams(LayoutParams source) {
+            this((FrameLayout.LayoutParams) source);
+            mPercentLayoutInfo = source.mPercentLayoutInfo;
+        }
+
+        @Override
+        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
+            return mPercentLayoutInfo;
+        }
+
+        @Override
+        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
+            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
+        }
+    }
+}
diff --git a/percent/src/android/support/percent/PercentLayoutHelper.java b/percent/src/android/support/percent/PercentLayoutHelper.java
new file mode 100644
index 0000000..216371f
--- /dev/null
+++ b/percent/src/android/support/percent/PercentLayoutHelper.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.percent;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.v4.view.MarginLayoutParamsCompat;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import android.support.percent.R;
+
+/**
+ * Helper for layouts that want to support percentage based dimensions.
+ *
+ * <p>This class collects utility methods that are involved in extracting percentage based dimension
+ * attributes and applying them to ViewGroup's children. If you would like to implement a layout
+ * that supports percentage based dimensions, you need to take several steps:
+ *
+ * <ol>
+ * <li> You need a {@link ViewGroup.LayoutParams} subclass in your ViewGroup that implements
+ * {@link android.support.percent.PercentLayoutHelper.PercentLayoutParams}.
+ * <li> In your {@code LayoutParams(Context c, AttributeSet attrs)} constructor create an instance
+ * of {@link PercentLayoutHelper.PercentLayoutInfo} by calling
+ * {@link PercentLayoutHelper#getPercentLayoutInfo(Context, AttributeSet)}. Return this
+ * object from {@code public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo()}
+ * method that you implemented for {@link android.support.percent.PercentLayoutHelper.PercentLayoutParams} interface.
+ * <li> Override
+ * {@link ViewGroup.LayoutParams#setBaseAttributes(TypedArray, int, int)}
+ * with a single line implementation {@code PercentLayoutHelper.fetchWidthAndHeight(this, a,
+ * widthAttr, heightAttr);}
+ * <li> In your ViewGroup override {@link ViewGroup#generateLayoutParams(AttributeSet)} to return
+ * your LayoutParams.
+ * <li> In your {@link ViewGroup#onMeasure(int, int)} override, you need to implement following
+ * pattern:
+ * <pre class="prettyprint">
+ * protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ *     mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
+ *     super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ *     if (mHelper.handleMeasuredStateTooSmall()) {
+ *         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ *     }
+ * }
+ * </pre>
+ * <li>In your {@link ViewGroup#onLayout(boolean, int, int, int, int)} override, you need to
+ * implement following pattern:
+ * <pre class="prettyprint">
+ * protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ *     super.onLayout(changed, left, top, right, bottom);
+ *     mHelper.restoreOriginalParams();
+ * }
+ * </pre>
+ * </ol>
+ */
+public class PercentLayoutHelper {
+    private static final String TAG = "PercentLayout";
+
+    private final ViewGroup mHost;
+
+    public PercentLayoutHelper(ViewGroup host) {
+        mHost = host;
+    }
+
+    /**
+     * Helper method to be called from {@link ViewGroup.LayoutParams#setBaseAttributes} override
+     * that reads layout_width and layout_height attribute values without throwing an exception if
+     * they aren't present.
+     */
+    public static void fetchWidthAndHeight(ViewGroup.LayoutParams params, TypedArray array,
+            int widthAttr, int heightAttr) {
+        params.width = array.getLayoutDimension(widthAttr, 0);
+        params.height = array.getLayoutDimension(heightAttr, 0);
+    }
+
+    /**
+     * Iterates over children and changes their width and height to one calculated from percentage
+     * values.
+     * @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup.
+     * @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
+     */
+    public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "adjustChildren: " + mHost + " widthMeasureSpec: "
+                    + View.MeasureSpec.toString(widthMeasureSpec) + " heightMeasureSpec: "
+                    + View.MeasureSpec.toString(heightMeasureSpec));
+        }
+        int widthHint = View.MeasureSpec.getSize(widthMeasureSpec);
+        int heightHint = View.MeasureSpec.getSize(heightMeasureSpec);
+        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
+            View view = mHost.getChildAt(i);
+            ViewGroup.LayoutParams params = view.getLayoutParams();
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "should adjust " + view + " " + params);
+            }
+            if (params instanceof PercentLayoutParams) {
+                PercentLayoutInfo info =
+                        ((PercentLayoutParams) params).getPercentLayoutInfo();
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "using " + info);
+                }
+                if (info != null) {
+                    if (params instanceof ViewGroup.MarginLayoutParams) {
+                        info.fillMarginLayoutParams((ViewGroup.MarginLayoutParams) params,
+                                widthHint, heightHint);
+                    } else {
+                        info.fillLayoutParams(params, widthHint, heightHint);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Constructs a PercentLayoutInfo from attributes associated with a View. Call this method from
+     * {@code LayoutParams(Context c, AttributeSet attrs)} constructor.
+     */
+    public static PercentLayoutInfo getPercentLayoutInfo(Context context,
+            AttributeSet attrs) {
+        PercentLayoutInfo info = null;
+        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);
+        float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1,
+                -1f);
+        if (value != -1f) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "percent width: " + value);
+            }
+            info = info != null ? info : new PercentLayoutInfo();
+            info.widthPercent = value;
+        }
+        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f);
+        if (value != -1f) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "percent height: " + value);
+            }
+            info = info != null ? info : new PercentLayoutInfo();
+            info.heightPercent = value;
+        }
+        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);
+        if (value != -1f) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "percent margin: " + value);
+            }
+            info = info != null ? info : new PercentLayoutInfo();
+            info.leftMarginPercent = value;
+            info.topMarginPercent = value;
+            info.rightMarginPercent = value;
+            info.bottomMarginPercent = value;
+        }
+        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1,
+                -1f);
+        if (value != -1f) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "percent left margin: " + value);
+            }
+            info = info != null ? info : new PercentLayoutInfo();
+            info.leftMarginPercent = value;
+        }
+        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1,
+                -1f);
+        if (value != -1f) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "percent top margin: " + value);
+            }
+            info = info != null ? info : new PercentLayoutInfo();
+            info.topMarginPercent = value;
+        }
+        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1,
+                -1f);
+        if (value != -1f) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "percent right margin: " + value);
+            }
+            info = info != null ? info : new PercentLayoutInfo();
+            info.rightMarginPercent = value;
+        }
+        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1,
+                -1f);
+        if (value != -1f) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "percent bottom margin: " + value);
+            }
+            info = info != null ? info : new PercentLayoutInfo();
+            info.bottomMarginPercent = value;
+        }
+        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1,
+                -1f);
+        if (value != -1f) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "percent start margin: " + value);
+            }
+            info = info != null ? info : new PercentLayoutInfo();
+            info.startMarginPercent = value;
+        }
+        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1,
+                -1f);
+        if (value != -1f) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "percent end margin: " + value);
+            }
+            info = info != null ? info : new PercentLayoutInfo();
+            info.endMarginPercent = value;
+        }
+        array.recycle();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "constructed: " + info);
+        }
+        return info;
+    }
+
+    /**
+     * Iterates over children and restores their original dimensions that were changed for
+     * percentage values. Calling this method only makes sense if you previously called
+     * {@link PercentLayoutHelper#adjustChildren(int, int)}.
+     */
+    public void restoreOriginalParams() {
+        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
+            View view = mHost.getChildAt(i);
+            ViewGroup.LayoutParams params = view.getLayoutParams();
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "should restore " + view + " " + params);
+            }
+            if (params instanceof PercentLayoutParams) {
+                PercentLayoutInfo info =
+                        ((PercentLayoutParams) params).getPercentLayoutInfo();
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "using " + info);
+                }
+                if (info != null) {
+                    if (params instanceof ViewGroup.MarginLayoutParams) {
+                        info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params);
+                    } else {
+                        info.restoreLayoutParams(params);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Iterates over children and checks if any of them would like to get more space than it
+     * received through the percentage dimension.
+     *
+     * If you are building a layout that supports percentage dimensions you are encouraged to take
+     * advantage of this method. The developer should be able to specify that a child should be
+     * remeasured by adding normal dimension attribute with {@code wrap_content} value. For example
+     * he might specify child's attributes as {@code app:layout_widthPercent="60%p"} and
+     * {@code android:layout_width="wrap_content"}. In this case if the child receives too little
+     * space, it will be remeasured with width set to {@code WRAP_CONTENT}.
+     *
+     * @return True if the measure phase needs to be rerun because one of the children would like
+     * to receive more space.
+     */
+    public boolean handleMeasuredStateTooSmall() {
+        boolean needsSecondMeasure = false;
+        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
+            View view = mHost.getChildAt(i);
+            ViewGroup.LayoutParams params = view.getLayoutParams();
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "should handle measured state too small " + view + " " + params);
+            }
+            if (params instanceof PercentLayoutParams) {
+                PercentLayoutInfo info =
+                        ((PercentLayoutParams) params).getPercentLayoutInfo();
+                if (info != null) {
+                    if (shouldHandleMeasuredWidthTooSmall(view, info)) {
+                        needsSecondMeasure = true;
+                        params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+                    }
+                    if (shouldHandleMeasuredHeightTooSmall(view, info)) {
+                        needsSecondMeasure = true;
+                        params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+                    }
+                }
+            }
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);
+        }
+        return needsSecondMeasure;
+    }
+
+    private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) {
+        int state = ViewCompat.getMeasuredWidthAndState(view) & ViewCompat.MEASURED_STATE_MASK;
+        return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0 &&
+                info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;
+    }
+
+    private static boolean shouldHandleMeasuredHeightTooSmall(View view, PercentLayoutInfo info) {
+        int state = ViewCompat.getMeasuredHeightAndState(view) & ViewCompat.MEASURED_STATE_MASK;
+        return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.heightPercent >= 0 &&
+                info.mPreservedParams.height == ViewGroup.LayoutParams.WRAP_CONTENT;
+    }
+
+    /**
+     * Container for information about percentage dimensions and margins. It acts as an extension
+     * for {@code LayoutParams}.
+     */
+    public static class PercentLayoutInfo {
+        public float widthPercent;
+
+        public float heightPercent;
+
+        public float leftMarginPercent;
+
+        public float topMarginPercent;
+
+        public float rightMarginPercent;
+
+        public float bottomMarginPercent;
+
+        public float startMarginPercent;
+
+        public float endMarginPercent;
+
+        /* package */ final ViewGroup.MarginLayoutParams mPreservedParams;
+
+        public PercentLayoutInfo() {
+            widthPercent = -1f;
+            heightPercent = -1f;
+            leftMarginPercent = -1f;
+            topMarginPercent = -1f;
+            rightMarginPercent = -1f;
+            bottomMarginPercent = -1f;
+            startMarginPercent = -1f;
+            endMarginPercent = -1f;
+            mPreservedParams = new ViewGroup.MarginLayoutParams(0, 0);
+        }
+
+        /**
+         * Fills {@code ViewGroup.LayoutParams} dimensions based on percentage values.
+         */
+        public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint,
+                int heightHint) {
+            // Preserve the original layout params, so we can restore them after the measure step.
+            mPreservedParams.width = params.width;
+            mPreservedParams.height = params.height;
+
+            if (widthPercent >= 0) {
+                params.width = (int) (widthHint * widthPercent);
+            }
+            if (heightPercent >= 0) {
+                params.height = (int) (heightHint * heightPercent);
+            }
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
+            }
+        }
+
+        /**
+         * Fills {@code ViewGroup.MarginLayoutParams} dimensions and margins based on percentage
+         * values.
+         */
+        public void fillMarginLayoutParams(ViewGroup.MarginLayoutParams params, int widthHint,
+                int heightHint) {
+            fillLayoutParams(params, widthHint, heightHint);
+
+            // Preserver the original margins, so we can restore them after the measure step.
+            mPreservedParams.leftMargin = params.leftMargin;
+            mPreservedParams.topMargin = params.topMargin;
+            mPreservedParams.rightMargin = params.rightMargin;
+            mPreservedParams.bottomMargin = params.bottomMargin;
+            MarginLayoutParamsCompat.setMarginStart(mPreservedParams,
+                    MarginLayoutParamsCompat.getMarginStart(params));
+            MarginLayoutParamsCompat.setMarginEnd(mPreservedParams,
+                    MarginLayoutParamsCompat.getMarginEnd(params));
+
+            if (leftMarginPercent >= 0) {
+                params.leftMargin = (int) (widthHint * leftMarginPercent);
+            }
+            if (topMarginPercent >= 0) {
+                params.topMargin = (int) (heightHint * topMarginPercent);
+            }
+            if (rightMarginPercent >= 0) {
+                params.rightMargin = (int) (widthHint * rightMarginPercent);
+            }
+            if (bottomMarginPercent >= 0) {
+                params.bottomMargin = (int) (heightHint * bottomMarginPercent);
+            }
+            if (startMarginPercent >= 0) {
+                MarginLayoutParamsCompat.setMarginStart(params,
+                        (int) (widthHint * startMarginPercent));
+            }
+            if (endMarginPercent >= 0) {
+                MarginLayoutParamsCompat.setMarginEnd(params,
+                        (int) (widthHint * endMarginPercent));
+            }
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "after fillMarginLayoutParams: (" + params.width + ", " + params.height
+                        + ")");
+            }
+        }
+
+        @Override
+        public String toString() {
+            return String.format("PercentLayoutInformation width: %f height %f, margins (%f, %f, "
+                            + " %f, %f, %f, %f)", widthPercent, heightPercent, leftMarginPercent,
+                    topMarginPercent, rightMarginPercent, bottomMarginPercent, startMarginPercent,
+                    endMarginPercent);
+
+        }
+
+        /**
+         * Restores original dimensions and margins after they were changed for percentage based
+         * values. Calling this method only makes sense if you previously called
+         * {@link PercentLayoutHelper.PercentLayoutInfo#fillMarginLayoutParams}.
+         */
+        public void restoreMarginLayoutParams(ViewGroup.MarginLayoutParams params) {
+            restoreLayoutParams(params);
+            params.leftMargin = mPreservedParams.leftMargin;
+            params.topMargin = mPreservedParams.topMargin;
+            params.rightMargin = mPreservedParams.rightMargin;
+            params.bottomMargin = mPreservedParams.bottomMargin;
+            MarginLayoutParamsCompat.setMarginStart(params,
+                    MarginLayoutParamsCompat.getMarginStart(mPreservedParams));
+            MarginLayoutParamsCompat.setMarginEnd(params,
+                    MarginLayoutParamsCompat.getMarginEnd(mPreservedParams));
+        }
+
+        /**
+         * Restores original dimensions after they were changed for percentage based values. Calling
+         * this method only makes sense if you previously called
+         * {@link PercentLayoutHelper.PercentLayoutInfo#fillLayoutParams}.
+         */
+        public void restoreLayoutParams(ViewGroup.LayoutParams params) {
+            params.width = mPreservedParams.width;
+            params.height = mPreservedParams.height;
+        }
+    }
+
+    /**
+     * If a layout wants to support percentage based dimensions and use this helper class, its
+     * {@code LayoutParams} subclass must implement this interface.
+     *
+     * Your {@code LayoutParams} subclass should contain an instance of {@code PercentLayoutInfo}
+     * and the implementation of this interface should be a simple accessor.
+     */
+    public interface PercentLayoutParams {
+        PercentLayoutInfo getPercentLayoutInfo();
+    }
+}
diff --git a/percent/src/android/support/percent/PercentRelativeLayout.java b/percent/src/android/support/percent/PercentRelativeLayout.java
new file mode 100644
index 0000000..1a06b8d
--- /dev/null
+++ b/percent/src/android/support/percent/PercentRelativeLayout.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.percent;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+
+/**
+ * Subclass of {@link android.widget.RelativeLayout} that supports percentage based dimensions and
+ * margins.
+ *
+ * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow
+ * this example:
+ *
+ * <pre class="prettyprint">
+ * &lt;android.support.percent.PercentRelativeLayout
+ *         xmlns:android="http://schemas.android.com/apk/res/android"
+ *         xmlns:app="http://schemas.android.com/apk/res-auto"
+ *         android:layout_width="match_parent"
+ *         android:layout_height="match_parent"/&gt
+ *     &lt;ImageView
+ *         app:layout_widthPercent="50%"
+ *         app:layout_heightPercent="50%"
+ *         app:layout_marginTopPercent="25%"
+ *         app:layout_marginLeftPercent="25%"/&gt
+ * &lt;/android.support.percent.PercentFrameLayout/&gt
+ * </pre>
+ *
+ * The attributes that you can use are:
+ * <ul>
+ *     <li>{@code layout_widthPercent}
+ *     <li>{@code layout_heightPercent}
+ *     <li>{@code layout_marginPercent}
+ *     <li>{@code layout_marginLeftPercent}
+ *     <li>{@code layout_marginTopPercent}
+ *     <li>{@code layout_marginRightPercent}
+ *     <li>{@code layout_marginBottomPercent}
+ *     <li>{@code layout_marginStartPercent}
+ *     <li>{@code layout_marginEndPercent}
+ * </ul>
+ *
+ * It is not necessary to specify {@code layout_width/height} if you specify {@code
+ * layout_widthPercent.} However, if you want the view to be able to take up more space than what
+ * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case
+ * if the percentage size is too small for the View's content, it will be resized using
+ * {@code wrap_content} rule.
+ */
+public class PercentRelativeLayout extends RelativeLayout {
+    private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
+
+    public PercentRelativeLayout(Context context) {
+        super(context);
+    }
+
+    public PercentRelativeLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public PercentRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        if (mHelper.handleMeasuredStateTooSmall()) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        mHelper.restoreOriginalParams();
+    }
+
+    public static class LayoutParams extends RelativeLayout.LayoutParams
+            implements PercentLayoutHelper.PercentLayoutParams {
+        private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        public LayoutParams(ViewGroup.LayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(MarginLayoutParams source) {
+            super(source);
+        }
+
+        @Override
+        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
+            return mPercentLayoutInfo;
+        }
+
+        @Override
+        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
+            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
+        }
+    }
+}
diff --git a/settings.gradle b/settings.gradle
index c4fdee2..85c9ae1 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -39,3 +39,6 @@
 
 include ':support-design'
 project(':support-design').projectDir = new File(rootDir, 'design')
+
+include ':support-percent'
+project(':support-percent').projectDir = new File(rootDir, 'percent')
diff --git a/v4/api/current.txt b/v4/api/current.txt
index 2bc5d17..274a7b3 100644
--- a/v4/api/current.txt
+++ b/v4/api/current.txt
@@ -2449,9 +2449,11 @@
   public class ViewPager extends android.view.ViewGroup {
     ctor public ViewPager(android.content.Context);
     ctor public ViewPager(android.content.Context, android.util.AttributeSet);
+    method public void addOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
     method public boolean arrowScroll(int);
     method public boolean beginFakeDrag();
     method protected boolean canScroll(android.view.View, boolean, int, int, int);
+    method public void clearOnPageChangeListeners();
     method public void endFakeDrag();
     method public boolean executeKeyEvent(android.view.KeyEvent);
     method public void fakeDragBy(float);
@@ -2464,11 +2466,12 @@
     method protected void onPageScrolled(int, float, int);
     method public void onRestoreInstanceState(android.os.Parcelable);
     method public android.os.Parcelable onSaveInstanceState();
+    method public void removeOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
     method public void setAdapter(android.support.v4.view.PagerAdapter);
     method public void setCurrentItem(int);
     method public void setCurrentItem(int, boolean);
     method public void setOffscreenPageLimit(int);
-    method public void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+    method public deprecated void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
     method public void setPageMargin(int);
     method public void setPageMarginDrawable(android.graphics.drawable.Drawable);
     method public void setPageMarginDrawable(int);
diff --git a/v4/java/android/support/v4/view/ViewPager.java b/v4/java/android/support/v4/view/ViewPager.java
index dc20cb6..f19104a 100644
--- a/v4/java/android/support/v4/view/ViewPager.java
+++ b/v4/java/android/support/v4/view/ViewPager.java
@@ -56,6 +56,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.List;
 
 /**
  * Layout manager that allows the user to flip left and right
@@ -214,6 +215,7 @@
     private boolean mCalledSuper;
     private int mDecorChildCount;
 
+    private List<OnPageChangeListener> mOnPageChangeListeners;
     private OnPageChangeListener mOnPageChangeListener;
     private OnPageChangeListener mInternalPageChangeListener;
     private OnAdapterChangeListener mAdapterChangeListener;
@@ -399,9 +401,7 @@
             // PageTransformers can do complex things that benefit from hardware layers.
             enableLayers(newState != SCROLL_STATE_IDLE);
         }
-        if (mOnPageChangeListener != null) {
-            mOnPageChangeListener.onPageScrollStateChanged(newState);
-        }
+        dispatchOnScrollStateChanged(newState);
     }
 
     /**
@@ -544,11 +544,8 @@
             // We don't have any idea how big we are yet and shouldn't have any pages either.
             // Just set things up and let the pending layout handle things.
             mCurItem = item;
-            if (dispatchSelected && mOnPageChangeListener != null) {
-                mOnPageChangeListener.onPageSelected(item);
-            }
-            if (dispatchSelected && mInternalPageChangeListener != null) {
-                mInternalPageChangeListener.onPageSelected(item);
+            if (dispatchSelected) {
+                dispatchOnPageSelected(item);
             }
             requestLayout();
         } else {
@@ -568,18 +565,12 @@
         }
         if (smoothScroll) {
             smoothScrollTo(destX, 0, velocity);
-            if (dispatchSelected && mOnPageChangeListener != null) {
-                mOnPageChangeListener.onPageSelected(item);
-            }
-            if (dispatchSelected && mInternalPageChangeListener != null) {
-                mInternalPageChangeListener.onPageSelected(item);
+            if (dispatchSelected) {
+                dispatchOnPageSelected(item);
             }
         } else {
-            if (dispatchSelected && mOnPageChangeListener != null) {
-                mOnPageChangeListener.onPageSelected(item);
-            }
-            if (dispatchSelected && mInternalPageChangeListener != null) {
-                mInternalPageChangeListener.onPageSelected(item);
+            if (dispatchSelected) {
+                dispatchOnPageSelected(item);
             }
             completeScroll(false);
             scrollTo(destX, 0);
@@ -592,12 +583,54 @@
      * scrolled. See {@link OnPageChangeListener}.
      *
      * @param listener Listener to set
+     *
+     * @deprecated Use {@link #addOnPageChangeListener(OnPageChangeListener)}
+     * and {@link #removeOnPageChangeListener(OnPageChangeListener)} instead.
      */
+    @Deprecated
     public void setOnPageChangeListener(OnPageChangeListener listener) {
         mOnPageChangeListener = listener;
     }
 
     /**
+     * Add a listener that will be invoked whenever the page changes or is incrementally
+     * scrolled. See {@link OnPageChangeListener}.
+     *
+     * <p>Components that add a listener should take care to remove it when finished.
+     * Other components that take ownership of a view may call {@link #clearOnPageChangeListeners()}
+     * to remove all attached listeners.</p>
+     *
+     * @param listener listener to add
+     */
+    public void addOnPageChangeListener(OnPageChangeListener listener) {
+        if (mOnPageChangeListeners == null) {
+            mOnPageChangeListeners = new ArrayList<>();
+        }
+        mOnPageChangeListeners.add(listener);
+    }
+
+    /**
+     * Remove a listener that was previously added via
+     * {@link #addOnPageChangeListener(OnPageChangeListener)}.
+     *
+     * @param listener listener to remove
+     */
+    public void removeOnPageChangeListener(OnPageChangeListener listener) {
+        if (mOnPageChangeListeners != null) {
+            mOnPageChangeListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Remove all listeners that are notified of any changes in scroll state or position.
+     */
+    public void clearOnPageChangeListeners() {
+        if (mOnPageChangeListeners != null) {
+            mOnPageChangeListeners.clear();
+        }
+    }
+
+    /**
      * Set a {@link PageTransformer} that will be called for each attached page whenever
      * the scroll position is changed. This allows the application to apply custom property
      * transformations to each page, overriding the default sliding look and feel.
@@ -1714,12 +1747,7 @@
             }
         }
 
-        if (mOnPageChangeListener != null) {
-            mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
-        }
-        if (mInternalPageChangeListener != null) {
-            mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
-        }
+        dispatchOnPageScrolled(position, offset, offsetPixels);
 
         if (mPageTransformer != null) {
             final int scrollX = getScrollX();
@@ -1738,6 +1766,57 @@
         mCalledSuper = true;
     }
 
+    private void dispatchOnPageScrolled(int position, float offset, int offsetPixels) {
+        if (mOnPageChangeListener != null) {
+            mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
+        }
+        if (mOnPageChangeListeners != null) {
+            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
+                OnPageChangeListener listener = mOnPageChangeListeners.get(i);
+                if (listener != null) {
+                    listener.onPageScrolled(position, offset, offsetPixels);
+                }
+            }
+        }
+        if (mInternalPageChangeListener != null) {
+            mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
+        }
+    }
+
+    private void dispatchOnPageSelected(int position) {
+        if (mOnPageChangeListener != null) {
+            mOnPageChangeListener.onPageSelected(position);
+        }
+        if (mOnPageChangeListeners != null) {
+            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
+                OnPageChangeListener listener = mOnPageChangeListeners.get(i);
+                if (listener != null) {
+                    listener.onPageSelected(position);
+                }
+            }
+        }
+        if (mInternalPageChangeListener != null) {
+            mInternalPageChangeListener.onPageSelected(position);
+        }
+    }
+
+    private void dispatchOnScrollStateChanged(int state) {
+        if (mOnPageChangeListener != null) {
+            mOnPageChangeListener.onPageScrollStateChanged(state);
+        }
+        if (mOnPageChangeListeners != null) {
+            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
+                OnPageChangeListener listener = mOnPageChangeListeners.get(i);
+                if (listener != null) {
+                    listener.onPageScrollStateChanged(state);
+                }
+            }
+        }
+        if (mInternalPageChangeListener != null) {
+            mInternalPageChangeListener.onPageScrollStateChanged(state);
+        }
+    }
+
     private void completeScroll(boolean postEvents) {
         boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;
         if (needPopulate) {
diff --git a/v4/java/android/support/v4/widget/MaterialProgressDrawable.java b/v4/java/android/support/v4/widget/MaterialProgressDrawable.java
index c054404..db46186 100644
--- a/v4/java/android/support/v4/widget/MaterialProgressDrawable.java
+++ b/v4/java/android/support/v4/widget/MaterialProgressDrawable.java
@@ -492,7 +492,7 @@
         private int mArrowWidth;
         private int mArrowHeight;
         private int mAlpha;
-        private final Paint mCirclePaint = new Paint();
+        private final Paint mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         private int mBackgroundColor;
         private int mCurrentColor;
 
diff --git a/v7/appcompat/api/current.txt b/v7/appcompat/api/current.txt
index e88c4d7..4973452 100644
--- a/v7/appcompat/api/current.txt
+++ b/v7/appcompat/api/current.txt
@@ -374,6 +374,7 @@
     field public static int backgroundTint;
     field public static int backgroundTintMode;
     field public static int barSize;
+    field public static int borderlessButtonStyle;
     field public static int buttonBarButtonStyle;
     field public static int buttonBarNegativeButtonStyle;
     field public static int buttonBarNeutralButtonStyle;
@@ -742,7 +743,7 @@
     field public static int abc_switch_track_mtrl_alpha;
     field public static int abc_tab_indicator_material;
     field public static int abc_tab_indicator_mtrl_alpha;
-    field public static int abc_text_cursor_mtrl_alpha;
+    field public static int abc_text_cursor_material;
     field public static int abc_textfield_activated_mtrl_alpha;
     field public static int abc_textfield_default_mtrl_alpha;
     field public static int abc_textfield_search_activated_mtrl_alpha;
@@ -995,6 +996,8 @@
     field public static int Base_Theme_AppCompat_Light_Dialog_MinWidth;
     field public static int Base_V11_Theme_AppCompat_Dialog;
     field public static int Base_V11_Theme_AppCompat_Light_Dialog;
+    field public static int Base_V12_Widget_AppCompat_AutoCompleteTextView;
+    field public static int Base_V12_Widget_AppCompat_EditText;
     field public static int Base_V21_Theme_AppCompat;
     field public static int Base_V21_Theme_AppCompat_Dialog;
     field public static int Base_V21_Theme_AppCompat_Light;
@@ -1003,6 +1006,8 @@
     field public static int Base_V7_Theme_AppCompat_Dialog;
     field public static int Base_V7_Theme_AppCompat_Light;
     field public static int Base_V7_Theme_AppCompat_Light_Dialog;
+    field public static int Base_V7_Widget_AppCompat_AutoCompleteTextView;
+    field public static int Base_V7_Widget_AppCompat_EditText;
     field public static int Base_Widget_AppCompat_ActionBar;
     field public static int Base_Widget_AppCompat_ActionBar_Solid;
     field public static int Base_Widget_AppCompat_ActionBar_TabBar;
@@ -1060,8 +1065,6 @@
     field public static int Platform_ThemeOverlay_AppCompat_Light;
     field public static int Platform_V11_AppCompat;
     field public static int Platform_V11_AppCompat_Light;
-    field public static int Platform_V12_AppCompat;
-    field public static int Platform_V12_AppCompat_Light;
     field public static int Platform_V14_AppCompat;
     field public static int Platform_V14_AppCompat_Light;
     field public static int RtlOverlay_DialogWindowTitle_AppCompat;
@@ -1423,6 +1426,7 @@
     field public static int Theme_android_windowAnimationStyle;
     field public static int Theme_android_windowIsFloating;
     field public static int Theme_autoCompleteTextViewStyle;
+    field public static int Theme_borderlessButtonStyle;
     field public static int Theme_buttonBarButtonStyle;
     field public static int Theme_buttonBarNegativeButtonStyle;
     field public static int Theme_buttonBarNeutralButtonStyle;
diff --git a/v7/appcompat/res-public/values/public_attrs.xml b/v7/appcompat/res-public/values/public_attrs.xml
index d70b9d1..3ded592 100644
--- a/v7/appcompat/res-public/values/public_attrs.xml
+++ b/v7/appcompat/res-public/values/public_attrs.xml
@@ -57,6 +57,7 @@
      <public type="attr" name="backgroundTint"/>
      <public type="attr" name="backgroundTintMode"/>
      <public type="attr" name="barSize"/>
+     <public type="attr" name="borderlessButtonStyle"/>
      <public type="attr" name="buttonBarButtonStyle"/>
      <public type="attr" name="buttonBarNegativeButtonStyle"/>
      <public type="attr" name="buttonBarNeutralButtonStyle"/>
diff --git a/v7/appcompat/res/drawable-hdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 5e0bf84..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 36348a8..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 666b10a..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 08ee2b4..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable/abc_text_cursor_material.xml b/v7/appcompat/res/drawable/abc_text_cursor_material.xml
new file mode 100644
index 0000000..885670c
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_text_cursor_material.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <size android:height="2dp"
+          android:width="2dp"/>
+    <solid android:color="@android:color/white"/>
+</shape>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-v12/themes_base.xml b/v7/appcompat/res/values-v12/styles_base.xml
similarity index 63%
rename from v7/appcompat/res/values-v12/themes_base.xml
rename to v7/appcompat/res/values-v12/styles_base.xml
index c912434..f7965a4 100644
--- a/v7/appcompat/res/values-v12/themes_base.xml
+++ b/v7/appcompat/res/values-v12/styles_base.xml
@@ -16,15 +16,16 @@
 
 <resources>
 
-    <style name="Platform.AppCompat" parent="Platform.V12.AppCompat" />
-    <style name="Platform.AppCompat.Light" parent="Platform.V12.AppCompat.Light" />
+    <style name="Base.Widget.AppCompat.EditText" parent="Base.V12.Widget.AppCompat.EditText" />
 
-    <style name="Platform.V12.AppCompat" parent="Platform.V11.AppCompat">
-        <item name="android:textCursorDrawable">@drawable/abc_text_cursor_mtrl_alpha</item>
+    <style name="Base.V12.Widget.AppCompat.EditText" parent="Base.V7.Widget.AppCompat.EditText">
+        <item name="android:textCursorDrawable">@drawable/abc_text_cursor_material</item>
     </style>
 
-    <style name="Platform.V12.AppCompat.Light" parent="Platform.V11.AppCompat.Light">
-        <item name="android:textCursorDrawable">@drawable/abc_text_cursor_mtrl_alpha</item>
+    <style name="Base.Widget.AppCompat.AutoCompleteTextView" parent="Base.V12.Widget.AppCompat.AutoCompleteTextView" />
+
+    <style name="Base.V12.Widget.AppCompat.AutoCompleteTextView" parent="Base.V7.Widget.AppCompat.AutoCompleteTextView">
+        <item name="android:textCursorDrawable">@drawable/abc_text_cursor_material</item>
     </style>
 
 </resources>
diff --git a/v7/appcompat/res/values-v14/themes_base.xml b/v7/appcompat/res/values-v14/themes_base.xml
index 5fdc73c..97b7aaa 100644
--- a/v7/appcompat/res/values-v14/themes_base.xml
+++ b/v7/appcompat/res/values-v14/themes_base.xml
@@ -19,14 +19,14 @@
     <style name="Platform.AppCompat" parent="Platform.V14.AppCompat" />
     <style name="Platform.AppCompat.Light" parent="Platform.V14.AppCompat.Light" />
 
-    <style name="Platform.V14.AppCompat" parent="Platform.V12.AppCompat">
+    <style name="Platform.V14.AppCompat" parent="Platform.V11.AppCompat">
         <item name="android:actionModeSelectAllDrawable">?actionModeSelectAllDrawable</item>
 
         <item name="android:listPreferredItemPaddingLeft">@dimen/abc_list_item_padding_horizontal_material</item>
         <item name="android:listPreferredItemPaddingRight">@dimen/abc_list_item_padding_horizontal_material</item>
     </style>
 
-    <style name="Platform.V14.AppCompat.Light" parent="Platform.V12.AppCompat.Light">
+    <style name="Platform.V14.AppCompat.Light" parent="Platform.V11.AppCompat.Light">
         <item name="android:actionModeSelectAllDrawable">?actionModeSelectAllDrawable</item>
 
         <item name="android:listPreferredItemPaddingLeft">@dimen/abc_list_item_padding_horizontal_material</item>
diff --git a/v7/appcompat/res/values-v21/themes_base.xml b/v7/appcompat/res/values-v21/themes_base.xml
index a8c3df4..06b33b1 100644
--- a/v7/appcompat/res/values-v21/themes_base.xml
+++ b/v7/appcompat/res/values-v21/themes_base.xml
@@ -64,6 +64,7 @@
         <!-- General view attributes -->
         <item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
         <item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackgroundBorderless</item>
+        <item name="borderlessButtonStyle">?android:borderlessButtonStyle</item>
         <item name="dividerHorizontal">?android:attr/dividerHorizontal</item>
         <item name="dividerVertical">?android:attr/dividerVertical</item>
         <item name="editTextBackground">?android:attr/editTextBackground</item>
@@ -113,6 +114,7 @@
         <!-- General view attributes -->
         <item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
         <item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackgroundBorderless</item>
+        <item name="borderlessButtonStyle">?android:borderlessButtonStyle</item>
         <item name="dividerHorizontal">?android:attr/dividerHorizontal</item>
         <item name="dividerVertical">?android:attr/dividerVertical</item>
         <item name="editTextBackground">?android:attr/editTextBackground</item>
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index 17286fb..6a6870c 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -215,6 +215,8 @@
         <attr name="selectableItemBackground" format="reference"/>
         <!-- Background drawable for borderless standalone items that need focus/pressed states. -->
         <attr name="selectableItemBackgroundBorderless" format="reference" />
+        <!-- Style for buttons without an explicit border, often used in groups. -->
+        <attr name="borderlessButtonStyle" format="reference" />
         <!-- A drawable that may be used as a vertical divider between visual elements. -->
         <attr name="dividerVertical" format="reference"/>
         <!-- A drawable that may be used as a horizontal divider between visual elements. -->
diff --git a/v7/appcompat/res/values/styles_base.xml b/v7/appcompat/res/values/styles_base.xml
index f445e73..0feb530 100644
--- a/v7/appcompat/res/values/styles_base.xml
+++ b/v7/appcompat/res/values/styles_base.xml
@@ -287,7 +287,9 @@
         <item name="android:textColor">?android:textColorSecondary</item>
     </style>
 
-    <style name="Base.Widget.AppCompat.AutoCompleteTextView" parent="android:Widget.AutoCompleteTextView">
+    <style name="Base.Widget.AppCompat.AutoCompleteTextView" parent="Base.V7.Widget.AppCompat.AutoCompleteTextView" />
+
+    <style name="Base.V7.Widget.AppCompat.AutoCompleteTextView" parent="android:Widget.AutoCompleteTextView">
         <item name="android:dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
         <item name="android:popupBackground">@drawable/abc_popup_background_mtrl_mult</item>
         <item name="android:background">?attr/editTextBackground</item>
@@ -351,11 +353,14 @@
         <item name="defaultQueryHint">@string/abc_search_hint</item>
     </style>
 
-    <style name="Base.Widget.AppCompat.EditText" parent="android:Widget.EditText">
+    <style name="Base.Widget.AppCompat.EditText" parent="Base.V7.Widget.AppCompat.EditText" />
+
+    <style name="Base.V7.Widget.AppCompat.EditText" parent="android:Widget.EditText">
         <item name="android:background">?attr/editTextBackground</item>
         <item name="android:textColor">?attr/editTextColor</item>
         <item name="android:textAppearance">?android:attr/textAppearanceMediumInverse</item>
     </style>
+
     <!-- contains values used in all dpis -->
     <style name="Base.Widget.AppCompat.DrawerArrowToggle.Common" parent="">
         <item name="color">?android:attr/textColorSecondary</item>
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index 0f3732e..3e2c841 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -119,6 +119,7 @@
 
         <item name="selectableItemBackground">@drawable/abc_item_background_holo_dark</item>
         <item name="selectableItemBackgroundBorderless">?attr/selectableItemBackground</item>
+        <item name="borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
         <item name="homeAsUpIndicator">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
 
         <item name="dividerVertical">@drawable/abc_list_divider_mtrl_alpha</item>
@@ -266,6 +267,7 @@
 
         <item name="selectableItemBackground">@drawable/abc_item_background_holo_light</item>
         <item name="selectableItemBackgroundBorderless">?attr/selectableItemBackground</item>
+        <item name="borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
         <item name="homeAsUpIndicator">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
 
         <item name="dividerVertical">@drawable/abc_list_divider_mtrl_alpha</item>
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/ActivityChooserModel.java b/v7/appcompat/src/android/support/v7/internal/widget/ActivityChooserModel.java
index d97f8e7..1cdbbbd 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/ActivityChooserModel.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ActivityChooserModel.java
@@ -37,6 +37,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -972,7 +973,7 @@
         }
         try {
             XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(fis, null);
+            parser.setInput(fis, StandardCharsets.UTF_8.name());
 
             int type = XmlPullParser.START_DOCUMENT;
             while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
@@ -1055,7 +1056,7 @@
 
             try {
                 serializer.setOutput(fos, null);
-                serializer.startDocument("UTF-8", true);
+                serializer.startDocument(StandardCharsets.UTF_8.name(), true);
                 serializer.startTag(null, TAG_HISTORICAL_RECORDS);
 
                 final int recordCount = historicalRecords.size();
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java b/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
index 1e06c4d..fadfdcb 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
@@ -90,7 +90,7 @@
             R.drawable.abc_textfield_activated_mtrl_alpha,
             R.drawable.abc_textfield_search_activated_mtrl_alpha,
             R.drawable.abc_cab_background_top_mtrl_alpha,
-            R.drawable.abc_text_cursor_mtrl_alpha
+            R.drawable.abc_text_cursor_material
     };
 
     /**
@@ -112,8 +112,6 @@
             R.drawable.abc_tab_indicator_material,
             R.drawable.abc_textfield_search_material,
             R.drawable.abc_spinner_mtrl_am_alpha,
-            R.drawable.abc_btn_check_material,
-            R.drawable.abc_btn_radio_material,
             R.drawable.abc_spinner_textfield_background_material,
             R.drawable.abc_ratingbar_full_material,
             R.drawable.abc_switch_track_mtrl_alpha,
@@ -122,6 +120,16 @@
             R.drawable.abc_btn_borderless_material
     };
 
+    /**
+     * Drawables which should be tinted using a state list containing values of
+     * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated} for the checked
+     * state.
+     */
+    private static final int[] TINT_CHECKABLE_BUTTON_LIST = {
+            R.drawable.abc_btn_check_material,
+            R.drawable.abc_btn_radio_material
+    };
+
     private final WeakReference<Context> mContextRef;
     private SparseArray<ColorStateList> mTintLists;
     private ColorStateList mDefaultColorStateList;
@@ -255,6 +263,7 @@
                 arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, drawableId) ||
                 arrayContains(TINT_COLOR_CONTROL_STATE_LIST, drawableId) ||
                 arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, drawableId) ||
+                arrayContains(TINT_CHECKABLE_BUTTON_LIST, drawableId) ||
                 drawableId == R.drawable.abc_cab_background_top_material;
     }
 
@@ -293,6 +302,8 @@
                 tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal);
             } else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
                 tint = getDefaultColorStateList(context);
+            } else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) {
+                tint = createCheckableButtonColorStateList(context);
             }
 
             if (tint != null) {
@@ -357,6 +368,28 @@
         return mDefaultColorStateList;
     }
 
+    private ColorStateList createCheckableButtonColorStateList(Context context) {
+        final int[][] states = new int[3][];
+        final int[] colors = new int[3];
+        int i = 0;
+
+        // Disabled state
+        states[i] = ThemeUtils.DISABLED_STATE_SET;
+        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
+        i++;
+
+        states[i] = ThemeUtils.CHECKED_STATE_SET;
+        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
+        i++;
+
+        // Default enabled state
+        states[i] = ThemeUtils.EMPTY_STATE_SET;
+        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
+        i++;
+
+        return new ColorStateList(states, colors);
+    }
+
     private ColorStateList createSwitchTrackColorStateList(Context context) {
         final int[][] states = new int[3][];
         final int[] colors = new int[3];
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
index 5bb998e..c84ebc7 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
@@ -2197,23 +2197,19 @@
 
         public void setMediaSessionCompat(final MediaSessionCompat session) {
             mCompatSession = session;
-            if (session == null) {
-                if (mRccMediaSession != null) {
-                    removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
-                    mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener);
-                }
-            }
             if (android.os.Build.VERSION.SDK_INT >= 21) {
-                setMediaSession(session.getMediaSession());
+                setMediaSession(session != null ? session.getMediaSession() : null);
             } else if (android.os.Build.VERSION.SDK_INT >= 14) {
                 if (mRccMediaSession != null) {
                     removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
                     mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener);
                 }
                 mRccMediaSession = session;
-                session.addOnActiveChangeListener(mSessionActiveListener);
-                if (session.isActive()) {
-                    addRemoteControlClient(session.getRemoteControlClient());
+                if (session != null) {
+                    session.addOnActiveChangeListener(mSessionActiveListener);
+                    if (session.isActive()) {
+                        addRemoteControlClient(session.getRemoteControlClient());
+                    }
                 }
             }
         }
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
index 7a4e7ee..1462245 100644
--- a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
+++ b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
@@ -19,6 +19,8 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.os.AsyncTask;
+import android.support.annotation.ColorInt;
+import android.support.annotation.Nullable;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v4.os.AsyncTaskCompat;
 import android.util.TimingLogger;
@@ -152,6 +154,7 @@
     /**
      * Returns the most vibrant swatch in the palette. Might be null.
      */
+    @Nullable
     public Swatch getVibrantSwatch() {
         return mGenerator.getVibrantSwatch();
     }
@@ -159,6 +162,7 @@
     /**
      * Returns a light and vibrant swatch from the palette. Might be null.
      */
+    @Nullable
     public Swatch getLightVibrantSwatch() {
         return mGenerator.getLightVibrantSwatch();
     }
@@ -166,6 +170,7 @@
     /**
      * Returns a dark and vibrant swatch from the palette. Might be null.
      */
+    @Nullable
     public Swatch getDarkVibrantSwatch() {
         return mGenerator.getDarkVibrantSwatch();
     }
@@ -173,6 +178,7 @@
     /**
      * Returns a muted swatch from the palette. Might be null.
      */
+    @Nullable
     public Swatch getMutedSwatch() {
         return mGenerator.getMutedSwatch();
     }
@@ -180,6 +186,7 @@
     /**
      * Returns a muted and light swatch from the palette. Might be null.
      */
+    @Nullable
     public Swatch getLightMutedSwatch() {
         return mGenerator.getLightMutedSwatch();
     }
@@ -187,6 +194,7 @@
     /**
      * Returns a muted and dark swatch from the palette. Might be null.
      */
+    @Nullable
     public Swatch getDarkMutedSwatch() {
         return mGenerator.getDarkMutedSwatch();
     }
@@ -196,7 +204,8 @@
      *
      * @param defaultColor value to return if the swatch isn't available
      */
-    public int getVibrantColor(int defaultColor) {
+    @ColorInt
+    public int getVibrantColor(@ColorInt int defaultColor) {
         Swatch swatch = getVibrantSwatch();
         return swatch != null ? swatch.getRgb() : defaultColor;
     }
@@ -206,7 +215,8 @@
      *
      * @param defaultColor value to return if the swatch isn't available
      */
-    public int getLightVibrantColor(int defaultColor) {
+    @ColorInt
+    public int getLightVibrantColor(@ColorInt int defaultColor) {
         Swatch swatch = getLightVibrantSwatch();
         return swatch != null ? swatch.getRgb() : defaultColor;
     }
@@ -216,7 +226,8 @@
      *
      * @param defaultColor value to return if the swatch isn't available
      */
-    public int getDarkVibrantColor(int defaultColor) {
+    @ColorInt
+    public int getDarkVibrantColor(@ColorInt int defaultColor) {
         Swatch swatch = getDarkVibrantSwatch();
         return swatch != null ? swatch.getRgb() : defaultColor;
     }
@@ -226,7 +237,8 @@
      *
      * @param defaultColor value to return if the swatch isn't available
      */
-    public int getMutedColor(int defaultColor) {
+    @ColorInt
+    public int getMutedColor(@ColorInt int defaultColor) {
         Swatch swatch = getMutedSwatch();
         return swatch != null ? swatch.getRgb() : defaultColor;
     }
@@ -236,7 +248,8 @@
      *
      * @param defaultColor value to return if the swatch isn't available
      */
-    public int getLightMutedColor(int defaultColor) {
+    @ColorInt
+    public int getLightMutedColor(@ColorInt int defaultColor) {
         Swatch swatch = getLightMutedSwatch();
         return swatch != null ? swatch.getRgb() : defaultColor;
     }
@@ -246,7 +259,8 @@
      *
      * @param defaultColor value to return if the swatch isn't available
      */
-    public int getDarkMutedColor(int defaultColor) {
+    @ColorInt
+    public int getDarkMutedColor(@ColorInt int defaultColor) {
         Swatch swatch = getDarkMutedSwatch();
         return swatch != null ? swatch.getRgb() : defaultColor;
     }
@@ -285,7 +299,7 @@
 
         private float[] mHsl;
 
-        public Swatch(int color, int population) {
+        public Swatch(@ColorInt int color, int population) {
             mRed = Color.red(color);
             mGreen = Color.green(color);
             mBlue = Color.blue(color);
@@ -304,6 +318,7 @@
         /**
          * @return this swatch's RGB color value
          */
+        @ColorInt
         public int getRgb() {
             return mRgb;
         }
@@ -333,6 +348,7 @@
          * Returns an appropriate color to use for any 'title' text which is displayed over this
          * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
          */
+        @ColorInt
         public int getTitleTextColor() {
             ensureTextColorsGenerated();
             return mTitleTextColor;
@@ -342,6 +358,7 @@
          * Returns an appropriate color to use for any 'body' text which is displayed over this
          * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
          */
+        @ColorInt
         public int getBodyTextColor() {
             ensureTextColorsGenerated();
             return mBodyTextColor;
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
index 8a773e8..18d385c 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
@@ -822,8 +822,12 @@
         long[] fieldIDs, long[] values, int[] sizes, long[] depClosures,
         long[] depFieldIDs) {
       validate();
-      return rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values,
+      long c = rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values,
           sizes, depClosures, depFieldIDs);
+      if (c == 0) {
+          throw new RSRuntimeException("Failed creating closure.");
+      }
+      return c;
     }
 
     native long rsnInvokeClosureCreate(long con, long invokeID, byte[] params,
@@ -831,8 +835,12 @@
     synchronized long nInvokeClosureCreate(long invokeID, byte[] params,
         long[] fieldIDs, long[] values, int[] sizes) {
       validate();
-      return rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
+      long c = rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
           values, sizes);
+      if (c == 0) {
+          throw new RSRuntimeException("Failed creating closure.");
+      }
+      return c;
     }
 
     native void rsnClosureSetArg(long con, long closureID, int index,
diff --git a/v8/renderscript/jni/android_renderscript_RenderScript.cpp b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
index d22c502..525c6bd 100644
--- a/v8/renderscript/jni/android_renderscript_RenderScript.cpp
+++ b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
@@ -345,79 +345,167 @@
                jlong returnValue, jlongArray fieldIDArray,
                jlongArray valueArray, jintArray sizeArray,
                jlongArray depClosureArray, jlongArray depFieldIDArray) {
+  jlong ret = 0;
+
   jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
   jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
-  RsScriptFieldID* fieldIDs =
-      (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
-  for (int i = 0; i< fieldIDs_length; i++) {
-    fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
-  }
-
   jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
   jsize values_length = _env->GetArrayLength(valueArray);
-  uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
-  for (int i = 0; i < values_length; i++) {
-    values[i] = (uintptr_t)jValues[i];
-  }
-
-  jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
+  jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
   jsize sizes_length = _env->GetArrayLength(sizeArray);
-
   jlong* jDepClosures =
       _env->GetLongArrayElements(depClosureArray, nullptr);
   jsize depClosures_length = _env->GetArrayLength(depClosureArray);
-  RsClosure* depClosures =
-      (RsClosure*)alloca(sizeof(RsClosure) * depClosures_length);
-  for (int i = 0; i < depClosures_length; i++) {
-    depClosures[i] = (RsClosure)jDepClosures[i];
-  }
-
   jlong* jDepFieldIDs =
       _env->GetLongArrayElements(depFieldIDArray, nullptr);
   jsize depFieldIDs_length = _env->GetArrayLength(depFieldIDArray);
-  RsScriptFieldID* depFieldIDs =
-      (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * depFieldIDs_length);
-  for (int i = 0; i < depClosures_length; i++) {
+
+  size_t numValues, numDependencies;
+  RsScriptFieldID* fieldIDs;
+  uintptr_t* values;
+  RsClosure* depClosures;
+  RsScriptFieldID* depFieldIDs;
+
+  if (fieldIDs_length != values_length || values_length != sizes_length) {
+      LOG_API("Unmatched field IDs, values, and sizes in closure creation.");
+      goto exit;
+  }
+
+  numValues = (size_t)fieldIDs_length;
+
+  if (depClosures_length != depFieldIDs_length) {
+      LOG_API("Unmatched closures and field IDs for dependencies in closure creation.");
+      goto exit;
+  }
+
+  numDependencies = (size_t)depClosures_length;
+
+  if (numDependencies > numValues) {
+      LOG_API("Unexpected number of dependencies in closure creation");
+      goto exit;
+  }
+
+  if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
+      LOG_API("Too many arguments or globals in closure creation");
+      goto exit;
+  }
+
+  fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues);
+  if (fieldIDs == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numValues; i++) {
+    fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
+  }
+
+  values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
+  if (values == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numValues; i++) {
+    values[i] = (uintptr_t)jValues[i];
+  }
+
+  depClosures = (RsClosure*)alloca(sizeof(RsClosure) * numDependencies);
+  if (depClosures == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numDependencies; i++) {
+    depClosures[i] = (RsClosure)jDepClosures[i];
+  }
+
+  depFieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numDependencies);
+  if (depFieldIDs == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numDependencies; i++) {
     depFieldIDs[i] = (RsClosure)jDepFieldIDs[i];
   }
 
-  return (jlong)(uintptr_t)dispatchTab.ClosureCreate(
+  ret = (jlong)(uintptr_t)dispatchTab.ClosureCreate(
       (RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
-      fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
-      (int*)sizes, (size_t)sizes_length,
-      depClosures, (size_t)depClosures_length,
-      depFieldIDs, (size_t)depFieldIDs_length);
+      fieldIDs, numValues, values, numValues,
+      (int*)jSizes, numValues,
+      depClosures, numDependencies,
+      depFieldIDs, numDependencies);
+
+exit:
+
+  _env->ReleaseLongArrayElements(depFieldIDArray, jDepFieldIDs, JNI_ABORT);
+  _env->ReleaseLongArrayElements(depClosureArray, jDepClosures, JNI_ABORT);
+  _env->ReleaseIntArrayElements (sizeArray,       jSizes,       JNI_ABORT);
+  _env->ReleaseLongArrayElements(valueArray,      jValues,      JNI_ABORT);
+  _env->ReleaseLongArrayElements(fieldIDArray,    jFieldIDs,    JNI_ABORT);
+
+  return ret;
 }
 
 static jlong
 nInvokeClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong invokeID,
                      jbyteArray paramArray, jlongArray fieldIDArray, jlongArray valueArray,
                      jintArray sizeArray) {
+  jlong ret = 0;
+
   jbyte* jParams = _env->GetByteArrayElements(paramArray, nullptr);
   jsize jParamLength = _env->GetArrayLength(paramArray);
-
   jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
   jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
-  RsScriptFieldID* fieldIDs =
-      (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
-  for (int i = 0; i< fieldIDs_length; i++) {
+  jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
+  jsize values_length = _env->GetArrayLength(valueArray);
+  jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
+  jsize sizes_length = _env->GetArrayLength(sizeArray);
+
+  size_t numValues;
+  RsScriptFieldID* fieldIDs;
+  uintptr_t* values;
+
+  if (fieldIDs_length != values_length || values_length != sizes_length) {
+      LOG_API("Unmatched field IDs, values, and sizes in closure creation.");
+      goto exit;
+  }
+
+  numValues = (size_t) fieldIDs_length;
+
+  if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
+      LOG_API("Too many arguments or globals in closure creation");
+      goto exit;
+  }
+
+  fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues);
+  if (fieldIDs == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numValues; i++) {
     fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
   }
 
-  jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
-  jsize values_length = _env->GetArrayLength(valueArray);
-  uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
-  for (int i = 0; i < values_length; i++) {
+  values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
+  if (values == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numValues; i++) {
     values[i] = (uintptr_t)jValues[i];
   }
 
-  jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
-  jsize sizes_length = _env->GetArrayLength(sizeArray);
-
-  return (jlong)(uintptr_t)dispatchTab.InvokeClosureCreate(
+  ret = (jlong)(uintptr_t)dispatchTab.InvokeClosureCreate(
       (RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
-      fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
-      (int*)sizes, (size_t)sizes_length);
+      fieldIDs, numValues, values, numValues,
+      (int*)jSizes, numValues);
+
+exit:
+
+  _env->ReleaseIntArrayElements (sizeArray,       jSizes,       JNI_ABORT);
+  _env->ReleaseLongArrayElements(valueArray,      jValues,      JNI_ABORT);
+  _env->ReleaseLongArrayElements(fieldIDArray,    jFieldIDs,    JNI_ABORT);
+  _env->ReleaseByteArrayElements(paramArray,      jParams,      JNI_ABORT);
+
+  return ret;
 }
 
 static void
@@ -437,20 +525,40 @@
 static long
 nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name,
                     jstring cacheDir, jlongArray closureArray) {
+  jlong ret = 0;
+
   AutoJavaStringToUTF8 nameUTF(_env, name);
   AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
 
   jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
   jsize numClosures = _env->GetArrayLength(closureArray);
-  RsClosure* closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
+
+  RsClosure* closures;
+
+  if (numClosures > (jsize) RS_SCRIPT_GROUP_MAX_NUMBER_CLOSURES) {
+    LOG_API("Too many closures in script group");
+    goto exit;
+  }
+
+  closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
+  if (closures == nullptr) {
+      goto exit;
+  }
+
   for (int i = 0; i < numClosures; i++) {
     closures[i] = (RsClosure)jClosures[i];
   }
 
-  return (jlong)(uintptr_t)dispatchTab.ScriptGroup2Create(
+  ret = (jlong)(uintptr_t)dispatchTab.ScriptGroup2Create(
       (RsContext)con, nameUTF.c_str(), nameUTF.length(),
       cacheDirUTF.c_str(), cacheDirUTF.length(),
       closures, numClosures);
+
+exit:
+
+  _env->ReleaseLongArrayElements(closureArray, jClosures, JNI_ABORT);
+
+  return ret;
 }
 
 static void
@@ -1138,7 +1246,7 @@
             (void *)script, slot, val);
     if (mUseInc) {
         dispatchTabInc.ScriptSetVarD((RsContext)con, (RsScript)script, slot, val);
-    } else { 
+    } else {
         dispatchTab.ScriptSetVarD((RsContext)con, (RsScript)script, slot, val);
     }
 }
@@ -1389,7 +1497,7 @@
             (void *)sid, slot, sig);
     if (mUseInc) {
         return (jlong)(uintptr_t)dispatchTabInc.ScriptKernelIDCreate((RsContext)con, (RsScript)sid,
-                                                                     slot, sig);    
+                                                                     slot, sig);
     } else {
         return (jlong)(uintptr_t)dispatchTab.ScriptKernelIDCreate((RsContext)con, (RsScript)sid,
                                                                   slot, sig);