Merge "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/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/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..9b27580 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;
@@ -90,6 +91,17 @@
         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/src/android/support/v17/leanback/transition/TransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
index 9420154..156ad97 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
@@ -103,6 +103,8 @@
 
         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 +148,8 @@
 
         public Object loadTransition(Context context, int resId);
 
+        public void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject);
+
         public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup);
     }
 
@@ -238,6 +242,11 @@
         }
 
         @Override
+        public Object createFadeAndShortSlide(int edge, float distance) {
+            return new TransitionStub();
+        }
+
+        @Override
         public Object createSlide(int slideEdge) {
             return new TransitionStub();
         }
@@ -360,6 +369,10 @@
         }
 
         @Override
+        public void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
+        }
+
+        @Override
         public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
         }
     }
@@ -552,6 +565,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);
         }
@@ -780,6 +803,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/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..cfd6406 100644
--- a/v4/tests/AndroidManifest.xml
+++ b/v4/tests/AndroidManifest.xml
@@ -22,6 +22,11 @@
             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"/>
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/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/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/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