Merge "Revert "Give transitioning fragment a context prior to retrieving transition"" into mnc-ub-dev
diff --git a/build.gradle b/build.gradle
index 3b23406..8e89705 100644
--- a/build.gradle
+++ b/build.gradle
@@ -34,7 +34,7 @@
 }
 
 ext.supportRepoOut = new File(buildDir, 'support_repo')
-ext.testApkDistOut = new File(buildDir, 'test_apks')
+ext.testApkDistOut = ext.distDir
 
 // Main task called by the build server.
 task(createArchive) << {
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index 998fc2f..c090419 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -538,10 +538,6 @@
 
         private void updateFabTranslationForSnackbar(CoordinatorLayout parent,
                 final FloatingActionButton fab, View snackbar) {
-            if (fab.getVisibility() != View.VISIBLE) {
-                return;
-            }
-
             final float targetTransY = getFabTranslationYForSnackbar(parent, fab);
             if (mFabTranslationY == targetTransY) {
                 // We're already at (or currently animating to) the target value, return...
@@ -555,7 +551,8 @@
                 mFabTranslationYAnimator.cancel();
             }
 
-            if (Math.abs(currentTransY - targetTransY) > (fab.getHeight() * 0.667f)) {
+            if (fab.isShown()
+                    && Math.abs(currentTransY - targetTransY) > (fab.getHeight() * 0.667f)) {
                 // If the FAB will be travelling by more than 2/3 of it's height, let's animate
                 // it instead
                 if (mFabTranslationYAnimator == null) {
diff --git a/graphics/drawable/Android.mk b/graphics/drawable/Android.mk
index 06d8d6f..cfd3895 100644
--- a/graphics/drawable/Android.mk
+++ b/graphics/drawable/Android.mk
@@ -18,25 +18,21 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v7-vectordrawable
 LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, static util)
+LOCAL_SRC_FILES := $(call all-java-files-under, static common)
 
 LOCAL_JAVA_LIBRARIES := android-support-v4
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
-
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 #Animated vector drawable library
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v11-animatedvectordrawable
 LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, animated util)
+LOCAL_SRC_FILES := $(call all-java-files-under, animated common)
 
 LOCAL_JAVA_LIBRARIES := android-support-v4
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
-
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-vectordrawable
 
 LOCAL_AAPT_FLAGS := --no-version-vectors
-include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
index 2b83d73..37f4a6f 100644
--- a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
+++ b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
@@ -25,7 +25,6 @@
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.Rect;
-import android.graphics.Region;
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
@@ -126,7 +125,7 @@
  * @attr ref android.R.styleable#AnimatedVectorDrawableCompatTarget_animation
  */
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class AnimatedVectorDrawableCompat extends Drawable implements Animatable {
+public class AnimatedVectorDrawableCompat extends VectorDrawableCommon implements Animatable {
     private static final String LOGTAG = "AnimatedVDCompat";
 
     private static final String ANIMATED_VECTOR = "animated-vector";
@@ -138,9 +137,6 @@
 
     private Context mContext;
 
-    // Drawable delegation for Lollipop and above.
-    AnimatedVectorDrawable mDelegateDrawable;
-
     AnimatedVectorDrawableDelegateState mCachedConstantStateDelegate;
 
     private AnimatedVectorDrawableCompat() {
@@ -461,82 +457,6 @@
         return false;
     }
 
-    // Extra override functions for delegation for SDK >= 7.
-    @Override
-    public void clearColorFilter() {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.clearColorFilter();
-            return;
-        }
-        super.clearColorFilter();
-    }
-
-    @Override
-    public Drawable getCurrent() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getCurrent();
-        }
-        return super.getCurrent();
-    }
-
-    @Override
-    public int getMinimumWidth() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getMinimumWidth();
-        }
-        return super.getMinimumWidth();
-    }
-
-    @Override
-    public int getMinimumHeight() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getMinimumHeight();
-        }
-        return super.getMinimumHeight();
-    }
-
-    @Override
-    public boolean getPadding(Rect padding) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getPadding(padding);
-        }
-        return super.getPadding(padding);
-    }
-
-    @Override
-    public int[] getState() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getState();
-        }
-        return super.getState();
-    }
-
-
-    @Override
-    public Region getTransparentRegion() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getTransparentRegion();
-        }
-        return super.getTransparentRegion();
-    }
-
-    @Override
-    public void setChangingConfigurations(int configs) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setChangingConfigurations(configs);
-            return;
-        }
-        super.setChangingConfigurations(configs);
-    }
-
-    @Override
-    public boolean setState(int[] stateSet) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.setState(stateSet);
-        }
-        return super.setState(stateSet);
-    }
-
     /**
      * Constant state for delegating the creating drawable job.
      * Instead of creating a VectorDrawable, create a VectorDrawableCompat instance which contains
@@ -662,7 +582,7 @@
     @Override
     public boolean isRunning() {
         if (mDelegateDrawable != null) {
-            return mDelegateDrawable.isRunning();
+            return ((AnimatedVectorDrawable) mDelegateDrawable).isRunning();
         }
         final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
         final int size = animators.size();
@@ -693,7 +613,7 @@
     @Override
     public void start() {
         if (mDelegateDrawable != null) {
-            mDelegateDrawable.start();
+            ((AnimatedVectorDrawable) mDelegateDrawable).start();
             return;
         }
         // If any one of the animator has not ended, do nothing.
@@ -713,7 +633,7 @@
     @Override
     public void stop() {
         if (mDelegateDrawable != null) {
-            mDelegateDrawable.stop();
+            ((AnimatedVectorDrawable) mDelegateDrawable).stop();
             return;
         }
         final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
diff --git a/graphics/drawable/util/src/android/support/graphics/drawable/AndroidResources.java b/graphics/drawable/common/src/android/support/graphics/drawable/AndroidResources.java
similarity index 100%
rename from graphics/drawable/util/src/android/support/graphics/drawable/AndroidResources.java
rename to graphics/drawable/common/src/android/support/graphics/drawable/AndroidResources.java
diff --git a/graphics/drawable/util/src/android/support/graphics/drawable/TypedArrayUtils.java b/graphics/drawable/common/src/android/support/graphics/drawable/TypedArrayUtils.java
similarity index 100%
rename from graphics/drawable/util/src/android/support/graphics/drawable/TypedArrayUtils.java
rename to graphics/drawable/common/src/android/support/graphics/drawable/TypedArrayUtils.java
diff --git a/graphics/drawable/common/src/android/support/graphics/drawable/VectorDrawableCommon.java b/graphics/drawable/common/src/android/support/graphics/drawable/VectorDrawableCommon.java
new file mode 100644
index 0000000..216932a
--- /dev/null
+++ b/graphics/drawable/common/src/android/support/graphics/drawable/VectorDrawableCommon.java
@@ -0,0 +1,274 @@
+/*
+ * 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.graphics.drawable;
+
+import android.annotation.TargetApi;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.ColorFilter;
+import android.graphics.Outline;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Internal common delegation shared by VectorDrawableCompat and AnimatedVectorDrawableCompat
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+abstract class VectorDrawableCommon extends Drawable {
+    /**
+     * Obtains styled attributes from the theme, if available, or unstyled
+     * resources if the theme is null.
+     */
+    static TypedArray obtainAttributes(
+            Resources res, Resources.Theme theme, AttributeSet set, int[] attrs) {
+        if (theme == null) {
+            return res.obtainAttributes(set, attrs);
+        }
+        return theme.obtainStyledAttributes(set, attrs, 0, 0);
+    }
+
+    // Drawable delegation for Lollipop and above.
+    Drawable mDelegateDrawable;
+
+    @Override
+    public void setColorFilter(int color, PorterDuff.Mode mode) {
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.setColorFilter(color, mode);
+            return;
+        }
+        super.setColorFilter(color, mode);
+    }
+
+    @Override
+    public ColorFilter getColorFilter() {
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.getColorFilter();
+        }
+        return super.getColorFilter();
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.setLevel(level);
+        }
+        return super.onLevelChange(level);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.setBounds(bounds);
+            return;
+        }
+        super.onBoundsChange(bounds);
+    }
+
+    @Override
+    public void setHotspot(float x, float y) {
+        // API >= 21 only.
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.setHotspot(x, y);
+        }
+        return;
+    }
+
+    @Override
+    public void setHotspotBounds(int left, int top, int right, int bottom) {
+        // API >= 21 only.
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.setHotspotBounds(left, top, right, bottom);
+            return;
+        }
+    }
+
+    @TargetApi(23)
+    @Override
+    public void getHotspotBounds(Rect outRect) {
+        // API >= 21 only.
+        if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 23) {
+            mDelegateDrawable.getHotspotBounds(outRect);
+            return;
+        }
+    }
+
+    @Override
+    public Rect getDirtyBounds() {
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.getBounds();
+        }
+        return super.getDirtyBounds();
+    }
+
+    @Override
+    public void setFilterBitmap(boolean filter) {
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.setFilterBitmap(filter);
+            return;
+        }
+    }
+
+    @TargetApi(23)
+    @Override
+    public boolean isFilterBitmap() {
+        // API >= 23 only
+        if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 23) {
+            return mDelegateDrawable.isFilterBitmap();
+        }
+        return false;
+    }
+
+    @Override
+    public void getOutline(Outline outline) {
+        // API >= 21 only
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.getOutline(outline);
+            return;
+        }
+    }
+
+    @Override
+    public void jumpToCurrentState() {
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.jumpToCurrentState();
+            return;
+        }
+    }
+
+    @Override
+    public void setAutoMirrored(boolean mirrored) {
+        // API >= 21 only.
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.setAutoMirrored(mirrored);
+            return;
+        }
+    }
+
+    @Override
+    public boolean isAutoMirrored() {
+        // API >= 21 only.
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.isAutoMirrored();
+        }
+        return false;
+    }
+
+    @Override
+    public void applyTheme(Resources.Theme t) {
+        // API >= 21 only.
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.applyTheme(t);
+            return;
+        }
+    }
+
+    @TargetApi(23)
+    @Override
+    public int getLayoutDirection() {
+        if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 23) {
+            return mDelegateDrawable.getLayoutDirection();
+        }
+        return View.LAYOUT_DIRECTION_LTR;
+    }
+
+    @TargetApi(23)
+    @Override
+    public boolean onLayoutDirectionChanged(int layoutDirection) {
+        if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 23) {
+            return mDelegateDrawable.onLayoutDirectionChanged(layoutDirection);
+        }
+        return false;
+    }
+
+    @Override
+    public void clearColorFilter() {
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.clearColorFilter();
+            return;
+        }
+        super.clearColorFilter();
+    }
+
+    @Override
+    public Drawable getCurrent() {
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.getCurrent();
+        }
+        return super.getCurrent();
+    }
+
+    @Override
+    public int getMinimumWidth() {
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.getMinimumWidth();
+        }
+        return super.getMinimumWidth();
+    }
+
+    @Override
+    public int getMinimumHeight() {
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.getMinimumHeight();
+        }
+        return super.getMinimumHeight();
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.getPadding(padding);
+        }
+        return super.getPadding(padding);
+    }
+
+    @Override
+    public int[] getState() {
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.getState();
+        }
+        return super.getState();
+    }
+
+
+    @Override
+    public Region getTransparentRegion() {
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.getTransparentRegion();
+        }
+        return super.getTransparentRegion();
+    }
+
+    @Override
+    public void setChangingConfigurations(int configs) {
+        if (mDelegateDrawable != null) {
+            mDelegateDrawable.setChangingConfigurations(configs);
+            return;
+        }
+        super.setChangingConfigurations(configs);
+    }
+
+    @Override
+    public boolean setState(int[] stateSet) {
+        if (mDelegateDrawable != null) {
+            return mDelegateDrawable.setState(stateSet);
+        }
+        return super.setState(stateSet);
+    }
+}
diff --git a/graphics/drawable/res/values/attrs.xml b/graphics/drawable/res/values/attrs.xml
deleted file mode 100644
index b54c9a6..0000000
--- a/graphics/drawable/res/values/attrs.xml
+++ /dev/null
@@ -1,183 +0,0 @@
-<?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>
-  <!-- If set, specifies the color to apply to the drawable as a tint. By default,
-       no tint is applied. May be a color state list. -->
-  <attr name="tint" format="color" />
-  <!-- When a tint color is set, specifies its Porter-Duff blending mode. The
-       default value is src_in, which treats the drawable as an alpha mask. -->
-  <attr name="tintMode">
-    <!-- The tint is drawn on top of the drawable.
-         [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
-    <enum name="src_over" value="3" />
-    <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
-         color channels are thrown out. [Sa * Da, Sc * Da] -->
-    <enum name="src_in" value="5" />
-    <!-- The tint is drawn above the drawable, but with the drawable’s alpha
-         channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
-    <enum name="src_atop" value="9" />
-    <!-- Multiplies the color and alpha channels of the drawable with those of
-         the tint. [Sa * Da, Sc * Dc] -->
-    <enum name="multiply" value="14" />
-    <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
-    <enum name="screen" value="15" />
-    <!-- Combines the tint and drawable color and alpha channels, clamping the
-         result to valid color values. Saturate(S + D) -->
-    <enum name="add" value="16" />
-  </attr>
-  <!-- Animation to use on each child. -->
-  <attr name="animation" format="reference" />
-  <!-- alpha property of the view, as a value between 0 (completely transparent) and 1
-             (completely opaque). -->
-  <attr name="alpha" format="float" />
-  <attr name="pivotX" format="float|fraction" />
-  <attr name="pivotY" format="float|fraction" />
-  <!-- rotation of the view, in degrees. -->
-  <attr name="rotation" format="float" />
-
-  <!-- scale of the view in the x direction. -->
-  <attr name="scaleX" format="float" />
-
-  <!-- scale of the view in the y direction. -->
-  <attr name="scaleY" format="float" />
-  <!-- Makes the TextView be exactly this many pixels tall.
-       You could get the same effect by specifying this number in the
-       layout parameters. -->
-  <attr name="height" format="dimension" />
-  <!-- Makes the TextView be exactly this many pixels wide.
-       You could get the same effect by specifying this number in the
-       layout parameters. -->
-  <attr name="width" format="dimension" />
-  <!-- A unique name for the given item.  This must use a Java-style naming
-       convention to ensure the name is unique, for example
-       "com.mycompany.MyName". -->
-  <attr name="name" format="string" />
-
-  <!-- ========================== -->
-  <!--   VectorDrawable class   -->
-  <!-- ========================== -->
-  <eat-comment />
-
-  <!-- Drawable used to draw vector paths. -->
-  <declare-styleable name="VectorDrawable">
-    <!-- If set, specifies the color to apply to the drawable as a tint. By default,
-         no tint is applied. May be a color state list. -->
-    <attr name="tint" />
-    <!-- When a tint color is set, specifies its Porter-Duff blending mode. The
-         default value is src_in, which treats the drawable as an alpha mask. -->
-    <attr name="tintMode" />
-    <!-- Indicates if the drawable needs to be mirrored when its layout direction is
-         RTL (right-to-left). -->
-    <attr name="autoMirrored" format="boolean" />
-    <!-- The intrinsic width of the Vector Drawable. -->
-    <attr name="width" />
-    <!-- The intrinsic height of the Vector Drawable. -->
-    <attr name="height" />
-    <!-- The width of the canvas the drawing is on. -->
-    <attr name="viewportWidth" format="float"/>
-    <!-- The height of the canvas the drawing is on. -->
-    <attr name="viewportHeight" format="float"/>
-    <!-- The name of this vector drawable -->
-    <attr name="name" />
-    <!-- The opacity of the whole vector drawable, as a value between 0
-         (completely transparent) and 1 (completely opaque). -->
-    <attr name="alpha" />
-  </declare-styleable>
-
-  <!-- Defines the group used in VectorDrawables. -->
-  <declare-styleable name="VectorDrawableGroup">
-    <!-- The name of this group -->
-    <attr name="name" />
-    <!-- The amount to rotate the group -->
-    <attr name="rotation" />
-    <!-- The X coordinate of the center of rotation of a group -->
-    <attr name="pivotX" />
-    <!-- The Y coordinate of the center of rotation of a group -->
-    <attr name="pivotY" />
-    <!-- The amount to translate the group on X coordinate -->
-    <attr name="translateX" format="float"/>
-    <!-- The amount to translate the group on Y coordinate -->
-    <attr name="translateY" format="float"/>
-    <!-- The amount to scale the group on X coordinate -->
-    <attr name="scaleX" />
-    <!-- The amount to scale the group on X coordinate -->
-    <attr name="scaleY" />
-  </declare-styleable>
-
-  <!-- Defines the path used in VectorDrawables. -->
-  <declare-styleable name="VectorDrawablePath">
-    <!-- The name of this path -->
-    <attr name="name" />
-    <!-- The width a path stroke -->
-    <attr name="strokeWidth" format="float" />
-    <!-- The color to stroke the path if not defined implies no stroke-->
-    <attr name="strokeColor" format="color" />
-    <!-- The opacity of a path stroke, as a value between 0 (completely transparent)
-         and 1 (completely opaque) -->
-    <attr name="strokeAlpha" format="float" />
-    <!-- The color to fill the path if not defined implies no fill-->
-    <attr name="fillColor" format="color" />
-    <!-- The alpha of the path fill, as a value between 0 (completely transparent)
-         and 1 (completely opaque)-->
-    <attr name="fillAlpha" format="float" />
-    <!-- The specification of the operations that define the path  -->
-    <attr name="pathData" format="string" />
-    <!-- The fraction of the path to trim from the start from 0 to 1 -->
-    <attr name="trimPathStart" format="float" />
-    <!-- The fraction of the path to trim from the end from 0 to 1  -->
-    <attr name="trimPathEnd" format="float" />
-    <!-- Shift trim region (allows visible region to include the start and end) from 0 to 1  -->
-    <attr name="trimPathOffset" format="float" />
-    <!-- The linecap for a stroked path -->
-    <attr name="strokeLineCap" format="enum">
-      <enum name="butt" value="0"/>
-      <enum name="round" value="1"/>
-      <enum name="square" value="2"/>
-    </attr>
-    <!-- The lineJoin for a stroked path -->
-    <attr name="strokeLineJoin" format="enum">
-      <enum name="miter" value="0"/>
-      <enum name="round" value="1"/>
-      <enum name="bevel" value="2"/>
-    </attr>
-    <!-- The Miter limit for a stroked path -->
-    <attr name="strokeMiterLimit" format="float"/>
-  </declare-styleable>
-
-  <!-- Defines the clip path used in VectorDrawables. -->
-  <declare-styleable name="VectorDrawableClipPath">
-    <!-- The Name of this path -->
-    <attr name="name" />
-    <!-- The specification of the operations that define the path  -->
-    <attr name="pathData"/>
-  </declare-styleable>
-
-  <!-- ========================== -->
-  <!--   AnimatedVectorDrawable class   -->
-  <!-- ========================== -->
-  <eat-comment />
-
-  <!-- Define the AnimatedVectorDrawable. -->
-  <declare-styleable name="AnimatedVectorDrawable">
-    <!-- The static vector drawable. -->
-    <attr name="drawable" format="reference" />
-  </declare-styleable>
-
-  <!-- Defines the target used in the AnimatedVectorDrawable. -->
-  <declare-styleable name="AnimatedVectorDrawableTarget">
-    <!-- The name of the target path, group or vector drawable -->
-    <attr name="name" />
-    <!-- The animation for the target path, group or vector drawable -->
-    <attr name="animation" />
-  </declare-styleable>
-</resources>
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
index 9ca398f..e351031 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
@@ -36,9 +36,9 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.VectorDrawable;
 import android.os.Build;
+import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.annotation.DrawableRes;
 import android.support.v4.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -182,7 +182,7 @@
  * </pre></li>
  */
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class VectorDrawableCompat extends Drawable {
+public class VectorDrawableCompat extends VectorDrawableCommon {
     static final String LOGTAG = "VectorDrawableCompat";
 
     static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
@@ -213,9 +213,6 @@
     // caching the bitmap by default is allowed.
     private boolean mAllowCaching = true;
 
-    // Drawable delegation for Lollipop and above.
-    VectorDrawable mDelegateDrawable;
-
     // The Constant state associated with the <code>mDelegateDrawable</code>.
     private ConstantState mCachedConstantStateDelegate;
 
@@ -522,19 +519,6 @@
         return color;
     }
 
-    /**
-     * Obtains styled attributes from the theme, if available, or unstyled
-     * resources if the theme is null.
-     */
-    static TypedArray obtainAttributes(
-            Resources res, Theme theme, AttributeSet set, int[] attrs) {
-        if (theme == null) {
-            return res.obtainAttributes(set, attrs);
-        }
-        return theme.obtainStyledAttributes(set, attrs, 0, 0);
-    }
-
-
     @Override
     public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs)
             throws XmlPullParserException, IOException {
@@ -638,7 +622,7 @@
 
         // shown up from API 11.
         final float alphaInFloat = TypedArrayUtils.getNamedFloat(a, parser, "alpha",
-                    AndroidResources.styleable_VectorDrawable_alpha, pathRenderer.getAlpha());
+                AndroidResources.styleable_VectorDrawable_alpha, pathRenderer.getAlpha());
         pathRenderer.setAlpha(alphaInFloat);
 
         final String name = a.getString(AndroidResources.styleable_VectorDrawable_name);
@@ -749,15 +733,6 @@
 
     // Extra override functions for delegation for SDK >= 7.
     @Override
-    public void clearColorFilter() {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.clearColorFilter();
-            return;
-        }
-        super.clearColorFilter();
-    }
-
-    @Override
     public void setBounds(int left, int top, int right, int bottom) {
         if (mDelegateDrawable != null) {
             mDelegateDrawable.setBounds(left, top, right, bottom);
@@ -784,55 +759,6 @@
     }
 
     @Override
-    public Drawable getCurrent() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getCurrent();
-        }
-        return super.getCurrent();
-    }
-
-    @Override
-    public int getMinimumWidth() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getMinimumWidth();
-        }
-        return super.getMinimumWidth();
-    }
-
-    @Override
-    public int getMinimumHeight() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getMinimumHeight();
-        }
-        return super.getMinimumHeight();
-    }
-
-    @Override
-    public boolean getPadding(Rect padding) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getPadding(padding);
-        }
-        return super.getPadding(padding);
-    }
-
-    @Override
-    public int[] getState() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getState();
-        }
-        return super.getState();
-    }
-
-
-    @Override
-    public Region getTransparentRegion() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getTransparentRegion();
-        }
-        return super.getTransparentRegion();
-    }
-
-    @Override
     public void invalidateSelf() {
         if (mDelegateDrawable != null) {
             mDelegateDrawable.invalidateSelf();
@@ -851,23 +777,6 @@
     }
 
     @Override
-    public void setChangingConfigurations(int configs) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setChangingConfigurations(configs);
-            return;
-        }
-        super.setChangingConfigurations(configs);
-    }
-
-    @Override
-    public boolean setState(int[] stateSet) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.setState(stateSet);
-        }
-        return super.setState(stateSet);
-    }
-
-    @Override
     public boolean setVisible(boolean visible, boolean restart) {
         if (mDelegateDrawable != null) {
             return mDelegateDrawable.setVisible(visible, restart);
diff --git a/graphics/drawable/testanimated/Android.mk b/graphics/drawable/testanimated/Android.mk
index 004cddb..f4d6269 100644
--- a/graphics/drawable/testanimated/Android.mk
+++ b/graphics/drawable/testanimated/Android.mk
@@ -22,10 +22,6 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_RESOURCE_DIR = \
-        $(LOCAL_PATH)/res \
-        frameworks/support/graphics/drawable/res \
-
 LOCAL_PACKAGE_NAME := AndroidAnimatedVectorDrawableTests
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v11-animatedvectordrawable android-support-v4
diff --git a/graphics/drawable/teststatic/Android.mk b/graphics/drawable/teststatic/Android.mk
index 4c4a7ba..48322c7 100644
--- a/graphics/drawable/teststatic/Android.mk
+++ b/graphics/drawable/teststatic/Android.mk
@@ -22,10 +22,6 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_RESOURCE_DIR = \
-        $(LOCAL_PATH)/res \
-        frameworks/support/graphics/drawable/res \
-
 LOCAL_PACKAGE_NAME := AndroidVectorDrawableTests
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-vectordrawable android-support-v4
diff --git a/v17/leanback/api/current.txt b/v17/leanback/api/current.txt
index c4f58e7..7cd06a1 100644
--- a/v17/leanback/api/current.txt
+++ b/v17/leanback/api/current.txt
@@ -1097,6 +1097,8 @@
     method public int getItemViewType(android.support.v17.leanback.widget.GuidedAction);
     method public android.support.v17.leanback.widget.VerticalGridView getSubActionsGridView();
     method public boolean isButtonActions();
+    method public boolean isExpandTransitionSupported();
+    method public boolean isInExpandTransition();
     method public boolean isSubActionsExpanded();
     method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
     method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
@@ -1115,10 +1117,12 @@
     method public int onProvideItemLayoutId();
     method public int onProvideItemLayoutId(int);
     method public int onProvideLayoutId();
+    method public void onUpdateExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
     method public void setAsButtonActions();
     method public void setEditingMode(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
     method public void setExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
     method protected void setupImeOptions(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void startExpandedTransition(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
     field public static final int VIEW_TYPE_DEFAULT = 0; // 0x0
   }
 
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
index 1762c15..e9f8505 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
@@ -18,6 +18,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.TimeInterpolator;
+import android.graphics.Rect;
 import android.transition.Fade;
 import android.transition.Transition;
 import android.transition.TransitionValues;
@@ -38,54 +39,101 @@
     private static final String PROPNAME_SCREEN_POSITION =
             "android:fadeAndShortSlideTransition:screenPosition";
 
-    private CalculateSlide mSlideCalculator = sCalculateEnd;
+    private CalculateSlide mSlideCalculator;
     private Visibility mFade = new Fade();
+    private float mDistance = -1;
 
-    private interface CalculateSlide {
+    private static abstract class CalculateSlide {
 
-        /** Returns the translation value for view when it goes out of the scene */
-        float getGoneX(ViewGroup sceneRoot, View view, int[] position);
+        /** Returns the translation X value for view when it goes out of the scene */
+        float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
+            return view.getTranslationX();
+        }
+
+        /** Returns the translation Y value for view when it goes out of the scene */
+        float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
+            return view.getTranslationY();
+        }
     }
 
-    private static final CalculateSlide sCalculateStart = new CalculateSlide() {
+    float getHorizontalDistance(ViewGroup sceneRoot) {
+        return mDistance >= 0 ? mDistance : (sceneRoot.getWidth() / 4);
+    }
+
+    float getVerticalDistance(ViewGroup sceneRoot) {
+        return mDistance >= 0 ? mDistance : (sceneRoot.getHeight() / 4);
+    }
+
+    final static CalculateSlide sCalculateStart = new CalculateSlide() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view, int[] position) {
+        public float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
             final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
             final float x;
             if (isRtl) {
-                x = view.getTranslationX() + sceneRoot.getWidth() / 4;
+                x = view.getTranslationX() + t.getHorizontalDistance(sceneRoot);
             } else {
-                x = view.getTranslationX() - sceneRoot.getWidth() / 4;
+                x = view.getTranslationX() - t.getHorizontalDistance(sceneRoot);
             }
             return x;
         }
     };
 
-    private static final CalculateSlide sCalculateEnd = new CalculateSlide() {
+    final static CalculateSlide sCalculateEnd = new CalculateSlide() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view, int[] position) {
+        public float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
             final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
             final float x;
             if (isRtl) {
-                x = view.getTranslationX() - sceneRoot.getWidth() / 4;
+                x = view.getTranslationX() - t.getHorizontalDistance(sceneRoot);
             } else {
-                x = view.getTranslationX() + sceneRoot.getWidth() / 4;
+                x = view.getTranslationX() + t.getHorizontalDistance(sceneRoot);
             }
             return x;
         }
     };
 
-    private static final CalculateSlide sCalculateBoth = new CalculateSlide() {
-
+    final static CalculateSlide sCalculateStartEnd = new CalculateSlide() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view, int[] position) {
+        public float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
             final int viewCenter = position[0] + view.getWidth() / 2;
             sceneRoot.getLocationOnScreen(position);
-            final int sceneRootCenter = position[0] + sceneRoot.getWidth() / 2;
+            Rect center = t.getEpicenter();
+            final int sceneRootCenter = center == null ? (position[0] + sceneRoot.getWidth() / 2)
+                    : center.centerX();
             if (viewCenter < sceneRootCenter) {
-                return view.getTranslationX() - sceneRoot.getWidth() / 2;
+                return view.getTranslationX() - t.getHorizontalDistance(sceneRoot);
             } else {
-                return view.getTranslationX() + sceneRoot.getWidth() / 2;
+                return view.getTranslationX() + t.getHorizontalDistance(sceneRoot);
+            }
+        }
+    };
+
+    final static CalculateSlide sCalculateBottom = new CalculateSlide() {
+        @Override
+        public float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
+            return view.getTranslationY() + t.getVerticalDistance(sceneRoot);
+        }
+    };
+
+    final static CalculateSlide sCalculateTop = new CalculateSlide() {
+        @Override
+        public float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
+            return view.getTranslationY() - t.getVerticalDistance(sceneRoot);
+        }
+    };
+
+    final CalculateSlide sCalculateTopBottom = new CalculateSlide() {
+        @Override
+        public float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
+            final int viewCenter = position[1] + view.getHeight() / 2;
+            sceneRoot.getLocationOnScreen(position);
+            Rect center = getEpicenter();
+            final int sceneRootCenter = center == null ? (position[1] + sceneRoot.getHeight() / 2)
+                    : center.centerY();
+            if (viewCenter < sceneRootCenter) {
+                return view.getTranslationY() - t.getVerticalDistance(sceneRoot);
+            } else {
+                return view.getTranslationY() + t.getVerticalDistance(sceneRoot);
             }
         }
     };
@@ -134,7 +182,16 @@
                 mSlideCalculator = sCalculateEnd;
                 break;
             case Gravity.START | Gravity.END:
-                mSlideCalculator = sCalculateBoth;
+                mSlideCalculator = sCalculateStartEnd;
+                break;
+            case Gravity.TOP:
+                mSlideCalculator = sCalculateTop;
+                break;
+            case Gravity.BOTTOM:
+                mSlideCalculator = sCalculateBottom;
+                break;
+            case Gravity.TOP | Gravity.BOTTOM:
+                mSlideCalculator = sCalculateTopBottom;
                 break;
             default:
                 throw new IllegalArgumentException("Invalid slide direction");
@@ -156,12 +213,21 @@
         }
         int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
         int left = position[0];
+        int top = position[1];
         float endX = view.getTranslationX();
-        float startX = mSlideCalculator.getGoneX(sceneRoot, view, position);
+        float startX = mSlideCalculator.getGoneX(this, sceneRoot, view, position);
+        float endY = view.getTranslationY();
+        float startY = mSlideCalculator.getGoneY(this, sceneRoot, view, position);
         final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view, endValues,
-                left, startX, endX, sDecelerate, this);
+                left, top, startX, startY, endX, endY, sDecelerate, this);
+        final Animator fadeAnimator = mFade.onAppear(sceneRoot, view, startValues, endValues);
+        if (slideAnimator == null) {
+            return fadeAnimator;
+        } else if (fadeAnimator == null) {
+            return slideAnimator;
+        }
         final AnimatorSet set = new AnimatorSet();
-        set.play(slideAnimator).with(mFade.onAppear(sceneRoot, view, startValues, endValues));
+        set.play(slideAnimator).with(fadeAnimator);
 
         return set;
     }
@@ -178,12 +244,22 @@
         }
         int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
         int left = position[0];
+        int top = position[1];
         float startX = view.getTranslationX();
-        float endX = mSlideCalculator.getGoneX(sceneRoot, view, position);
+        float endX = mSlideCalculator.getGoneX(this, sceneRoot, view, position);
+        float startY = view.getTranslationY();
+        float endY = mSlideCalculator.getGoneY(this, sceneRoot, view, position);
         final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view,
-                startValues, left, startX, endX, sDecelerate /* sAccelerate */, this);
+                startValues, left, top, startX, startY, endX, endY, sDecelerate /* sAccelerate */,
+                this);
+        final Animator fadeAnimator = mFade.onDisappear(sceneRoot, view, startValues, endValues);
+        if (slideAnimator == null) {
+            return fadeAnimator;
+        } else if (fadeAnimator == null) {
+            return slideAnimator;
+        }
         final AnimatorSet set = new AnimatorSet();
-        set.play(slideAnimator).with(mFade.onDisappear(sceneRoot, view, startValues, endValues));
+        set.play(slideAnimator).with(fadeAnimator);
 
         return set;
     }
@@ -200,6 +276,23 @@
         return super.removeListener(listener);
     }
 
+    /**
+     * Returns distance to slide.  When negative value is returned, it will use 1/4 of
+     * sceneRoot dimension.
+     */
+    public float getDistance() {
+        return mDistance;
+    }
+
+    /**
+     * Set distance to slide, default value is -1.  when negative value is set, it will use 1/4 of
+     * sceneRoot dimension.
+     * @param distance Pixels to slide.
+     */
+    public void setDistance(float distance) {
+        mDistance = distance;
+    }
+
     @Override
     public Transition clone() {
         FadeAndShortSlide clone = null;
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
index c5a33cb..1206ea8 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.transition.ChangeTransform;
 import android.transition.Transition;
+import android.transition.TransitionManager;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
@@ -86,10 +87,25 @@
         return AnimationUtils.loadInterpolator(context, R.interpolator.fast_out_linear_in);
     }
 
+    public static Object createChangeTransform() {
+        return new ChangeTransform();
+    }
+
     public static Object createFadeAndShortSlide(int edge) {
         return new FadeAndShortSlide(edge);
     }
 
+    public static Object createFadeAndShortSlide(int edge, float distance) {
+        FadeAndShortSlide slide = new FadeAndShortSlide(edge);
+        slide.setDistance(distance);
+        return slide;
+    }
+
+    public static void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
+        Transition transition = (Transition) transitionObject;
+        TransitionManager.beginDelayedTransition(sceneRoot, transition);
+    }
+
     public static void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
         viewGroup.setTransitionGroup(transitionGroup);
     }
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
index 2cc3545..46068da 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
@@ -9,54 +9,60 @@
 import android.graphics.Path;
 import android.transition.Transition;
 import android.transition.TransitionValues;
+import android.transition.Transition.TransitionListener;
 import android.view.View;
 
 /**
- * This class is used by Slide and Explode to create an animator that goes from the start position
- * to the end position. It takes into account the canceled position so that it will not blink out or
- * shift suddenly when the transition is interrupted.
+ * This class is used by Slide and Explode to create an animator that goes from the start
+ * position to the end position. It takes into account the canceled position so that it
+ * will not blink out or shift suddenly when the transition is interrupted.
  * @hide
  */
 class TranslationAnimationCreator {
 
     /**
-     * Creates an animator that can be used for x and/or y translations. When interrupted, it sets a
-     * tag to keep track of the position so that it may be continued from position.
+     * Creates an animator that can be used for x and/or y translations. When interrupted,
+     * it sets a tag to keep track of the position so that it may be continued from position.
      *
      * @param view The view being moved. This may be in the overlay for onDisappear.
      * @param values The values containing the view in the view hierarchy.
      * @param viewPosX The x screen coordinate of view
+     * @param viewPosY The y screen coordinate of view
      * @param startX The start translation x of view
+     * @param startY The start translation y of view
      * @param endX The end translation x of view
+     * @param endY The end translation y of view
      * @param interpolator The interpolator to use with this animator.
-     * @return An animator that moves from (startX, startY) to (endX, endY) unless there was a
-     *         previous interruption, in which case it moves from the current position to (endX,
-     *         endY).
+     * @return An animator that moves from (startX, startY) to (endX, endY) unless there was
+     * a previous interruption, in which case it moves from the current position to (endX, endY).
      */
-    static Animator createAnimation(View view, TransitionValues values, int viewPosX, float startX,
-            float endX, TimeInterpolator interpolator, Transition transition) {
+    static Animator createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY,
+            float startX, float startY, float endX, float endY, TimeInterpolator interpolator,
+            Transition transition) {
         float terminalX = view.getTranslationX();
-        Integer startPosition = (Integer) values.view.getTag(R.id.transitionPosition);
+        float terminalY = view.getTranslationY();
+        int[] startPosition = (int[]) values.view.getTag(R.id.transitionPosition);
         if (startPosition != null) {
-            startX = startPosition - viewPosX + terminalX;
+            startX = startPosition[0] - viewPosX + terminalX;
+            startY = startPosition[1] - viewPosY + terminalY;
         }
-        // Initial position is at translation startX, startY, so position is offset by that
-        // amount
+        // Initial position is at translation startX, startY, so position is offset by that amount
         int startPosX = viewPosX + Math.round(startX - terminalX);
+        int startPosY = viewPosY + Math.round(startY - terminalY);
 
         view.setTranslationX(startX);
-        if (startX == endX) {
+        view.setTranslationY(startY);
+        if (startX == endX && startY == endY) {
             return null;
         }
-        float y = view.getTranslationY();
         Path path = new Path();
-        path.moveTo(startX, y);
-        path.lineTo(endX, y);
-        ObjectAnimator anim =
-                ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y, path);
+        path.moveTo(startX, startY);
+        path.lineTo(endX, endY);
+        ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y,
+                path);
 
-        TransitionPositionListener listener =
-                new TransitionPositionListener(view, values.view, startPosX, terminalX);
+        TransitionPositionListener listener = new TransitionPositionListener(view, values.view,
+                startPosX, startPosY, terminalX, terminalY);
         transition.addListener(listener);
         anim.addListener(listener);
         anim.addPauseListener(listener);
@@ -64,23 +70,28 @@
         return anim;
     }
 
-    private static class TransitionPositionListener extends AnimatorListenerAdapter
-            implements Transition.TransitionListener {
+    private static class TransitionPositionListener extends AnimatorListenerAdapter implements
+            TransitionListener {
 
         private final View mViewInHierarchy;
         private final View mMovingView;
         private final int mStartX;
-        private Integer mTransitionPosition;
+        private final int mStartY;
+        private int[] mTransitionPosition;
         private float mPausedX;
+        private float mPausedY;
         private final float mTerminalX;
+        private final float mTerminalY;
 
-        private TransitionPositionListener(View movingView, View viewInHierarchy, int startX,
-                float terminalX) {
+        private TransitionPositionListener(View movingView, View viewInHierarchy,
+                int startX, int startY, float terminalX, float terminalY) {
             mMovingView = movingView;
             mViewInHierarchy = viewInHierarchy;
             mStartX = startX - Math.round(mMovingView.getTranslationX());
+            mStartY = startY - Math.round(mMovingView.getTranslationY());
             mTerminalX = terminalX;
-            mTransitionPosition = (Integer) mViewInHierarchy.getTag(R.id.transitionPosition);
+            mTerminalY = terminalY;
+            mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transitionPosition);
             if (mTransitionPosition != null) {
                 mViewInHierarchy.setTag(R.id.transitionPosition, null);
             }
@@ -88,41 +99,53 @@
 
         @Override
         public void onAnimationCancel(Animator animation) {
-            mTransitionPosition = Math.round(mStartX + mMovingView.getTranslationX());
+            if (mTransitionPosition == null) {
+                mTransitionPosition = new int[2];
+            }
+            mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX());
+            mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY());
             mViewInHierarchy.setTag(R.id.transitionPosition, mTransitionPosition);
         }
 
         @Override
-        public void onAnimationEnd(Animator animator) {}
+        public void onAnimationEnd(Animator animator) {
+        }
 
         @Override
         public void onAnimationPause(Animator animator) {
             mPausedX = mMovingView.getTranslationX();
+            mPausedY = mMovingView.getTranslationY();
             mMovingView.setTranslationX(mTerminalX);
+            mMovingView.setTranslationY(mTerminalY);
         }
 
         @Override
         public void onAnimationResume(Animator animator) {
             mMovingView.setTranslationX(mPausedX);
+            mMovingView.setTranslationY(mPausedY);
         }
 
         @Override
-        public void onTransitionStart(Transition transition) {}
+        public void onTransitionStart(Transition transition) {
+        }
 
         @Override
         public void onTransitionEnd(Transition transition) {
             mMovingView.setTranslationX(mTerminalX);
+            mMovingView.setTranslationY(mTerminalY);
         }
 
         @Override
-        public void onTransitionCancel(Transition transition) {}
+        public void onTransitionCancel(Transition transition) {
+        }
 
         @Override
-        public void onTransitionPause(Transition transition) {}
+        public void onTransitionPause(Transition transition) {
+        }
 
         @Override
-        public void onTransitionResume(Transition transition) {}
+        public void onTransitionResume(Transition transition) {
+        }
     }
 
 }
-
diff --git a/v17/leanback/res/animator/lb_guidedactions_selector_hide.xml b/v17/leanback/res/animator/lb_guidedactions_selector_hide.xml
deleted file mode 100644
index e5dafb0..0000000
--- a/v17/leanback/res/animator/lb_guidedactions_selector_hide.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:duration="@integer/lb_guidedactions_animation_duration"
-    android:propertyName="alpha"
-    android:valueTo="0.0"
-    android:interpolator="@animator/lb_decelerator_2"
-    android:valueType="floatType" />
diff --git a/v17/leanback/res/animator/lb_guidedactions_selector_show.xml b/v17/leanback/res/animator/lb_guidedactions_selector_show.xml
deleted file mode 100644
index fcfd9fa..0000000
--- a/v17/leanback/res/animator/lb_guidedactions_selector_show.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:ordering="together">
-
-    <objectAnimator
-        android:duration="@integer/lb_guidedactions_animation_duration"
-        android:propertyName="alpha"
-        android:valueTo="1.0"
-        android:interpolator="@animator/lb_decelerator_2"
-        android:valueType="floatType" />
-
-    <objectAnimator
-        android:duration="@integer/lb_guidedactions_animation_duration"
-        android:propertyName="scaleY"
-        android:interpolator="@animator/lb_decelerator_2"
-        android:valueType="floatType" />
-</set>
diff --git a/v17/leanback/res/drawable/lb_headers_right_fading.xml b/v17/leanback/res/drawable/lb_headers_right_fading.xml
index b20c4e8..8aea204 100644
--- a/v17/leanback/res/drawable/lb_headers_right_fading.xml
+++ b/v17/leanback/res/drawable/lb_headers_right_fading.xml
@@ -20,6 +20,6 @@
     <gradient
         android:angle="0"
         android:startColor="#00000000"
-        android:endColor="?attr/defaultBrandColor"
+        android:endColor="#00000000"
         />
 </shape>
diff --git a/v17/leanback/res/layout/lb_guidedactions.xml b/v17/leanback/res/layout/lb_guidedactions.xml
index 9fe9484..2ba9075 100644
--- a/v17/leanback/res/layout/lb_guidedactions.xml
+++ b/v17/leanback/res/layout/lb_guidedactions.xml
@@ -48,10 +48,6 @@
             android:visibility="invisible"
             android:background="?attr/guidedActionsBackgroundDark" />
 
-        <android.support.v17.leanback.widget.NonOverlappingView
-            android:id="@+id/guidedactions_selector"
-            android:transitionName="guidedactions_selector"
-            style="?attr/guidedActionsSelectorStyle" />
     </android.support.v17.leanback.widget.NonOverlappingFrameLayout>
 
 </RelativeLayout>
diff --git a/v17/leanback/res/layout/lb_guidedactions_item.xml b/v17/leanback/res/layout/lb_guidedactions_item.xml
index 831c355..30f0384 100644
--- a/v17/leanback/res/layout/lb_guidedactions_item.xml
+++ b/v17/leanback/res/layout/lb_guidedactions_item.xml
@@ -15,7 +15,7 @@
      limitations under the License.
 -->
 <!-- Layout for an action item displayed in the 2 pane actions fragment. -->
-<android.support.v17.leanback.widget.NonOverlappingLinearLayout
+<android.support.v17.leanback.widget.NonOverlappingLinearLayoutWithForeground
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     style="?attr/guidedActionItemContainerStyle" >
@@ -49,4 +49,4 @@
         style="?attr/guidedActionItemChevronStyle"
         tools:ignore="ContentDescription" />
 
-</android.support.v17.leanback.widget.NonOverlappingLinearLayout>
+</android.support.v17.leanback.widget.NonOverlappingLinearLayoutWithForeground>
diff --git a/v17/leanback/res/layout/lb_guidedbuttonactions.xml b/v17/leanback/res/layout/lb_guidedbuttonactions.xml
index 274a550..7631c91 100644
--- a/v17/leanback/res/layout/lb_guidedbuttonactions.xml
+++ b/v17/leanback/res/layout/lb_guidedbuttonactions.xml
@@ -41,10 +41,6 @@
             android:id="@+id/guidedactions_list2"
             style="?attr/guidedButtonActionsListStyle" />
 
-        <android.support.v17.leanback.widget.NonOverlappingView
-            android:id="@+id/guidedactions_selector2"
-            android:transitionName="guidedactions_selector2"
-            style="?attr/guidedActionsSelectorStyle" />
     </android.support.v17.leanback.widget.NonOverlappingFrameLayout>
 
 </RelativeLayout>
diff --git a/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml b/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml
index c235485..d92890d 100644
--- a/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml
+++ b/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml
@@ -44,11 +44,9 @@
             <target android:targetId="@id/guidedactions_content" />
             <target android:targetId="@id/guidedactions_list" />
             <target android:targetId="@id/guidedactions_sub_list" />
-            <target android:targetId="@id/guidedactions_selector" />
             <target android:targetId="@id/guidedactions_list_background2" />
             <target android:targetId="@id/guidedactions_content2" />
             <target android:targetId="@id/guidedactions_list2" />
-            <target android:targetId="@id/guidedactions_selector2" />
         </targets>
   </slide>
 </transitionSet>
\ No newline at end of file
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index 0541b6a..c564452 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -352,17 +352,9 @@
              {@link android.support.v17.leanback.R.style#Widget_Leanback_GuidanceIconStyle}. -->
         <attr name="guidanceIconStyle" format="reference" />
 
-        <!-- Theme attribute for the animation used in a GuidedActionsPresenter when the action
-             selector is animated in at activity start. Default is {@link
-             android.support.v17.leanback.R.animator#lb_guidedactions_selector_show}. -->
-        <attr name="guidedActionsSelectorShowAnimation" format="reference" />
-        <!-- Theme attribute for the animation used in a GuidedActionsPresenter when the action
-             selector is animated in at activity start. Default is {@link
-             android.support.v17.leanback.R.animator#lb_guidedactions_selector_hide}. -->
-        <attr name="guidedActionsSelectorHideAnimation" format="reference" />
         <!-- Theme attribute for the style of the item selector in a GuidedActionsPresenter. Default is
-             {@link android.support.v17.leanback.R.style#Widget_Leanback_GuidedActionsSelectorStyle}. -->
-        <attr name="guidedActionsSelectorStyle" format="reference" />
+             ?android:attr/selectableItemBackground. -->
+        <attr name="guidedActionsSelectorDrawable" format="reference" />
 
         <!-- Theme attribute for the shadow elevation of GuidedActions. Default is
              {@link android.support.v17.leanback.R.dimen#lb_guidedactions_elevation}.-->
diff --git a/v17/leanback/res/values/colors.xml b/v17/leanback/res/values/colors.xml
index 858ace5..aae6563 100644
--- a/v17/leanback/res/values/colors.xml
+++ b/v17/leanback/res/values/colors.xml
@@ -73,7 +73,6 @@
     <!-- refactor naming here -->
     <color name="lb_guidedactions_background">#FF111111</color>
     <color name="lb_guidedactions_background_dark">#FF080808</color>
-    <color name="lb_guidedactions_selector_color">#26FFFFFF</color>
     <color name="lb_guidedactions_item_unselected_text_color">#FFF1F1F1</color>
     <!-- end refactor naming -->
 
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index a703808..c63f69e 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -235,7 +235,6 @@
     <item name="lb_guidedactions_width_weight_two_panels" format="float" type="string">1</item>
     <dimen name="lb_guidedactions_section_shadow_width">16dp</dimen>
     <dimen name="lb_guidedactions_elevation">12dp</dimen>
-    <dimen name="lb_guidedactions_selector_min_height">8dp</dimen>
     <dimen name="lb_guidedactions_vertical_padding">12dp</dimen>
 
     <item name="lb_guidedactions_item_disabled_text_alpha" format="float" type="string">0.25</item>
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index 06d47c6..71950cc 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -423,19 +423,12 @@
         <item name="android:scaleType">fitCenter</item>
     </style>
 
-    <!-- Style for the selector view in a GuidedActionsStylist's default layout. -->
-    <style name="Widget.Leanback.GuidedActionsSelectorStyle">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">@dimen/lb_guidedactions_selector_min_height</item>
-        <item name="android:layout_centerVertical">true</item>
-        <item name="android:background">@color/lb_guidedactions_selector_color</item>
-    </style>
-
     <!-- Style for the vertical grid of actions in a GuidedActionsStylist's default layout. -->
     <style name="Widget.Leanback.GuidedActionsListStyle">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:focusable">true</item>
+        <item name="android:focusableInTouchMode">true</item>
         <item name="focusOutEnd">true</item>
         <item name="focusOutFront">false</item>
         <item name="focusOutSideStart">false</item>
@@ -461,7 +454,9 @@
     <style name="Widget.Leanback.GuidedActionItemContainerStyle">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
+        <item name="android:foreground">?attr/guidedActionsSelectorDrawable</item>
         <item name="android:focusable">true</item>
+        <item name="android:focusableInTouchMode">true</item>
         <item name="android:minHeight">@dimen/lb_guidedactions_item_min_height</item>
         <item name="android:paddingBottom">@dimen/lb_guidedactions_vertical_padding</item>
         <item name="android:paddingStart">@dimen/lb_guidedactions_item_start_padding</item>
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index aa5d5c9..50835a3 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -139,12 +139,10 @@
         <item name="guidedActionsElevation">@dimen/lb_guidedactions_elevation</item>
         <item name="guidedActionsBackground">@color/lb_guidedactions_background</item>
         <item name="guidedActionsBackgroundDark">@color/lb_guidedactions_background_dark</item>
-        <item name="guidedActionsSelectorStyle">@style/Widget.Leanback.GuidedActionsSelectorStyle</item>
+        <item name="guidedActionsSelectorDrawable">?android:attr/selectableItemBackground</item>
         <item name="guidedActionsListStyle">@style/Widget.Leanback.GuidedActionsListStyle</item>
         <item name="guidedSubActionsListStyle">@style/Widget.Leanback.GuidedSubActionsListStyle</item>
         <item name="guidedButtonActionsListStyle">@style/Widget.Leanback.GuidedButtonActionsListStyle</item>
-        <item name="guidedActionsSelectorShowAnimation">@animator/lb_guidedactions_selector_show</item>
-        <item name="guidedActionsSelectorHideAnimation">@animator/lb_guidedactions_selector_hide</item>
 
         <item name="guidedActionItemContainerStyle">@style/Widget.Leanback.GuidedActionItemContainerStyle</item>
         <item name="guidedActionItemCheckmarkStyle">@style/Widget.Leanback.GuidedActionItemCheckmarkStyle</item>
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
index 545f8ca..b2397b3 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
@@ -480,16 +480,12 @@
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_root), "guidedactions_root");
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
-                R.id.guidedactions_selector), "guidedactions_selector");
-        TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_content), "guidedactions_content");
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_list_background), "guidedactions_list_background");
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_root2), "guidedactions_root2");
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
-                R.id.guidedactions_selector2), "guidedactions_selector2");
-        TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_content2), "guidedactions_content2");
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_list_background2), "guidedactions_list_background2");
@@ -1015,6 +1011,9 @@
         mSubAdapter = new GuidedActionAdapter(null, new GuidedActionAdapter.ClickListener() {
             @Override
             public void onGuidedActionClicked(GuidedAction action) {
+                if (mActionsStylist.isInExpandTransition()) {
+                    return;
+                }
                 if (GuidedStepFragment.this.onSubGuidedActionClicked(action)) {
                     collapseSubActions();
                 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
index d60cfec..725a487 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
@@ -482,16 +482,12 @@
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_root), "guidedactions_root");
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
-                R.id.guidedactions_selector), "guidedactions_selector");
-        TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_content), "guidedactions_content");
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_list_background), "guidedactions_list_background");
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_root2), "guidedactions_root2");
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
-                R.id.guidedactions_selector2), "guidedactions_selector2");
-        TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_content2), "guidedactions_content2");
         TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
                 R.id.guidedactions_list_background2), "guidedactions_list_background2");
@@ -1017,6 +1013,9 @@
         mSubAdapter = new GuidedActionAdapter(null, new GuidedActionAdapter.ClickListener() {
             @Override
             public void onGuidedActionClicked(GuidedAction action) {
+                if (mActionsStylist.isInExpandTransition()) {
+                    return;
+                }
                 if (GuidedStepSupportFragment.this.onSubGuidedActionClicked(action)) {
                     collapseSubActions();
                 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
index 219bb98..fda899f 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
@@ -16,6 +16,7 @@
 
 import android.content.Context;
 import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.os.Bundle;
@@ -139,8 +140,13 @@
             FocusHighlightHelper.setupHeaderItemFocusHighlight(listView);
         }
         if (mBackgroundColorSet) {
-            view.setBackgroundColor(mBackgroundColor);
+            listView.setBackgroundColor(mBackgroundColor);
             updateFadingEdgeToBrandColor(mBackgroundColor);
+        } else {
+            Drawable d = listView.getBackground();
+            if (d instanceof ColorDrawable) {
+                updateFadingEdgeToBrandColor(((ColorDrawable) d).getColor());
+            }
         }
         updateListViewVisibility();
     }
@@ -214,8 +220,8 @@
         mBackgroundColor = color;
         mBackgroundColorSet = true;
 
-        if (getView() != null) {
-            getView().setBackgroundColor(mBackgroundColor);
+        if (getVerticalGridView() != null) {
+            getVerticalGridView().setBackgroundColor(mBackgroundColor);
             updateFadingEdgeToBrandColor(mBackgroundColor);
         }
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
index ecf04d8..d998495 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.os.Bundle;
@@ -141,8 +142,13 @@
             FocusHighlightHelper.setupHeaderItemFocusHighlight(listView);
         }
         if (mBackgroundColorSet) {
-            view.setBackgroundColor(mBackgroundColor);
+            listView.setBackgroundColor(mBackgroundColor);
             updateFadingEdgeToBrandColor(mBackgroundColor);
+        } else {
+            Drawable d = listView.getBackground();
+            if (d instanceof ColorDrawable) {
+                updateFadingEdgeToBrandColor(((ColorDrawable) d).getColor());
+            }
         }
         updateListViewVisibility();
     }
@@ -216,8 +222,8 @@
         mBackgroundColor = color;
         mBackgroundColorSet = true;
 
-        if (getView() != null) {
-            getView().setBackgroundColor(mBackgroundColor);
+        if (getVerticalGridView() != null) {
+            getVerticalGridView().setBackgroundColor(mBackgroundColor);
             updateFadingEdgeToBrandColor(mBackgroundColor);
         }
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
index 9420154..272f5af 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
@@ -99,10 +99,14 @@
 
         public Object createFadeTransition(int fadingMode);
 
+        public Object createChangeTransform();
+
         public Object createChangeBounds(boolean reparent);
 
         public Object createFadeAndShortSlide(int edge);
 
+        public Object createFadeAndShortSlide(int edge, float distance);
+
         public void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay);
 
         public void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay);
@@ -146,6 +150,8 @@
 
         public Object loadTransition(Context context, int resId);
 
+        public void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject);
+
         public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup);
     }
 
@@ -233,11 +239,21 @@
         }
 
         @Override
+        public Object createChangeTransform() {
+            return new TransitionStub();
+        }
+
+        @Override
         public Object createFadeAndShortSlide(int edge) {
             return new TransitionStub();
         }
 
         @Override
+        public Object createFadeAndShortSlide(int edge, float distance) {
+            return new TransitionStub();
+        }
+
+        @Override
         public Object createSlide(int slideEdge) {
             return new TransitionStub();
         }
@@ -360,6 +376,10 @@
         }
 
         @Override
+        public void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
+        }
+
+        @Override
         public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
         }
     }
@@ -552,6 +572,16 @@
         }
 
         @Override
+        public Object createFadeAndShortSlide(int edge, float distance) {
+            return TransitionHelperApi21.createFadeAndShortSlide(edge, distance);
+        }
+
+        @Override
+        public void beginDelayedTransition(ViewGroup sceneRoot, Object transition) {
+            TransitionHelperApi21.beginDelayedTransition(sceneRoot, transition);
+        }
+
+        @Override
         public Object getEnterTransition(Window window) {
             return TransitionHelperApi21.getEnterTransition(window);
         }
@@ -585,6 +615,12 @@
         public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
             TransitionHelperApi21.setTransitionGroup(viewGroup, transitionGroup);
         }
+
+        @Override
+        public Object createChangeTransform() {
+            return TransitionHelperApi21.createChangeTransform();
+        }
+
     }
 
     static {
@@ -637,6 +673,10 @@
         return sImpl.createChangeBounds(reparent);
     }
 
+    public static Object createChangeTransform() {
+        return sImpl.createChangeTransform();
+    }
+
     public static void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) {
         sImpl.setChangeBoundsStartDelay(changeBounds, view, startDelay);
     }
@@ -780,6 +820,14 @@
         return sImpl.createFadeAndShortSlide(edge);
     }
 
+    public static Object createFadeAndShortSlide(int edge, float distance) {
+        return sImpl.createFadeAndShortSlide(edge, distance);
+    }
+
+    public static void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
+        sImpl.beginDelayedTransition(sceneRoot, transitionObject);
+    }
+
     public static void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
         sImpl.setTransitionGroup(viewGroup, transitionGroup);
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index 8f23dbd..344a308 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -2957,10 +2957,10 @@
          } else if (mOrientation == VERTICAL) {
              switch(direction) {
                  case View.FOCUS_LEFT:
-                     movement = (!mReverseFlowPrimary) ? PREV_ROW : NEXT_ROW;
+                     movement = (!mReverseFlowSecondary) ? PREV_ROW : NEXT_ROW;
                      break;
                  case View.FOCUS_RIGHT:
-                     movement = (!mReverseFlowPrimary) ? NEXT_ROW : PREV_ROW;
+                     movement = (!mReverseFlowSecondary) ? NEXT_ROW : PREV_ROW;
                      break;
                  case View.FOCUS_UP:
                      movement = PREV_ITEM;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
index fe1861b..5c39aba 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
@@ -25,8 +25,11 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Build.VERSION;
 import android.support.annotation.NonNull;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.transition.TransitionHelper;
+import android.support.v17.leanback.transition.TransitionListener;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.view.ViewCompat;
@@ -37,6 +40,7 @@
 import android.util.TypedValue;
 import android.view.animation.DecelerateInterpolator;
 import android.view.inputmethod.EditorInfo;
+import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -73,7 +77,6 @@
  * <p>
  * Note: If an alternate list layout is provided, the following view IDs must be supplied:
  * <ul>
- * <li>{@link android.support.v17.leanback.R.id#guidedactions_selector}</li>
  * <li>{@link android.support.v17.leanback.R.id#guidedactions_list}</li>
  * </ul><p>
  * These view IDs must be present in order for the stylist to function. The list ID must correspond
@@ -98,9 +101,7 @@
  *
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorShowAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorHideAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorStyle
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorDrawable
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsListStyle
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedSubActionsListStyle
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedButtonActionsListStyle
@@ -280,12 +281,9 @@
     private VerticalGridView mActionsGridView;
     private VerticalGridView mSubActionsGridView;
     private View mBgView;
-    private View mSelectorView;
     private View mContentView;
     private boolean mButtonActions;
 
-    private Animator mSelectorAnimator;
-
     // Cached values from resources
     private float mEnabledTextAlpha;
     private float mDisabledTextAlpha;
@@ -300,18 +298,7 @@
     private int mDisplayHeight;
 
     private GuidedAction mExpandedAction = null;
-
-    private final RecyclerView.OnScrollListener mOnGridScrollListener =
-            new RecyclerView.OnScrollListener() {
-        @Override
-        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
-            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
-                if (mSelectorView.getAlpha() != 1f) {
-                    updateSelectorView(true);
-                }
-            }
-        }
-    };
+    private Object mExpandTransition;
 
     /**
      * Creates a view appropriate for displaying a list of GuidedActions, using the provided
@@ -328,15 +315,6 @@
         mMainView = (ViewGroup) inflater.inflate(onProvideLayoutId(), container, false);
         mContentView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_content2 :
                 R.id.guidedactions_content);
-        mSelectorView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_selector2 :
-                R.id.guidedactions_selector);
-        mSelectorView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
-            @Override
-            public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                updateSelectorView(false);
-            }
-        });
         mBgView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_list_background2 :
                 R.id.guidedactions_list_background);
         if (mMainView instanceof VerticalGridView) {
@@ -350,24 +328,12 @@
             mActionsGridView.setWindowAlignmentOffset(0);
             mActionsGridView.setWindowAlignmentOffsetPercent(50f);
             mActionsGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-            if (mSelectorView != null) {
-                mActionsGridView.setOnScrollListener(mOnGridScrollListener);
-            }
             if (!mButtonActions) {
                 mSubActionsGridView = (VerticalGridView) mMainView.findViewById(
                         R.id.guidedactions_sub_list);
-                if (mSelectorView != null && mSubActionsGridView != null) {
-                    mSubActionsGridView.setOnScrollListener(mOnGridScrollListener);
-                }
             }
         }
 
-        if (mSelectorView != null) {
-            // ALlow focus to move to other views
-            mMainView.getViewTreeObserver().addOnGlobalFocusChangeListener(
-                    mGlobalFocusChangeListener);
-        }
-
         // Cache widths, chevron alpha values, max and min text lines, etc
         Context ctx = mMainView.getContext();
         TypedValue val = new TypedValue();
@@ -410,28 +376,14 @@
         return mButtonActions;
     }
 
-    final ViewTreeObserver.OnGlobalFocusChangeListener mGlobalFocusChangeListener =
-            new ViewTreeObserver.OnGlobalFocusChangeListener() {
-
-        @Override
-        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-            updateSelectorView(false);
-        }
-    };
-
     /**
      * Called when destroy the View created by GuidedActionsStylist.
      */
     public void onDestroyView() {
-        if (mSelectorView != null) {
-            mMainView.getViewTreeObserver().removeOnGlobalFocusChangeListener(
-                    mGlobalFocusChangeListener);
-        }
-        endSelectorAnimator();
         mExpandedAction = null;
+        mExpandTransition = null;
         mActionsGridView = null;
         mSubActionsGridView = null;
-        mSelectorView = null;
         mContentView = null;
         mBgView = null;
         mMainView = null;
@@ -795,15 +747,136 @@
      * hide the other items in main list.  When null, collapse the sub actions list.
      */
     public void setExpandedViewHolder(ViewHolder avh) {
-        if (mSubActionsGridView == null) {
+        if (mSubActionsGridView == null || isInExpandTransition()) {
             return;
         }
-        if (avh == null) {
-            mExpandedAction = null;
-        } else if (avh.getAction() != mExpandedAction) {
-            mExpandedAction = avh.getAction();
+        if (isExpandTransitionSupported()) {
+            startExpandedTransition(avh);
+        } else {
+            onUpdateExpandedViewHolder(avh);
         }
-        updateAllChevronAndVisibility(avh);
+    }
+
+    /**
+     * Returns true if it is running an expanding or collapsing transition, false otherwise.
+     * @return True if it is running an expanding or collapsing transition, false otherwise.
+     */
+    public boolean isInExpandTransition() {
+        return mExpandTransition != null;
+    }
+
+    /**
+     * Returns if expand/collapse animation is supported.  When this method returns true,
+     * {@link #startExpandedTransition(ViewHolder)} will be used.  When this method returns false,
+     * {@link #onUpdateExpandedViewHolder(ViewHolder)} will be called.
+     * @return True if it is running an expanding or collapsing transition, false otherwise.
+     */
+    public boolean isExpandTransitionSupported() {
+        return VERSION.SDK_INT >= 21;
+    }
+
+    /**
+     * Start transition to expand or collapse GuidedActionStylist.
+     * @param avh When not null, the GuidedActionStylist expands the sub actions of avh.  When null
+     * the GuidedActionStylist will collapse sub actions.
+     */
+    public void startExpandedTransition(ViewHolder avh) {
+        ViewHolder focusAvh = null; // expand / collapse view holder
+        final int count = mActionsGridView.getChildCount();
+        for (int i = 0; i < count; i++) {
+            ViewHolder vh = (ViewHolder) mActionsGridView
+                    .getChildViewHolder(mActionsGridView.getChildAt(i));
+            if (avh == null && vh.itemView.getVisibility() == View.VISIBLE) {
+                // going to collapse this one.
+                focusAvh = vh;
+                break;
+            } else if (avh != null && vh.getAction() == avh.getAction()) {
+                // going to expand this one.
+                focusAvh = vh;
+                break;
+            }
+        }
+        if (focusAvh == null) {
+            // huh?
+            onUpdateExpandedViewHolder(avh);
+            return;
+        }
+        Object set = TransitionHelper.createTransitionSet(false);
+        Object slideAndFade = TransitionHelper.createFadeAndShortSlide(Gravity.TOP | Gravity.BOTTOM,
+                (float) focusAvh.itemView.getHeight());
+        Object changeFocusItemTransform = TransitionHelper.createChangeTransform();
+        Object changeFocusItemBounds = TransitionHelper.createChangeBounds(false);
+        Object fadeGrid = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN |
+                TransitionHelper.FADE_OUT);
+        Object changeGridBounds = TransitionHelper.createChangeBounds(false);
+        if (avh == null) {
+            TransitionHelper.setStartDelay(slideAndFade, 150);
+            TransitionHelper.setStartDelay(changeFocusItemTransform, 100);
+            TransitionHelper.setStartDelay(changeFocusItemBounds, 100);
+        } else {
+            TransitionHelper.setStartDelay(fadeGrid, 100);
+            TransitionHelper.setStartDelay(changeGridBounds, 100);
+            TransitionHelper.setStartDelay(changeFocusItemTransform, 50);
+            TransitionHelper.setStartDelay(changeFocusItemBounds, 50);
+        }
+        for (int i = 0; i < count; i++) {
+            ViewHolder vh = (ViewHolder) mActionsGridView
+                    .getChildViewHolder(mActionsGridView.getChildAt(i));
+            if (vh == focusAvh) {
+                // going to expand/collapse this one.
+                TransitionHelper.include(changeFocusItemTransform, vh.itemView);
+                TransitionHelper.include(changeFocusItemBounds, vh.itemView);
+            } else {
+                // going to slide this item to top / bottom.
+                TransitionHelper.include(slideAndFade, vh.itemView);
+            }
+        }
+        TransitionHelper.include(fadeGrid, mSubActionsGridView);
+        TransitionHelper.include(changeGridBounds, mSubActionsGridView);
+        TransitionHelper.addTransition(set, slideAndFade);
+        TransitionHelper.addTransition(set, changeFocusItemTransform);
+        TransitionHelper.addTransition(set, changeFocusItemBounds);
+        TransitionHelper.addTransition(set, fadeGrid);
+        TransitionHelper.addTransition(set, changeGridBounds);
+        mExpandTransition = set;
+        TransitionHelper.addTransitionListener(mExpandTransition, new TransitionListener() {
+            @Override
+            public void onTransitionEnd(Object transition) {
+                mExpandTransition = null;
+            }
+        });
+        if (avh != null && mSubActionsGridView.getTop() != avh.itemView.getTop()) {
+            // For expanding, set the initial position of subActionsGridView before running
+            // a ChangeBounds on it.
+            final ViewHolder toUpdate = avh;
+            mSubActionsGridView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    if (mSubActionsGridView == null) {
+                        return;
+                    }
+                    mSubActionsGridView.removeOnLayoutChangeListener(this);
+                    mMainView.post(new Runnable() {
+                        public void run() {
+                            if (mMainView == null) {
+                                return;
+                            }
+                            TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
+                            onUpdateExpandedViewHolder(toUpdate);
+                        }
+                    });
+                }
+            });
+            ViewGroup.MarginLayoutParams lp =
+                    (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
+            lp.topMargin = avh.itemView.getTop();
+            lp.height = 0;
+            mSubActionsGridView.setLayoutParams(lp);
+            return;
+        }
+        TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
+        onUpdateExpandedViewHolder(avh);
     }
 
     /**
@@ -820,7 +893,17 @@
         return mExpandedAction;
     }
 
-    private void updateAllChevronAndVisibility(final ViewHolder avh) {
+    /**
+     * Expand or collapse GuidedActionStylist.
+     * @param avh When not null, the GuidedActionStylist expands the sub actions of avh.  When null
+     * the GuidedActionStylist will collapse sub actions.
+     */
+    public void onUpdateExpandedViewHolder(ViewHolder avh) {
+        if (avh == null) {
+            mExpandedAction = null;
+        } else if (avh.getAction() != mExpandedAction) {
+            mExpandedAction = avh.getAction();
+        }
         final int count = mActionsGridView.getChildCount();
         for (int i = 0; i < count; i++) {
             ViewHolder vh = (ViewHolder) mActionsGridView
@@ -828,43 +911,41 @@
             updateChevronAndVisibility(vh);
         }
         if (mSubActionsGridView != null) {
-            updateExpandStatus(avh);
-        }
-    }
-
-    private void updateExpandStatus(ViewHolder avh) {
-        if (avh != null) {
-            mSubActionsGridView.setVisibility(View.VISIBLE);
-            mSubActionsGridView.requestFocus();
-            mSubActionsGridView.setSelectedPosition(0);
-            ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
-                    mActionsGridView.getLayoutParams();
-            lp.topMargin = - avh.itemView.getHeight();
-            lp.bottomMargin = avh.itemView.getHeight();
-            mActionsGridView.setLayoutParams(lp);
-            lp = (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
-            lp.topMargin = avh.itemView.getTop();
-            mSubActionsGridView.setLayoutParams(lp);
-            ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
-                    .setActions(avh.getAction().getSubActions());
-        } else {
-            mSubActionsGridView.setVisibility(View.INVISIBLE);
-            ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
-                    .setActions(Collections.EMPTY_LIST);
-            ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
-                    mActionsGridView.getLayoutParams();
-            lp.bottomMargin = lp.topMargin = 0;
-            mActionsGridView.setLayoutParams(lp);
-            mActionsGridView.requestFocus();
+            if (avh != null) {
+                ViewGroup.MarginLayoutParams lp =
+                        (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
+                lp.topMargin = avh.itemView.getTop();
+                lp.height = ViewGroup.MarginLayoutParams.MATCH_PARENT;
+                mSubActionsGridView.setLayoutParams(lp);
+                mSubActionsGridView.setVisibility(View.VISIBLE);
+                mSubActionsGridView.requestFocus();
+                mSubActionsGridView.setSelectedPosition(0);
+                ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
+                        .setActions(avh.getAction().getSubActions());
+            } else {
+                mSubActionsGridView.setVisibility(View.INVISIBLE);
+                ViewGroup.MarginLayoutParams lp =
+                        (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
+                lp.height = 0;
+                mSubActionsGridView.setLayoutParams(lp);
+                ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
+                        .setActions(Collections.EMPTY_LIST);
+                mActionsGridView.requestFocus();
+            }
         }
     }
 
     private void updateChevronAndVisibility(ViewHolder vh) {
         if (!vh.isSubAction()) {
-            if (mExpandedAction == null || vh.getAction() == mExpandedAction) {
+            if (mExpandedAction == null) {
                 vh.itemView.setVisibility(View.VISIBLE);
+                vh.itemView.setTranslationY(0);
+            } else if (vh.getAction() == mExpandedAction) {
+                vh.itemView.setVisibility(View.VISIBLE);
+                vh.itemView.setTranslationY(- vh.itemView.getHeight());
             } else {
                 vh.itemView.setVisibility(View.INVISIBLE);
+                vh.itemView.setTranslationY(0);
             }
         }
         if (vh.mChevronView != null) {
@@ -953,55 +1034,4 @@
         return (int)(mDisplayHeight - 2*mVerticalPadding - 2*mTitleMaxLines*title.getLineHeight());
     }
 
-    private void endSelectorAnimator() {
-        if (mSelectorAnimator != null) {
-            mSelectorAnimator.end();
-            mSelectorAnimator = null;
-        }
-    }
-
-    private void updateSelectorView(boolean animate) {
-        if ((mActionsGridView == null && mSubActionsGridView == null)
-                || mSelectorView == null || mSelectorView.getHeight() <= 0) {
-            return;
-        }
-        RecyclerView actionsGridView = null;
-        View focusedChild = mActionsGridView.getFocusedChild();
-        if (focusedChild == null && mSubActionsGridView != null) {
-            focusedChild = mSubActionsGridView.getFocusedChild();
-            if (focusedChild != null) {
-                actionsGridView = mSubActionsGridView;
-            }
-        } else {
-            actionsGridView = mActionsGridView;
-        }
-        endSelectorAnimator();
-        if (focusedChild == null || !actionsGridView.hasFocus()
-                || actionsGridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
-            if (animate) {
-                mSelectorAnimator = createAnimator(mSelectorView,
-                        R.attr.guidedActionsSelectorHideAnimation);
-                mSelectorAnimator.start();
-            } else {
-                mSelectorView.setAlpha(0f);
-            }
-        } else {
-            final float scaleY = (float) focusedChild.getHeight() / mSelectorView.getHeight();
-            Rect r = new Rect(0, 0, focusedChild.getWidth(), focusedChild.getHeight());
-            mMainView.offsetDescendantRectToMyCoords(focusedChild, r);
-            mMainView.offsetRectIntoDescendantCoords(mSelectorView, r);
-            mSelectorView.setTranslationY(r.exactCenterY() - mSelectorView.getHeight() * 0.5f);
-            if (animate) {
-                mSelectorAnimator = createAnimator(mSelectorView,
-                        R.attr.guidedActionsSelectorShowAnimation);
-                ((ObjectAnimator) ((AnimatorSet) mSelectorAnimator).getChildAnimations().get(1))
-                        .setFloatValues(scaleY);
-                mSelectorAnimator.start();
-            } else {
-                mSelectorView.setAlpha(1f);
-                mSelectorView.setScaleY(scaleY);
-            }
-        }
-    }
-
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingLinearLayoutWithForeground.java b/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingLinearLayoutWithForeground.java
new file mode 100644
index 0000000..52832e9
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingLinearLayoutWithForeground.java
@@ -0,0 +1,141 @@
+/*
+ * 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.v17.leanback.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build.VERSION;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+/**
+ * Implements foreground drawable before M and falls back to M's foreground implementation.
+ * @hide
+ */
+class NonOverlappingLinearLayoutWithForeground extends LinearLayout {
+
+    private static final int VERSION_M = 23;
+
+    private Drawable mForeground;
+    private boolean mForegroundBoundsChanged;
+    private final Rect mSelfBounds = new Rect();
+
+    public NonOverlappingLinearLayoutWithForeground(Context context) {
+        this(context, null);
+    }
+
+    public NonOverlappingLinearLayoutWithForeground(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NonOverlappingLinearLayoutWithForeground(Context context, AttributeSet attrs,
+            int defStyle) {
+        super(context, attrs, defStyle);
+        if (context.getApplicationInfo().targetSdkVersion >= VERSION_M
+                && VERSION.SDK_INT >= VERSION_M) {
+            // dont need do anything, base View constructor >=M already reads the foreground if
+            // targetSDK is >= M.
+        } else {
+            // in other cases, including M but targetSDK is less than M, we need setForeground in
+            // code.
+            TypedArray a = context.obtainStyledAttributes(attrs,
+                    new int[] { android.R.attr.foreground });
+            Drawable d = a.getDrawable(0);
+            if (d != null) {
+                setForegroundCompat(d);
+            }
+        }
+    }
+
+    public void setForegroundCompat(Drawable d) {
+        if (VERSION.SDK_INT >= VERSION_M) {
+            // From M,  foreground is naturally supported.
+            ForegroundHelper.getInstance().setForeground(this, d);
+        } else {
+            // before M, do our own customized foreground draw.
+            if (mForeground != d) {
+                mForeground = d;
+                mForegroundBoundsChanged = true;
+                setWillNotDraw(false);
+                mForeground.setCallback(this);
+                if (mForeground.isStateful()) {
+                    mForeground.setState(getDrawableState());
+                }
+            }
+        }
+    }
+
+    public Drawable getForegroundCompat() {
+        if (VERSION.SDK_INT >= VERSION_M) {
+            return ForegroundHelper.getInstance().getForeground(this);
+        } else {
+            return mForeground;
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        if (mForeground != null) {
+            final Drawable foreground = mForeground;
+            if (mForegroundBoundsChanged) {
+                mForegroundBoundsChanged = false;
+                final Rect selfBounds = mSelfBounds;
+                final int w = getRight() - getLeft();
+                final int h = getBottom() - getTop();
+                selfBounds.set(0, 0, w, h);
+                foreground.setBounds(selfBounds);
+            }
+            foreground.draw(canvas);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        mForegroundBoundsChanged |= changed;
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || (who == mForeground);
+    }
+
+    @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        if (mForeground != null) {
+            mForeground.jumpToCurrentState();
+        }
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        if (mForeground != null && mForeground.isStateful()) {
+            mForeground.setState(getDrawableState());
+        }
+    }
+
+    /**
+     * Avoids creating a hardware layer when animating alpha.
+     */
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/v17/tests/res/layout/vertical_grid_ltr.xml b/v17/tests/res/layout/vertical_grid_ltr.xml
new file mode 100644
index 0000000..6a390a3
--- /dev/null
+++ b/v17/tests/res/layout/vertical_grid_ltr.xml
@@ -0,0 +1,33 @@
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:lb="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layoutDirection="ltr"
+    >
+  <Button android:id="@+id/button"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_alignParentStart="true"
+      android:text="button"
+      />
+  <android.support.v17.leanback.widget.VerticalGridViewEx
+      android:id="@+id/gridview"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:layout_toEndOf="@id/button"
+      android:clipToPadding="false"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:background="#00ffff"
+      lb:horizontalMargin="12dip"
+      lb:verticalMargin="24dip"
+      lb:numberOfColumns="1"
+      lb:columnWidth="150dip"
+      lb:focusOutSideStart="false"
+      lb:focusOutSideEnd="true"
+      android:paddingBottom="12dip"
+      android:paddingLeft="12dip"
+      android:paddingRight="12dip"
+      android:paddingTop="12dip" />
+</RelativeLayout>
diff --git a/v17/tests/res/layout/vertical_grid_rtl.xml b/v17/tests/res/layout/vertical_grid_rtl.xml
new file mode 100644
index 0000000..87b2054
--- /dev/null
+++ b/v17/tests/res/layout/vertical_grid_rtl.xml
@@ -0,0 +1,33 @@
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:lb="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layoutDirection="rtl"
+    >
+  <Button android:id="@+id/button"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_alignParentStart="true"
+      android:text="button"
+      />
+  <android.support.v17.leanback.widget.VerticalGridViewEx
+      android:id="@+id/gridview"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:layout_toEndOf="@id/button"
+      android:clipToPadding="false"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:background="#00ffff"
+      lb:horizontalMargin="12dip"
+      lb:verticalMargin="24dip"
+      lb:numberOfColumns="1"
+      lb:columnWidth="150dip"
+      lb:focusOutSideStart="false"
+      lb:focusOutSideEnd="true"
+      android:paddingBottom="12dip"
+      android:paddingLeft="12dip"
+      android:paddingRight="12dip"
+      android:paddingTop="12dip" />
+</RelativeLayout>
diff --git a/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java b/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java
index 335f449..dba1754 100644
--- a/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java
+++ b/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java
@@ -1100,6 +1100,58 @@
 
     }
 
+    public void testLtrFocusOutStartDisabled() throws Throwable {
+        final int numItems = 200;
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_ltr);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+        initActivity(intent);
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.requestFocus();
+                mGridView.setSelectedPositionSmooth(0);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
+        waitForScrollIdle(mVerifyLayout);
+        assertTrue(mGridView.hasFocus());
+    }
+
+    public void testRtlFocusOutStartDisabled() throws Throwable {
+        final int numItems = 200;
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_rtl);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+        initActivity(intent);
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.requestFocus();
+                mGridView.setSelectedPositionSmooth(0);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        waitForScrollIdle(mVerifyLayout);
+        assertTrue(mGridView.hasFocus());
+    }
+
     public void testTransferFocusable() throws Throwable {
         final int numItems = 200;
         final int numColumns = 3;
diff --git a/v4/java/android/support/v4/media/session/MediaSessionCompat.java b/v4/java/android/support/v4/media/session/MediaSessionCompat.java
index d35ada6..4980a9f 100644
--- a/v4/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/v4/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -25,6 +25,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Bundle;
@@ -1303,8 +1304,42 @@
             }
         }
 
+        /**
+         * Clones the given {@link MediaMetadataCompat}, deep-copying bitmaps in the metadata if
+         * they exist. If there is no bitmap in the metadata, this method just returns the given
+         * metadata.
+         *
+         * @param metadata A {@link MediaMetadataCompat} to be cloned.
+         * @return A newly cloned metadata if it contains bitmaps. Otherwise, the given metadata
+         *         will be returned.
+         */
+        private MediaMetadataCompat cloneMetadataIfNeeded(MediaMetadataCompat metadata) {
+            if (metadata == null) {
+                return null;
+            } else if (!metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ART)
+                    && !metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)) {
+                return metadata;
+            }
+            MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(metadata);
+            Bitmap artBitmap = metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ART);
+            if (artBitmap != null) {
+                builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART,
+                        artBitmap.copy(artBitmap.getConfig(), false));
+            }
+            Bitmap albumArtBitmap = metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
+            if (albumArtBitmap != null) {
+                builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART,
+                        albumArtBitmap.copy(albumArtBitmap.getConfig(), false));
+            }
+            return builder.build();
+        }
+
         @Override
         public void setMetadata(MediaMetadataCompat metadata) {
+            if (android.os.Build.VERSION.SDK_INT >= 14 && metadata != null) {
+                // Clone bitmaps in metadata for protecting them to be recycled by RCC.
+                metadata = cloneMetadataIfNeeded(metadata);
+            }
             synchronized (mLock) {
                 mMetadata = metadata;
             }
diff --git a/v4/tests/AndroidManifest.xml b/v4/tests/AndroidManifest.xml
index b80e400..63b3fb6 100644
--- a/v4/tests/AndroidManifest.xml
+++ b/v4/tests/AndroidManifest.xml
@@ -22,10 +22,17 @@
             android:targetSdkVersion="23"
             tools:overrideLibrary="android.support.test,android.support.test.espresso, android.support.test.espresso.idling"/>
 
+    <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.READ_CONTACTS"/>
+    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+
     <application android:supportsRtl="true">
         <uses-library android:name="android.test.runner" />
         <activity android:name="android.support.v4.widget.test.TextViewTestActivity"/>
 
+        <activity android:name="android.support.v4.view.ViewPagerActivity"/>
+
         <activity android:name="android.support.v4.widget.TestActivity"/>
 
         <activity
diff --git a/v4/tests/NO_DOCS b/v4/tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/v4/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/v4/tests/java/android/support/v4/content/ContextCompatTest.java b/v4/tests/java/android/support/v4/content/ContextCompatTest.java
index 8f3cd04..e2fc0b7 100644
--- a/v4/tests/java/android/support/v4/content/ContextCompatTest.java
+++ b/v4/tests/java/android/support/v4/content/ContextCompatTest.java
@@ -17,6 +17,7 @@
 
 import android.content.res.ColorStateList;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.v4.ThemedYellowActivity;
@@ -40,8 +41,8 @@
     public void testGetColor() throws Throwable {
         Context context = getActivity();
 
-        assertEquals("Unthemed color load",
-                ContextCompat.getColor(context, R.color.text_color), 0xFFFF8090);
+        assertEquals("Unthemed color load", 0xFFFF8090,
+                ContextCompat.getColor(context, R.color.text_color));
 
         if (Build.VERSION.SDK_INT >= 23) {
             // The following test is only expected to pass on v23+ devices. The result of
@@ -59,28 +60,28 @@
 
         ColorStateList unthemedColorStateList =
                 ContextCompat.getColorStateList(context, R.color.complex_unthemed_selector);
-        assertEquals("Unthemed color state list load: default",
-                unthemedColorStateList.getDefaultColor(), 0xFF70A0C0);
-        assertEquals("Unthemed color state list load: focused",
+        assertEquals("Unthemed color state list load: default", 0xFF70A0C0,
+                unthemedColorStateList.getDefaultColor());
+        assertEquals("Unthemed color state list load: focused", 0xFF70B0F0,
                 unthemedColorStateList.getColorForState(
-                        new int[] {android.R.attr.state_focused}, 0), 0xFF70B0F0);
-        assertEquals("Unthemed color state list load: pressed",
+                        new int[]{android.R.attr.state_focused}, 0));
+        assertEquals("Unthemed color state list load: pressed", 0xFF6080B0,
                 unthemedColorStateList.getColorForState(
-                        new int[] {android.R.attr.state_pressed}, 0), 0xFF6080B0);
+                        new int[]{android.R.attr.state_pressed}, 0));
 
         if (Build.VERSION.SDK_INT >= 23) {
             // The following tests are only expected to pass on v23+ devices. The result of
             // calling theme-aware getColorStateList() in pre-v23 is undefined.
             ColorStateList themedYellowColorStateList =
                     ContextCompat.getColorStateList(context, R.color.complex_themed_selector);
-            assertEquals("Themed yellow color state list load: default",
-                    themedYellowColorStateList.getDefaultColor(), 0xFFF0B000);
-            assertEquals("Themed yellow color state list load: focused",
+            assertEquals("Themed yellow color state list load: default", 0xFFF0B000,
+                    themedYellowColorStateList.getDefaultColor());
+            assertEquals("Themed yellow color state list load: focused", 0xFFF0A020,
                     themedYellowColorStateList.getColorForState(
-                            new int[]{android.R.attr.state_focused}, 0), 0xFFF0A020);
-            assertEquals("Themed yellow color state list load: pressed",
+                            new int[]{android.R.attr.state_focused}, 0));
+            assertEquals("Themed yellow color state list load: pressed", 0xFFE0A040,
                     themedYellowColorStateList.getColorForState(
-                            new int[]{android.R.attr.state_pressed}, 0), 0xFFE0A040);
+                            new int[]{android.R.attr.state_pressed}, 0));
         }
     }
 
@@ -103,4 +104,62 @@
                     themedYellowDrawable, 0xFFF0B000);
         }
     }
-}
+
+
+    @UiThreadTest
+    @SmallTest
+    public void testCheckSelfPermission() throws Throwable {
+        Context context = getActivity();
+
+        try {
+            ContextCompat.checkSelfPermission(context, null);
+            fail("Should have thrown an exception on null parameter");
+        } catch (IllegalArgumentException iae) {
+            // This is the expected condition - just ignore and continue with the rest of the
+            // tests in this method.
+        }
+
+        assertEquals("Vibrate permission granted", PackageManager.PERMISSION_GRANTED,
+                ContextCompat.checkSelfPermission(context,
+                        android.Manifest.permission.VIBRATE));
+        assertEquals("Wake lock permission granted", PackageManager.PERMISSION_GRANTED,
+                ContextCompat.checkSelfPermission(context,
+                        android.Manifest.permission.WAKE_LOCK));
+
+        if (Build.VERSION.SDK_INT >= 23) {
+            // As documented in http://developer.android.com/training/permissions/requesting.html
+            // starting in Android M (v23) dangerous permissions are not granted automactically
+            // to apps targeting SDK 23 even if those are defined in the manifest.
+            // This is why the following permissions are expected to be denied.
+            assertEquals("Read contacts permission granted", PackageManager.PERMISSION_DENIED,
+                    ContextCompat.checkSelfPermission(context,
+                            android.Manifest.permission.READ_CONTACTS));
+            assertEquals("Write contacts permission granted", PackageManager.PERMISSION_DENIED,
+                    ContextCompat.checkSelfPermission(context,
+                            android.Manifest.permission.WRITE_CONTACTS));
+        } else {
+            // And on older devices declared dangerous permissions are expected to be granted.
+            assertEquals("Read contacts permission denied", PackageManager.PERMISSION_GRANTED,
+                    ContextCompat.checkSelfPermission(context,
+                            android.Manifest.permission.READ_CONTACTS));
+            assertEquals("Write contacts permission denied", PackageManager.PERMISSION_GRANTED,
+                    ContextCompat.checkSelfPermission(context,
+                            android.Manifest.permission.WRITE_CONTACTS));
+        }
+
+        // The following permissions (normal and dangerous) are expected to be denied as they are
+        // not declared in our manifest.
+        assertEquals("Access network state permission denied", PackageManager.PERMISSION_DENIED,
+                ContextCompat.checkSelfPermission(context,
+                        android.Manifest.permission.ACCESS_NETWORK_STATE));
+        assertEquals("Bluetooth permission denied", PackageManager.PERMISSION_DENIED,
+                ContextCompat.checkSelfPermission(context,
+                        android.Manifest.permission.BLUETOOTH));
+        assertEquals("Call phone permission denied", PackageManager.PERMISSION_DENIED,
+                ContextCompat.checkSelfPermission(context,
+                        android.Manifest.permission.CALL_PHONE));
+        assertEquals("Delete packages permission denied", PackageManager.PERMISSION_DENIED,
+                ContextCompat.checkSelfPermission(context,
+                        android.Manifest.permission.DELETE_PACKAGES));
+    }
+}
\ No newline at end of file
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerActions.java b/v4/tests/java/android/support/v4/view/ViewPagerActions.java
new file mode 100644
index 0000000..978cfd5
--- /dev/null
+++ b/v4/tests/java/android/support/v4/view/ViewPagerActions.java
@@ -0,0 +1,172 @@
+/*
+ * 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.v4.view;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
+
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.v4.view.ViewPager;
+import android.view.View;
+
+import org.hamcrest.Matcher;
+
+public class ViewPagerActions {
+
+    /**
+     * Moves <code>ViewPager</code> to the right by one page.
+     */
+    public static ViewAction scrollRight() {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager scroll one page to the right";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                int current = viewPager.getCurrentItem();
+                viewPager.setCurrentItem(current + 1, false);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Moves <code>ViewPager</code> to the left by one page.
+     */
+    public static ViewAction scrollLeft() {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager scroll one page to the left";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                int current = viewPager.getCurrentItem();
+                viewPager.setCurrentItem(current - 1, false);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Moves <code>ViewPager</code> to the last page.
+     */
+    public static ViewAction scrollToLast() {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager scroll to last page";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                int size = viewPager.getAdapter().getCount();
+                if (size > 0) {
+                    viewPager.setCurrentItem(size - 1, false);
+                }
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Moves <code>ViewPager</code> to the first page.
+     */
+    public static ViewAction scrollToFirst() {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager scroll to first page";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                int size = viewPager.getAdapter().getCount();
+                if (size > 0) {
+                    viewPager.setCurrentItem(0, false);
+                }
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Moves <code>ViewPager</code> to specific page.
+     */
+    public static ViewAction scrollToPage(final int page) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager move one page to the right";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                viewPager.setCurrentItem(page, false);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+}
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerActivity.java b/v4/tests/java/android/support/v4/view/ViewPagerActivity.java
new file mode 100644
index 0000000..deeada0
--- /dev/null
+++ b/v4/tests/java/android/support/v4/view/ViewPagerActivity.java
@@ -0,0 +1,103 @@
+/*
+ * 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.v4.view;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v4.test.R;
+import android.support.v4.view.ViewPager;
+import android.util.Pair;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import java.util.ArrayList;
+
+public class ViewPagerActivity extends Activity {
+    private static class ColorPagerAdapter extends PagerAdapter {
+        private ArrayList<Pair<String, Integer>> mEntries = new ArrayList<>();
+
+        public void add(String title, int color) {
+            mEntries.add(new Pair(title, color));
+        }
+
+        @Override
+        public int getCount() {
+            return mEntries.size();
+        }
+
+        @Override
+        public Object instantiateItem(ViewGroup container, int position) {
+            final View view = new View(container.getContext());
+            view.setBackgroundColor(mEntries.get(position).second);
+
+            // Unlike ListView adapters, the ViewPager adapter is responsible
+            // for adding the view to the container.
+            container.addView(view);
+
+            return new ViewHolder(view, position);
+        }
+
+        @Override
+        public void destroyItem(ViewGroup container, int position, Object object) {
+            // The adapter is also responsible for removing the view.
+            container.removeView(((ViewHolder) object).view);
+        }
+
+        @Override
+        public int getItemPosition(Object object) {
+            return ((ViewHolder) object).position;
+        }
+
+        @Override
+        public boolean isViewFromObject(View view, Object object) {
+            return ((ViewHolder) object).view == view;
+        }
+
+        @Override
+        public CharSequence getPageTitle(int position) {
+            return mEntries.get(position).first;
+        }
+
+        private static class ViewHolder {
+            final View view;
+            final int position;
+
+            public ViewHolder(View view, int position) {
+                this.view = view;
+                this.position = position;
+            }
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        setContentView(R.layout.view_pager_layout);
+
+        ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
+
+        ColorPagerAdapter adapter = new ColorPagerAdapter();
+        adapter.add("Red", Color.RED);
+        adapter.add("Green", Color.GREEN);
+        adapter.add("Blue", Color.BLUE);
+
+        viewPager.setAdapter(adapter);
+    }
+}
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerTest.java b/v4/tests/java/android/support/v4/view/ViewPagerTest.java
new file mode 100644
index 0000000..5ccc95c
--- /dev/null
+++ b/v4/tests/java/android/support/v4/view/ViewPagerTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.v4.view;
+
+import android.support.test.espresso.Espresso;
+import android.support.test.espresso.UiController;
+import android.support.v4.test.R;
+import android.support.v4.view.ViewPager;
+import android.support.v4.widget.TestActivity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+import org.hamcrest.Matcher;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.swipeLeft;
+import static android.support.test.espresso.action.ViewActions.swipeRight;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+public class ViewPagerTest extends ActivityInstrumentationTestCase2<ViewPagerActivity> {
+    private ViewPager mViewPager;
+
+    public ViewPagerTest() {
+        super("android.support.v4.view", ViewPagerActivity.class);
+    }
+
+    public void setUp() throws Exception {
+        super.setUp();
+
+        final ViewPagerActivity activity = getActivity();
+        mViewPager = (ViewPager) activity.findViewById(R.id.pager);
+        mViewPager.setCurrentItem(0);
+    }
+
+    @SmallTest
+    public void testPageSelections() {
+        assertEquals("Initial state", 0, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(ViewPagerActions.scrollRight());
+        assertEquals("Scroll right", 1, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(ViewPagerActions.scrollRight());
+        assertEquals("Scroll right", 2, mViewPager.getCurrentItem());
+
+        // Try "scrolling" beyond the last page and test that we're still on the last page.
+        onView(withId(R.id.pager)).perform(ViewPagerActions.scrollRight());
+        assertEquals("Scroll right beyond last page", 2, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(ViewPagerActions.scrollLeft());
+        assertEquals("Scroll left", 1, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(ViewPagerActions.scrollLeft());
+        assertEquals("Scroll left", 0, mViewPager.getCurrentItem());
+
+        // Try "scrolling" beyond the first page and test that we're still on the first page.
+        onView(withId(R.id.pager)).perform(ViewPagerActions.scrollLeft());
+        assertEquals("Scroll left beyond first page", 0, mViewPager.getCurrentItem());
+
+    }
+
+    @SmallTest
+    public void testPageSwipes() {
+        assertEquals("Initial state", 0, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(swipeLeft());
+        assertEquals("Swipe left", 1, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(swipeLeft());
+        assertEquals("Swipe left", 2, mViewPager.getCurrentItem());
+
+        // Try swiping beyond the last page and test that we're still on the last page.
+        onView(withId(R.id.pager)).perform(swipeLeft());
+        assertEquals("Swipe left beyond last page", 2, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(swipeRight());
+        assertEquals("Swipe right", 1, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(swipeRight());
+        assertEquals("Swipe right", 0, mViewPager.getCurrentItem());
+
+        // Try swiping beyond the first page and test that we're still on the first page.
+        onView(withId(R.id.pager)).perform(swipeRight());
+        assertEquals("Swipe right beyond first page", 0, mViewPager.getCurrentItem());
+    }
+
+    @SmallTest
+    public void testPageSwipesComposite() {
+        assertEquals("Initial state", 0, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(swipeLeft(), swipeLeft());
+        assertEquals("Swipe twice left", 2, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(swipeLeft(), swipeRight());
+        assertEquals("Swipe left beyond last page and then right", 1, mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(swipeRight(), swipeRight());
+        assertEquals("Swipe right and then right beyond first page", 0,
+                mViewPager.getCurrentItem());
+
+        onView(withId(R.id.pager)).perform(swipeRight(), swipeLeft());
+        assertEquals("Swipe right beyond first page and then left", 1, mViewPager.getCurrentItem());
+    }
+}
diff --git a/v4/tests/res/layout/view_pager_layout.xml b/v4/tests/res/layout/view_pager_layout.xml
new file mode 100644
index 0000000..de248df
--- /dev/null
+++ b/v4/tests/res/layout/view_pager_layout.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<android.support.v4.view.ViewPager
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/pager"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <android.support.v4.view.PagerTitleStrip
+        android:id="@+id/titles"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top" />
+
+</android.support.v4.view.ViewPager>
+
diff --git a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
index f87109b..800350c 100644
--- a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
+++ b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
@@ -1119,6 +1119,7 @@
             }
 
             cancelPositionAnimator();
+            setThumbPosition(isChecked() ? 1 : 0);
         }
     }
 
diff --git a/v7/appcompat/tests/NO_DOCS b/v7/appcompat/tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/v7/appcompat/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
index 5856f38..bc961a7 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
@@ -25,6 +25,7 @@
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
 import android.support.annotation.NonNull;
@@ -72,6 +73,8 @@
     private RouteAdapter mAdapter;
     private ListView mListView;
     private boolean mAttachedToWindow;
+    private AsyncTask<Void, Void, Void> mRefreshRoutesTask;
+    private AsyncTask<Void, Void, Void> mOnItemClickTask;
 
     public MediaRouteChooserDialog(Context context) {
         this(context, 0);
@@ -197,12 +200,39 @@
      */
     public void refreshRoutes() {
         if (mAttachedToWindow) {
-            mRoutes.clear();
-            mRoutes.addAll(mRouter.getRoutes());
-            onFilterRoutes(mRoutes);
-            RouteComparator.loadRouteUsageScores(getContext(), mRoutes);
-            Collections.sort(mRoutes, RouteComparator.sInstance);
-            mAdapter.notifyDataSetChanged();
+            if (mRefreshRoutesTask != null) {
+                mRefreshRoutesTask.cancel(true);
+                mRefreshRoutesTask = null;
+            }
+            mRefreshRoutesTask = new AsyncTask<Void, Void, Void>() {
+                private ArrayList<MediaRouter.RouteInfo> mNewRoutes;
+
+                @Override
+                protected void onPreExecute() {
+                    mNewRoutes = new ArrayList<>(mRouter.getRoutes());
+                    onFilterRoutes(mNewRoutes);
+                }
+
+                @Override
+                protected Void doInBackground(Void... params) {
+                    // In API 4 ~ 10, AsyncTasks are running in parallel. Needs synchronization.
+                    synchronized (MediaRouteChooserDialog.this) {
+                        if (!isCancelled()) {
+                            RouteComparator.loadRouteUsageScores(getContext(), mNewRoutes);
+                        }
+                    }
+                    return null;
+                }
+
+                @Override
+                protected void onPostExecute(Void params) {
+                    mRoutes.clear();
+                    mRoutes.addAll(mNewRoutes);
+                    Collections.sort(mRoutes, RouteComparator.sInstance);
+                    mAdapter.notifyDataSetChanged();
+                    mRefreshRoutesTask = null;
+                }
+            }.execute();
         }
     }
 
@@ -277,11 +307,26 @@
 
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            MediaRouter.RouteInfo route = getItem(position);
-            if (route.isEnabled()) {
-                route.select();
-                RouteComparator.storeRouteUsageScores(getContext(), route.getId());
-                dismiss();
+            final MediaRouter.RouteInfo route = getItem(position);
+            if (route.isEnabled() && mOnItemClickTask == null) {
+                mOnItemClickTask = new AsyncTask<Void, Void, Void>() {
+                    @Override
+                    protected void onPreExecute() {
+                        route.select();
+                    }
+
+                    @Override
+                    protected Void doInBackground(Void... params) {
+                        RouteComparator.storeRouteUsageScores(getContext(), route.getId());
+                        return null;
+                    }
+
+                    @Override
+                    protected void onPostExecute(Void params) {
+                        dismiss();
+                        mOnItemClickTask = null;
+                    }
+                }.execute();
             }
         }
 
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
index 1f375a0..0e38e33 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
@@ -62,10 +62,14 @@
 import android.widget.TextView;
 
 import java.io.BufferedInputStream;
+import java.io.InputStream;
 import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * This class implements the route controller dialog for {@link MediaRouter}.
@@ -83,6 +87,7 @@
     // to allow the route provider time to propagate the change and publish a new
     // route descriptor.
     private static final int VOLUME_UPDATE_DELAY_MILLIS = 500;
+    private static final int CONNECTION_TIMEOUT_MILLIS = (int) TimeUnit.SECONDS.toMillis(30L);
 
     private static final int BUTTON_NEUTRAL_RES_ID = android.R.id.button3;
     private static final int BUTTON_DISCONNECT_RES_ID = android.R.id.button2;
@@ -759,6 +764,15 @@
         view.setLayoutParams(lp);
     }
 
+    private static boolean uriEquals(Uri uri1, Uri uri2) {
+        if (uri1 != null && uri1.equals(uri2)) {
+            return true;
+        } else if (uri1 == null && uri2 == null) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Returns desired art height to fit into controller dialog.
      */
@@ -955,7 +969,7 @@
 
         @Override
         protected void onPreExecute() {
-            if (mArtIconBitmap == mIconBitmap && mArtIconUri == mIconUri) {
+            if (!isIconChanged()) {
                 // Already handled the current art.
                 cancel(true);
             }
@@ -967,18 +981,12 @@
             if (mIconBitmap != null) {
                 art = mIconBitmap;
             } else if (mIconUri != null) {
-                String scheme = mIconUri.getScheme();
-                if (!(ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
-                        || ContentResolver.SCHEME_CONTENT.equals(scheme)
-                        || ContentResolver.SCHEME_FILE.equals(scheme))) {
-                    Log.w(TAG, "Icon Uri should point to local resources.");
-                    return null;
-                }
-                BufferedInputStream stream = null;
+                InputStream stream = null;
                 try {
-                    stream = new BufferedInputStream(
-                            mContext.getContentResolver().openInputStream(mIconUri));
-
+                    if ((stream = openInputStreamByScheme(mIconUri)) == null) {
+                        Log.w(TAG, "Unable to open: " + mIconUri);
+                        return null;
+                    }
                     // Query art size.
                     BitmapFactory.Options options = new BitmapFactory.Options();
                     options.inJustDecodeBounds = true;
@@ -992,8 +1000,10 @@
                     } catch (IOException e) {
                         // Failed to rewind the stream, try to reopen it.
                         stream.close();
-                        stream = new BufferedInputStream(mContext.getContentResolver()
-                                .openInputStream(mIconUri));
+                        if ((stream = openInputStreamByScheme(mIconUri)) == null) {
+                            Log.w(TAG, "Unable to open: " + mIconUri);
+                            return null;
+                        }
                     }
                     // Calculate required size to decode the art and possibly resize it.
                     options.inJustDecodeBounds = false;
@@ -1041,5 +1051,36 @@
                 updateLayoutHeight();
             }
         }
+
+        /**
+         * Returns whether a new art image is different from an original art image. Compares
+         * Bitmap objects first, and then compares URIs only if bitmap is unchanged with
+         * a null value.
+         */
+        private boolean isIconChanged() {
+            if (mIconBitmap != mArtIconBitmap) {
+                return true;
+            } else if (mIconBitmap == null && !uriEquals(mIconUri, mArtIconUri)) {
+                return true;
+            }
+            return false;
+        }
+
+        private InputStream openInputStreamByScheme(Uri uri) throws IOException {
+            String scheme = uri.getScheme().toLowerCase();
+            InputStream stream = null;
+            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
+                    || ContentResolver.SCHEME_CONTENT.equals(scheme)
+                    || ContentResolver.SCHEME_FILE.equals(scheme)) {
+                stream = mContext.getContentResolver().openInputStream(uri);
+            } else {
+                URL url = new URL(uri.toString());
+                URLConnection conn = url.openConnection();
+                conn.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS);
+                conn.setReadTimeout(CONNECTION_TIMEOUT_MILLIS);
+                stream = conn.getInputStream();
+            }
+            return (stream == null) ? null : new BufferedInputStream(stream);
+        }
     }
 }
diff --git a/v7/recyclerview/jvm-tests/NO_DOCS b/v7/recyclerview/jvm-tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/v7/recyclerview/jvm-tests/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index 57684f8..c956a2e 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -4961,7 +4961,7 @@
             final int cachedCount = mCachedViews.size();
             for (int i = 0; i < cachedCount; i++) {
                 final ViewHolder holder = mCachedViews.get(i);
-                if (holder != null && holder.getLayoutPosition() >= insertedAt) {
+                if (holder != null && holder.mPosition >= insertedAt) {
                     if (DEBUG) {
                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " +
                                 holder + " now at position " + (holder.mPosition + count));
@@ -4983,14 +4983,14 @@
             for (int i = cachedCount - 1; i >= 0; i--) {
                 final ViewHolder holder = mCachedViews.get(i);
                 if (holder != null) {
-                    if (holder.getLayoutPosition() >= removedEnd) {
+                    if (holder.mPosition >= removedEnd) {
                         if (DEBUG) {
                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
                                     " holder " + holder + " now at position " +
                                     (holder.mPosition - count));
                         }
                         holder.offsetPosition(-count, applyToPreLayout);
-                    } else if (holder.getLayoutPosition() >= removedFrom) {
+                    } else if (holder.mPosition >= removedFrom) {
                         // Item for this view was removed. Dump it from the cache.
                         holder.addFlags(ViewHolder.FLAG_REMOVED);
                         recycleCachedViewAt(i);
diff --git a/v7/recyclerview/tests/NO_DOCS b/v7/recyclerview/tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/v7/recyclerview/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
index 06dcbb0..917680b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
@@ -28,6 +28,8 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.UUID;
 
 public class RecyclerViewBasicTest extends AndroidTestCase {
@@ -170,6 +172,49 @@
                 adapterNew, null);
     }
 
+    public void testRecyclerOffsetsOnMove() {
+        MockLayoutManager  layoutManager = new MockLayoutManager();
+        final List<RecyclerView.ViewHolder> recycledVhs = new ArrayList<>();
+        mRecyclerView.setLayoutManager(layoutManager);
+        MockAdapter adapter = new MockAdapter(100) {
+            @Override
+            public void onViewRecycled(RecyclerView.ViewHolder holder) {
+                super.onViewRecycled(holder);
+                recycledVhs.add(holder);
+            }
+        };
+        MockViewHolder mvh = new MockViewHolder(new TextView(getContext()));
+        mRecyclerView.setAdapter(adapter);
+        adapter.bindViewHolder(mvh, 20);
+        mRecyclerView.mRecycler.mCachedViews.add(mvh);
+        mRecyclerView.offsetPositionRecordsForRemove(10, 9, false);
+
+        mRecyclerView.offsetPositionRecordsForRemove(11, 1, false);
+        assertEquals(1, recycledVhs.size());
+        assertSame(mvh, recycledVhs.get(0));
+    }
+
+    public void testRecyclerOffsetsOnAdd() {
+        MockLayoutManager  layoutManager = new MockLayoutManager();
+        final List<RecyclerView.ViewHolder> recycledVhs = new ArrayList<>();
+        mRecyclerView.setLayoutManager(layoutManager);
+        MockAdapter adapter = new MockAdapter(100) {
+            @Override
+            public void onViewRecycled(RecyclerView.ViewHolder holder) {
+                super.onViewRecycled(holder);
+                recycledVhs.add(holder);
+            }
+        };
+        MockViewHolder mvh = new MockViewHolder(new TextView(getContext()));
+        mRecyclerView.setAdapter(adapter);
+        adapter.bindViewHolder(mvh, 20);
+        mRecyclerView.mRecycler.mCachedViews.add(mvh);
+        mRecyclerView.offsetPositionRecordsForRemove(10, 9, false);
+
+        mRecyclerView.offsetPositionRecordsForInsert(15, 10);
+        assertEquals(11, mvh.mPosition);
+    }
+
     public void testSavedStateWithStatelessLayoutManager() throws InterruptedException {
         mRecyclerView.setLayoutManager(new MockLayoutManager() {
             @Override