Merge remote-tracking branch 'goog/mirror-m-wireless-internal-release'
diff --git a/annotations/src/android/support/annotation/CallSuper.java b/annotations/src/android/support/annotation/CallSuper.java
new file mode 100644
index 0000000..7180417
--- /dev/null
+++ b/annotations/src/android/support/annotation/CallSuper.java
@@ -0,0 +1,36 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+/**
+ * Denotes that any overriding methods should invoke this method as well.
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  &#64;CallSuper
+ *  public abstract void onFocusLost();
+ * }</pre>
+ */
+@Retention(CLASS)
+@Target({METHOD})
+public @interface CallSuper {
+}
\ No newline at end of file
diff --git a/annotations/src/android/support/annotation/CheckResult.java b/annotations/src/android/support/annotation/CheckResult.java
new file mode 100644
index 0000000..d527edc
--- /dev/null
+++ b/annotations/src/android/support/annotation/CheckResult.java
@@ -0,0 +1,56 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+/**
+ * Denotes that the annotated method returns a result that it typically is
+ * an error to ignore. This is usually used for methods that have no side effect,
+ * so calling it without actually looking at the result usually means the developer
+ * has misunderstood what the method does.
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  public &#64;CheckResult String trim(String s) { return s.trim(); }
+ *  ...
+ *  s.trim(); // this is probably an error
+ *  s = s.trim(); // ok
+ * }</pre>
+ */
+@Retention(CLASS)
+@Target({METHOD})
+public @interface CheckResult {
+    /** Defines the name of the suggested method to use instead, if applicable (using
+     * the same signature format as javadoc.) If there is more than one possibility,
+     * list them all separated by commas.
+     * <p>
+     * For example, ProcessBuilder has a method named {@code redirectErrorStream()}
+     * which sounds like it might redirect the error stream. It does not. It's just
+     * a getter which returns whether the process builder will redirect the error stream,
+     * and to actually set it, you must call {@code redirectErrorStream(boolean)}.
+     * In that case, the method should be defined like this:
+     * <pre>
+     *  &#64;CheckResult(suggest="#redirectErrorStream(boolean)")
+     *  public boolean redirectErrorStream() { ... }
+     * </pre>
+     */
+    String suggest() default "";
+}
\ No newline at end of file
diff --git a/annotations/src/android/support/annotation/ColorInt.java b/annotations/src/android/support/annotation/ColorInt.java
new file mode 100644
index 0000000..9b3cb7e
--- /dev/null
+++ b/annotations/src/android/support/annotation/ColorInt.java
@@ -0,0 +1,40 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+/**
+ * Denotes that the annotated element represents a packed color
+ * int, {@code AARRGGBB}. If applied to an int array, every element
+ * in the array represents a color integer.
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  public abstract void setTextColor(&#64;ColorInt int color);
+ * }</pre>
+ */
+@Retention(CLASS)
+@Target({PARAMETER,METHOD,LOCAL_VARIABLE,FIELD})
+public @interface ColorInt {
+}
\ No newline at end of file
diff --git a/annotations/src/android/support/annotation/FloatRange.java b/annotations/src/android/support/annotation/FloatRange.java
new file mode 100644
index 0000000..130d834
--- /dev/null
+++ b/annotations/src/android/support/annotation/FloatRange.java
@@ -0,0 +1,53 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+/**
+ * Denotes that the annotated element should be a float or double in the given range
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  &#64;FloatRange(from=0.0,to=1.0)
+ *  public float getAlpha() {
+ *      ...
+ *  }
+ * }</pre>
+ */
+@Retention(CLASS)
+@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE})
+public @interface FloatRange {
+    /** Smallest value. Whether it is inclusive or not is determined
+     * by {@link #fromInclusive} */
+    double from() default Double.NEGATIVE_INFINITY;
+    /** Largest value. Whether it is inclusive or not is determined
+     * by {@link #toInclusive} */
+    double to() default Double.POSITIVE_INFINITY;
+
+    /** Whether the from value is included in the range */
+    boolean fromInclusive() default true;
+
+    /** Whether the to value is included in the range */
+    boolean toInclusive() default true;
+}
\ No newline at end of file
diff --git a/annotations/src/android/support/annotation/IntDef.java b/annotations/src/android/support/annotation/IntDef.java
index ce49b6e..57a782b 100644
--- a/annotations/src/android/support/annotation/IntDef.java
+++ b/annotations/src/android/support/annotation/IntDef.java
@@ -34,7 +34,7 @@
  * <p>
  * Example:
  * <pre>{@code
- *  &#64;Retention(CLASS)
+ *  &#64;Retention(SOURCE)
  *  &#64;IntDef(&#123;NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS&#125;)
  *  public &#64;interface NavigationMode &#123;&#125;
  *  public static final int NAVIGATION_MODE_STANDARD = 0;
diff --git a/annotations/src/android/support/annotation/IntRange.java b/annotations/src/android/support/annotation/IntRange.java
new file mode 100644
index 0000000..6011276
--- /dev/null
+++ b/annotations/src/android/support/annotation/IntRange.java
@@ -0,0 +1,45 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+/**
+ * Denotes that the annotated element should be an int or long in the given range
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  &#64;IntRange(from=0,to=255)
+ *  public int getAlpha() {
+ *      ...
+ *  }
+ * }</pre>
+ */
+@Retention(CLASS)
+@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE})
+public @interface IntRange {
+    /** Smallest value, inclusive */
+    long from() default Long.MIN_VALUE;
+    /** Largest value, inclusive */
+    long to() default Long.MAX_VALUE;
+}
\ No newline at end of file
diff --git a/annotations/src/android/support/annotation/Keep.java b/annotations/src/android/support/annotation/Keep.java
new file mode 100644
index 0000000..de1c9e5
--- /dev/null
+++ b/annotations/src/android/support/annotation/Keep.java
@@ -0,0 +1,46 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+/**
+ * Denotes that the annotated element should not be removed when
+ * the code is minified at build time. This is typically used
+ * on methods and classes that are accessed only via reflection
+ * so a compiler may think that the code is unused.
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  &#64;Keep
+ *  public void foo() {
+ *      ...
+ *  }
+ * }</pre>
+ */
+@Retention(CLASS)
+@Target({PACKAGE,TYPE,ANNOTATION_TYPE,CONSTRUCTOR,METHOD,FIELD})
+public @interface Keep {
+}
diff --git a/annotations/src/android/support/annotation/Size.java b/annotations/src/android/support/annotation/Size.java
new file mode 100644
index 0000000..306fdf3
--- /dev/null
+++ b/annotations/src/android/support/annotation/Size.java
@@ -0,0 +1,50 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+/**
+ * Denotes that the annotated element should have a given size or length.
+ * Note that "-1" means "unset". Typically used with a parameter or
+ * return value of type array or collection.
+ * <p>
+ * Example:
+ * <pre>{@code
+ *  public void getLocationInWindow(&#64;Size(2) int[] location) {
+ *      ...
+ *  }
+ * }</pre>
+ */
+@Retention(CLASS)
+@Target({PARAMETER,LOCAL_VARIABLE,METHOD,FIELD})
+public @interface Size {
+    /** An exact size (or -1 if not specified) */
+    long value() default -1;
+    /** A minimum size, inclusive */
+    long min() default Long.MIN_VALUE;
+    /** A maximum size, inclusive */
+    long max() default Long.MAX_VALUE;
+    /** The size must be a multiple of this factor */
+    long multiple() default 1;
+}
\ No newline at end of file
diff --git a/design/Android.mk b/design/Android.mk
new file mode 100644
index 0000000..7db921b
--- /dev/null
+++ b/design/Android.mk
@@ -0,0 +1,89 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Build the resources using the current SDK version.
+# We do this here because the final static library must be compiled with an older
+# SDK version than the resources.  The resources library and the R class that it
+# contains will not be linked into the final static library.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-design-res
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
+    frameworks/support/v7/appcompat/res
+LOCAL_AAPT_FLAGS := \
+    --auto-add-overlay \
+    --extra-packages android.support.v7.appcompat
+LOCAL_JAR_EXCLUDE_FILES := none
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# A helper sub-library to resolve cyclic dependencies between src and the platform dependent
+# implementations
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-design-base
+LOCAL_SDK_VERSION := 7
+LOCAL_SRC_FILES := $(call all-java-files-under, base)
+LOCAL_JAVA_LIBRARIES := android-support-design-res \
+    android-support-v4 \
+    android-support-v7-appcompat
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# A helper sub-library that makes direct use of Eclair MR1 APIs
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-design-eclair-mr1
+LOCAL_SDK_VERSION := 7
+LOCAL_SRC_FILES := $(call all-java-files-under, eclair-mr1)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-base
+LOCAL_JAVA_LIBRARIES := android-support-design-res \
+    android-support-v4 \
+    android-support-v7-appcompat
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# A helper sub-library that makes direct use of Honeycomb APIs
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-design-honeycomb
+LOCAL_SDK_VERSION := 11
+LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-eclair-mr1
+LOCAL_JAVA_LIBRARIES := android-support-design-res \
+    android-support-v4 \
+    android-support-v7-appcompat
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# A helper sub-library that makes direct use of Lollipop APIs
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-design-lollipop
+LOCAL_SDK_VERSION := 21
+LOCAL_SRC_FILES := $(call all-java-files-under, lollipop)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-honeycomb
+LOCAL_JAVA_LIBRARIES := android-support-design-res \
+    android-support-v4 \
+    android-support-v7-appcompat
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Here is the final static library that apps can link against.
+# The R class is automatically excluded from the generated library.
+# Applications that use this library must specify LOCAL_RESOURCE_DIR
+# in their makefiles to include the resources in their package.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-design
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-lollipop
+LOCAL_JAVA_LIBRARIES := android-support-design-res \
+    android-support-v4 \
+    android-support-v7-appcompat
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/design/AndroidManifest.xml b/design/AndroidManifest.xml
new file mode 100644
index 0000000..963f89b
--- /dev/null
+++ b/design/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.design">
+    <uses-sdk android:minSdkVersion="7"/>
+    <application />
+</manifest>
diff --git a/design/base/android/support/design/widget/AnimationUtils.java b/design/base/android/support/design/widget/AnimationUtils.java
new file mode 100644
index 0000000..ad01cd5
--- /dev/null
+++ b/design/base/android/support/design/widget/AnimationUtils.java
@@ -0,0 +1,35 @@
+/*
+ * 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.design.widget;
+
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+class AnimationUtils {
+
+    static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+    static final Interpolator FAST_OUT_SLOW_IN_INTERPOLATOR = new FastOutSlowInInterpolator();
+
+    /**
+     * Linear interpolation between {@code startValue} and {@code endValue} by {@code fraction}.
+     */
+    static float lerp(float startValue, float endValue, float fraction) {
+        return startValue + (fraction * (endValue - startValue));
+    }
+
+}
diff --git a/design/base/android/support/design/widget/FloatingActionButtonImpl.java b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
new file mode 100644
index 0000000..4118d96
--- /dev/null
+++ b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
@@ -0,0 +1,57 @@
+/*
+ * 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.design.widget;
+
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+abstract class FloatingActionButtonImpl {
+
+    static final int[] PRESSED_ENABLED_STATE_SET = {android.R.attr.state_pressed,
+            android.R.attr.state_enabled};
+    static final int[] FOCUSED_ENABLED_STATE_SET = {android.R.attr.state_focused,
+            android.R.attr.state_enabled};
+    static final int[] EMPTY_STATE_SET = new int[0];
+
+    final View mView;
+    final ShadowViewDelegate mShadowViewDelegate;
+
+    FloatingActionButtonImpl(View view, ShadowViewDelegate shadowViewDelegate) {
+        mView = view;
+        mShadowViewDelegate = shadowViewDelegate;
+    }
+
+    abstract void setBackgroundDrawable(Drawable originalBackground, ColorStateList backgroundTint,
+            PorterDuff.Mode backgroundTintMode, int rippleColor);
+
+    abstract void setBackgroundTintList(ColorStateList tint);
+
+    abstract void setBackgroundTintMode(PorterDuff.Mode tintMode);
+
+    abstract void setRippleColor(int rippleColor);
+
+    abstract void setElevation(float elevation);
+
+    abstract void setPressedTranslationZ(float translationZ);
+
+    abstract void onDrawableStateChanged(int[] state);
+
+    abstract void jumpDrawableToCurrentState();
+
+}
diff --git a/design/base/android/support/design/widget/ShadowDrawableWrapper.java b/design/base/android/support/design/widget/ShadowDrawableWrapper.java
new file mode 100644
index 0000000..d8144d4
--- /dev/null
+++ b/design/base/android/support/design/widget/ShadowDrawableWrapper.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2014 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.design.widget;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.support.design.R;
+import android.support.v7.graphics.drawable.DrawableWrapper;
+
+/**
+ * A {@link android.graphics.drawable.Drawable} which wraps another drawable and
+ * draws a shadow around it.
+ */
+class ShadowDrawableWrapper extends DrawableWrapper {
+    // used to calculate content padding
+    static final double COS_45 = Math.cos(Math.toRadians(45));
+
+    static final float SHADOW_MULTIPLIER = 1.5f;
+
+    static final float SHADOW_TOP_SCALE = 0.25f;
+    static final float SHADOW_HORIZ_SCALE = 0.5f;
+    static final float SHADOW_BOTTOM_SCALE = 1f;
+
+    final Paint mCornerShadowPaint;
+    final Paint mEdgeShadowPaint;
+
+    final RectF mContentBounds;
+
+    float mCornerRadius;
+
+    Path mCornerShadowPath;
+
+    // updated value with inset
+    float mMaxShadowSize;
+    // actual value set by developer
+    float mRawMaxShadowSize;
+
+    // multiplied value to account for shadow offset
+    float mShadowSize;
+    // actual value set by developer
+    float mRawShadowSize;
+
+    private boolean mDirty = true;
+
+    private final int mShadowStartColor;
+    private final int mShadowMiddleColor;
+    private final int mShadowEndColor;
+
+    private boolean mAddPaddingForCorners = true;
+
+    /**
+     * If shadow size is set to a value above max shadow, we print a warning
+     */
+    private boolean mPrintedShadowClipWarning = false;
+
+    public ShadowDrawableWrapper(Resources resources, Drawable content, float radius,
+            float shadowSize, float maxShadowSize) {
+        super(content);
+
+        mShadowStartColor = resources.getColor(R.color.shadow_start_color);
+        mShadowMiddleColor = resources.getColor(R.color.shadow_mid_color);
+        mShadowEndColor = resources.getColor(R.color.shadow_end_color);
+
+        mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+        mCornerShadowPaint.setStyle(Paint.Style.FILL);
+        mCornerRadius = Math.round(radius);
+        mContentBounds = new RectF();
+        mEdgeShadowPaint = new Paint(mCornerShadowPaint);
+        mEdgeShadowPaint.setAntiAlias(false);
+        setShadowSize(shadowSize, maxShadowSize);
+    }
+
+    /**
+     * Casts the value to an even integer.
+     */
+    private static int toEven(float value) {
+        int i = Math.round(value);
+        return (i % 2 == 1) ? i - 1 : i;
+    }
+
+    public void setAddPaddingForCorners(boolean addPaddingForCorners) {
+        mAddPaddingForCorners = addPaddingForCorners;
+        invalidateSelf();
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        super.setAlpha(alpha);
+        mCornerShadowPaint.setAlpha(alpha);
+        mEdgeShadowPaint.setAlpha(alpha);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        mDirty = true;
+    }
+
+    void setShadowSize(float shadowSize, float maxShadowSize) {
+        if (shadowSize < 0 || maxShadowSize < 0) {
+            throw new IllegalArgumentException("invalid shadow size");
+        }
+        shadowSize = toEven(shadowSize);
+        maxShadowSize = toEven(maxShadowSize);
+        if (shadowSize > maxShadowSize) {
+            shadowSize = maxShadowSize;
+            if (!mPrintedShadowClipWarning) {
+                mPrintedShadowClipWarning = true;
+            }
+        }
+        if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) {
+            return;
+        }
+        mRawShadowSize = shadowSize;
+        mRawMaxShadowSize = maxShadowSize;
+        mShadowSize = Math.round(shadowSize * SHADOW_MULTIPLIER);
+        mMaxShadowSize = maxShadowSize;
+        mDirty = true;
+        invalidateSelf();
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius,
+                mAddPaddingForCorners));
+        int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius,
+                mAddPaddingForCorners));
+        padding.set(hOffset, vOffset, hOffset, vOffset);
+        return true;
+    }
+
+    public static float calculateVerticalPadding(float maxShadowSize, float cornerRadius,
+            boolean addPaddingForCorners) {
+        if (addPaddingForCorners) {
+            return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius);
+        } else {
+            return maxShadowSize * SHADOW_MULTIPLIER;
+        }
+    }
+
+    public static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,
+            boolean addPaddingForCorners) {
+        if (addPaddingForCorners) {
+            return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);
+        } else {
+            return maxShadowSize;
+        }
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    public void setCornerRadius(float radius) {
+        radius = Math.round(radius);
+        if (mCornerRadius == radius) {
+            return;
+        }
+        mCornerRadius = radius;
+        mDirty = true;
+        invalidateSelf();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mDirty) {
+            buildComponents(getBounds());
+            mDirty = false;
+        }
+        drawShadow(canvas);
+
+        super.draw(canvas);
+    }
+
+    private void drawShadow(Canvas canvas) {
+        final float edgeShadowTop = -mCornerRadius - mShadowSize;
+        final float shadowOffset = mCornerRadius;
+        final boolean drawHorizontalEdges = mContentBounds.width() - 2 * shadowOffset > 0;
+        final boolean drawVerticalEdges = mContentBounds.height() - 2 * shadowOffset > 0;
+
+        final float shadowOffsetTop = mRawShadowSize - (mRawShadowSize * SHADOW_TOP_SCALE);
+        final float shadowOffsetHorizontal = mRawShadowSize - (mRawShadowSize * SHADOW_HORIZ_SCALE);
+        final float shadowOffsetBottom = mRawShadowSize - (mRawShadowSize * SHADOW_BOTTOM_SCALE);
+
+        final float shadowScaleHorizontal = shadowOffset / (shadowOffset + shadowOffsetHorizontal);
+        final float shadowScaleTop = shadowOffset / (shadowOffset + shadowOffsetTop);
+        final float shadowScaleBottom = shadowOffset / (shadowOffset + shadowOffsetBottom);
+
+        // LT
+        int saved = canvas.save();
+        canvas.translate(mContentBounds.left + shadowOffset, mContentBounds.top + shadowOffset);
+        canvas.scale(shadowScaleHorizontal, shadowScaleTop);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawHorizontalEdges) {
+            // TE
+            canvas.scale(1f / shadowScaleHorizontal, 1f);
+            canvas.drawRect(0, edgeShadowTop,
+                    mContentBounds.width() - 2 * shadowOffset, -mCornerRadius,
+                    mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+        // RB
+        saved = canvas.save();
+        canvas.translate(mContentBounds.right - shadowOffset, mContentBounds.bottom - shadowOffset);
+        canvas.scale(shadowScaleHorizontal, shadowScaleBottom);
+        canvas.rotate(180f);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawHorizontalEdges) {
+            // BE
+            canvas.scale(1f / shadowScaleHorizontal, 1f);
+            canvas.drawRect(0, edgeShadowTop,
+                    mContentBounds.width() - 2 * shadowOffset, -mCornerRadius + mShadowSize,
+                    mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+        // LB
+        saved = canvas.save();
+        canvas.translate(mContentBounds.left + shadowOffset, mContentBounds.bottom - shadowOffset);
+        canvas.scale(shadowScaleHorizontal, shadowScaleBottom);
+        canvas.rotate(270f);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawVerticalEdges) {
+            // LE
+            canvas.scale(1f / shadowScaleBottom, 1f);
+            canvas.drawRect(0, edgeShadowTop,
+                    mContentBounds.height() - 2 * shadowOffset, -mCornerRadius, mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+        // RT
+        saved = canvas.save();
+        canvas.translate(mContentBounds.right - shadowOffset, mContentBounds.top + shadowOffset);
+        canvas.scale(shadowScaleHorizontal, shadowScaleTop);
+        canvas.rotate(90f);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawVerticalEdges) {
+            // RE
+            canvas.scale(1f / shadowScaleTop, 1f);
+            canvas.drawRect(0, edgeShadowTop,
+                    mContentBounds.height() - 2 * shadowOffset, -mCornerRadius, mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+    }
+
+    private void buildShadowCorners() {
+        RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
+        RectF outerBounds = new RectF(innerBounds);
+        outerBounds.inset(-mShadowSize, -mShadowSize);
+
+        if (mCornerShadowPath == null) {
+            mCornerShadowPath = new Path();
+        } else {
+            mCornerShadowPath.reset();
+        }
+        mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
+        mCornerShadowPath.moveTo(-mCornerRadius, 0);
+        mCornerShadowPath.rLineTo(-mShadowSize, 0);
+        // outer arc
+        mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
+        // inner arc
+        mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
+        mCornerShadowPath.close();
+
+        float shadowRadius = -outerBounds.top;
+        if (shadowRadius > 0f) {
+            float startRatio = mCornerRadius / shadowRadius;
+            float midRatio = startRatio + ((1f - startRatio) / 2f);
+            mCornerShadowPaint.setShader(new RadialGradient(0, 0, shadowRadius,
+                    new int[]{0, mShadowStartColor, mShadowMiddleColor, mShadowEndColor},
+                    new float[]{0f, startRatio, midRatio, 1f},
+                    Shader.TileMode.CLAMP));
+        }
+
+        // we offset the content shadowSize/2 pixels up to make it more realistic.
+        // this is why edge shadow shader has some extra space
+        // When drawing bottom edge shadow, we use that extra space.
+        mEdgeShadowPaint.setShader(new LinearGradient(0, innerBounds.top, 0, outerBounds.top,
+                new int[]{mShadowStartColor, mShadowMiddleColor, mShadowEndColor},
+                new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP));
+        mEdgeShadowPaint.setAntiAlias(false);
+    }
+
+    private void buildComponents(Rect bounds) {
+        // Card is offset SHADOW_MULTIPLIER * maxShadowSize to account for the shadow shift.
+        // We could have different top-bottom offsets to avoid extra gap above but in that case
+        // center aligning Views inside the CardView would be problematic.
+        final float verticalOffset = mRawMaxShadowSize * SHADOW_MULTIPLIER;
+        mContentBounds.set(bounds.left + mRawMaxShadowSize, bounds.top + verticalOffset,
+                bounds.right - mRawMaxShadowSize, bounds.bottom - verticalOffset);
+
+        getWrappedDrawable().setBounds((int) mContentBounds.left, (int) mContentBounds.top,
+                (int) mContentBounds.right, (int) mContentBounds.bottom);
+
+        buildShadowCorners();
+    }
+
+    public float getCornerRadius() {
+        return mCornerRadius;
+    }
+
+    public void setShadowSize(float size) {
+        setShadowSize(size, mRawMaxShadowSize);
+    }
+
+    public void setMaxShadowSize(float size) {
+        setShadowSize(mRawShadowSize, size);
+    }
+
+    public float getShadowSize() {
+        return mRawShadowSize;
+    }
+
+    public float getMaxShadowSize() {
+        return mRawMaxShadowSize;
+    }
+
+    public float getMinWidth() {
+        final float content = 2 *
+                Math.max(mRawMaxShadowSize, mCornerRadius + mRawMaxShadowSize / 2);
+        return content + mRawMaxShadowSize * 2;
+    }
+
+    public float getMinHeight() {
+        final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius
+                + mRawMaxShadowSize * SHADOW_MULTIPLIER / 2);
+        return content + (mRawMaxShadowSize * SHADOW_MULTIPLIER) * 2;
+    }
+}
diff --git a/design/base/android/support/design/widget/ShadowViewDelegate.java b/design/base/android/support/design/widget/ShadowViewDelegate.java
new file mode 100644
index 0000000..9a395e6
--- /dev/null
+++ b/design/base/android/support/design/widget/ShadowViewDelegate.java
@@ -0,0 +1,25 @@
+/*
+ * 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.design.widget;
+
+import android.graphics.drawable.Drawable;
+
+interface ShadowViewDelegate {
+    float getRadius();
+    void setShadowPadding(int left, int top, int right, int bottom);
+    void setBackgroundDrawable(Drawable background);
+}
diff --git a/design/base/android/support/design/widget/StateListAnimator.java b/design/base/android/support/design/widget/StateListAnimator.java
new file mode 100644
index 0000000..c937b0b
--- /dev/null
+++ b/design/base/android/support/design/widget/StateListAnimator.java
@@ -0,0 +1,189 @@
+/*
+ * 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.design.widget;
+
+import android.util.StateSet;
+import android.view.View;
+import android.view.animation.Animation;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+final class StateListAnimator {
+
+    private final ArrayList<Tuple> mTuples = new ArrayList<>();
+
+    private Tuple mLastMatch = null;
+    private Animation mRunningAnimation = null;
+    private WeakReference<View> mViewRef;
+
+    private Animation.AnimationListener mAnimationListener = new Animation.AnimationListener() {
+        @Override
+        public void onAnimationEnd(Animation animation) {
+            if (mRunningAnimation == animation) {
+                mRunningAnimation = null;
+            }
+        }
+
+        @Override
+        public void onAnimationStart(Animation animation) {
+            // no-op
+        }
+
+        @Override
+        public void onAnimationRepeat(Animation animation) {
+            // no-op
+        }
+    };
+
+    /**
+     * Associates the given Animation with the provided drawable state specs so that it will be run
+     * when the View's drawable state matches the specs.
+     *
+     * @param specs    The drawable state specs to match against
+     * @param animation The Animation to run when the specs match
+     */
+    public void addState(int[] specs, Animation animation) {
+        Tuple tuple = new Tuple(specs, animation);
+        animation.setAnimationListener(mAnimationListener);
+        mTuples.add(tuple);
+    }
+
+    /**
+     * Returns the current {@link Animation} which is started because of a state
+     * change.
+     *
+     * @return The currently running Animation or null if no Animation is running
+     */
+    Animation getRunningAnimation() {
+        return mRunningAnimation;
+    }
+
+
+    View getTarget() {
+        return mViewRef == null ? null : mViewRef.get();
+    }
+
+    void setTarget(View view) {
+        final View current = getTarget();
+        if (current == view) {
+            return;
+        }
+        if (current != null) {
+            clearTarget();
+        }
+        if (view != null) {
+            mViewRef = new WeakReference<>(view);
+        }
+    }
+
+    private void clearTarget() {
+        final View view = getTarget();
+        final int size = mTuples.size();
+        for (int i = 0; i < size; i++) {
+            Animation anim = mTuples.get(i).mAnimation;
+            if (view.getAnimation() == anim) {
+                view.clearAnimation();
+            }
+        }
+        mViewRef = null;
+        mLastMatch = null;
+        mRunningAnimation = null;
+    }
+
+    /**
+     * Called by View
+     */
+    void setState(int[] state) {
+        Tuple match = null;
+        final int count = mTuples.size();
+        for (int i = 0; i < count; i++) {
+            final Tuple tuple = mTuples.get(i);
+            if (StateSet.stateSetMatches(tuple.mSpecs, state)) {
+                match = tuple;
+                break;
+            }
+        }
+        if (match == mLastMatch) {
+            return;
+        }
+        if (mLastMatch != null) {
+            cancel();
+        }
+        mLastMatch = match;
+        if (match != null) {
+            start(match);
+        }
+    }
+
+    private void start(Tuple match) {
+        mRunningAnimation = match.mAnimation;
+
+        View view = getTarget();
+        if (view != null) {
+            view.startAnimation(mRunningAnimation);
+        }
+    }
+
+    private void cancel() {
+        if (mRunningAnimation != null) {
+            final View view = getTarget();
+            if (view != null && view.getAnimation() == mRunningAnimation) {
+                view.clearAnimation();
+            }
+            mRunningAnimation = null;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    ArrayList<Tuple> getTuples() {
+        return mTuples;
+    }
+
+    /**
+     * If there is an animation running for a recent state change, ends it. <p> This causes the
+     * animation to assign the end value(s) to the View.
+     */
+    public void jumpToCurrentState() {
+        if (mRunningAnimation != null) {
+            final View view = getTarget();
+            if (view != null && view.getAnimation() == mRunningAnimation) {
+                view.clearAnimation();
+            }
+        }
+    }
+
+    static class Tuple {
+        final int[] mSpecs;
+        final Animation mAnimation;
+
+        private Tuple(int[] specs, Animation Animation) {
+            mSpecs = specs;
+            mAnimation = Animation;
+        }
+
+        int[] getSpecs() {
+            return mSpecs;
+        }
+
+        Animation getAnimation() {
+            return mAnimation;
+        }
+    }
+}
\ No newline at end of file
diff --git a/design/build.gradle b/design/build.gradle
new file mode 100644
index 0000000..1740fce
--- /dev/null
+++ b/design/build.gradle
@@ -0,0 +1,36 @@
+apply plugin: 'android-library'
+
+archivesBaseName = 'design'
+
+dependencies {
+    compile project(':support-v4')
+    compile project(':support-appcompat-v7')
+}
+
+android {
+    compileSdkVersion 'current'
+
+    sourceSets {
+        main.manifest.srcFile 'AndroidManifest.xml'
+        main.java.srcDirs = ['base', 'eclair-mr1', 'honeycomb', 'lollipop', 'src']
+        main.res.srcDir 'res'
+        main.assets.srcDir 'assets'
+        main.resources.srcDir 'src'
+
+        // this moves src/instrumentTest to tests so all folders follow:
+        // tests/java, tests/res, tests/assets, ...
+        // This is a *reset* so it replaces the default paths
+        androidTest.setRoot('tests')
+        androidTest.java.srcDir 'tests/src'
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+    lintOptions {
+        // TODO: fix errors and reenable.
+        abortOnError false
+    }
+}
diff --git a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
new file mode 100644
index 0000000..a725dd8
--- /dev/null
+++ b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
@@ -0,0 +1,210 @@
+/*
+ * 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.design.widget;
+
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+class FloatingActionButtonEclairMr1 extends FloatingActionButtonImpl {
+
+    private Drawable mShapeDrawable;
+    private Drawable mRippleDrawable;
+
+    private float mElevation;
+    private float mPressedTranslationZ;
+    private int mAnimationDuration;
+
+    private StateListAnimator mStateListAnimator;
+
+    ShadowDrawableWrapper mShadowDrawable;
+
+    FloatingActionButtonEclairMr1(View view, ShadowViewDelegate shadowViewDelegate) {
+        super(view, shadowViewDelegate);
+
+        mAnimationDuration = view.getResources().getInteger(android.R.integer.config_shortAnimTime);
+
+        mStateListAnimator = new StateListAnimator();
+        mStateListAnimator.setTarget(view);
+
+        // Elevate with translationZ when pressed or focused
+        mStateListAnimator.addState(PRESSED_ENABLED_STATE_SET,
+                setupAnimation(new ElevateToTranslationZAnimation()));
+        mStateListAnimator.addState(FOCUSED_ENABLED_STATE_SET,
+                setupAnimation(new ElevateToTranslationZAnimation()));
+        // Reset back to elevation by default
+        mStateListAnimator.addState(EMPTY_STATE_SET,
+                setupAnimation(new ResetElevationAnimation()));
+    }
+
+    @Override
+    void setBackgroundDrawable(Drawable originalBackground, ColorStateList backgroundTint,
+            PorterDuff.Mode backgroundTintMode, int rippleColor) {
+        // First we need to tint the original background with the tint
+        mShapeDrawable = DrawableCompat.wrap(originalBackground);
+        DrawableCompat.setTintList(mShapeDrawable, backgroundTint);
+        if (backgroundTintMode != null) {
+            DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode);
+        }
+
+        // Now we created a mask Drawable which will be used for touch feedback.
+        // As we don't know the actual outline of mShapeDrawable, we'll just guess that it's a
+        // circle
+        GradientDrawable touchFeedbackShape = new GradientDrawable();
+        touchFeedbackShape.setShape(GradientDrawable.OVAL);
+        touchFeedbackShape.setColor(Color.WHITE);
+        touchFeedbackShape.setCornerRadius(mShadowViewDelegate.getRadius());
+
+        // We'll now wrap that touch feedback mask drawable with a ColorStateList
+        mRippleDrawable = DrawableCompat.wrap(touchFeedbackShape);
+        DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
+        DrawableCompat.setTintMode(mRippleDrawable, PorterDuff.Mode.MULTIPLY);
+
+        mShadowDrawable = new ShadowDrawableWrapper(
+                mView.getResources(),
+                new LayerDrawable(new Drawable[] {mShapeDrawable, mRippleDrawable}),
+                mShadowViewDelegate.getRadius(),
+                mElevation,
+                mElevation + mPressedTranslationZ);
+        mShadowDrawable.setAddPaddingForCorners(false);
+
+        mShadowViewDelegate.setBackgroundDrawable(mShadowDrawable);
+
+        updatePadding();
+    }
+
+    @Override
+    void setBackgroundTintList(ColorStateList tint) {
+        DrawableCompat.setTintList(mShapeDrawable, tint);
+    }
+
+    @Override
+    void setBackgroundTintMode(PorterDuff.Mode tintMode) {
+        DrawableCompat.setTintMode(mShapeDrawable, tintMode);
+    }
+
+    @Override
+    void setRippleColor(int rippleColor) {
+        DrawableCompat.setTint(mRippleDrawable, rippleColor);
+    }
+
+    @Override
+    void setElevation(float elevation) {
+        if (mElevation != elevation && mShadowDrawable != null) {
+            mShadowDrawable.setShadowSize(elevation, elevation + mPressedTranslationZ);
+            mElevation = elevation;
+            updatePadding();
+        }
+    }
+
+    @Override
+    void setPressedTranslationZ(float translationZ) {
+        if (mPressedTranslationZ != translationZ && mShadowDrawable != null) {
+            mPressedTranslationZ = translationZ;
+            mShadowDrawable.setMaxShadowSize(mElevation + translationZ);
+            updatePadding();
+        }
+    }
+
+    @Override
+    void onDrawableStateChanged(int[] state) {
+        mStateListAnimator.setState(state);
+    }
+
+    @Override
+    void jumpDrawableToCurrentState() {
+        mStateListAnimator.jumpToCurrentState();
+    }
+
+    private void updatePadding() {
+        Rect rect = new Rect();
+        mShadowDrawable.getPadding(rect);
+        mShadowViewDelegate.setShadowPadding(rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    private Animation setupAnimation(Animation animation) {
+        animation.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+        animation.setDuration(mAnimationDuration);
+        return animation;
+    }
+
+    private abstract class BaseShadowAnimation extends Animation {
+        private float mShadowSizeStart;
+        private float mShadowSizeDiff;
+
+        @Override
+        public void reset() {
+            super.reset();
+
+            mShadowSizeStart = mShadowDrawable.getShadowSize();
+            mShadowSizeDiff = getTargetShadowSize() - mShadowSizeStart;
+        }
+
+        @Override
+        protected void applyTransformation(float interpolatedTime, Transformation t) {
+            mShadowDrawable.setShadowSize(mShadowSizeStart + (mShadowSizeDiff * interpolatedTime));
+        }
+
+        /**
+         * @return the shadow size we want to animate to.
+         */
+        protected abstract float getTargetShadowSize();
+    }
+
+    private class ResetElevationAnimation extends BaseShadowAnimation {
+        @Override
+        protected float getTargetShadowSize() {
+            return mElevation;
+        }
+    }
+
+    private class ElevateToTranslationZAnimation extends BaseShadowAnimation {
+        @Override
+        protected float getTargetShadowSize() {
+            return mElevation + mPressedTranslationZ;
+        }
+    }
+
+    private static ColorStateList createColorStateList(int selectedColor) {
+        final int[][] states = new int[3][];
+        final int[] colors = new int[3];
+        int i = 0;
+
+        states[i] = FOCUSED_ENABLED_STATE_SET;
+        colors[i] = selectedColor;
+        i++;
+
+        states[i] = PRESSED_ENABLED_STATE_SET;
+        colors[i] = selectedColor;
+        i++;
+
+        // Default enabled state
+        states[i] = new int[0];
+        colors[i] = Color.TRANSPARENT;
+        i++;
+
+        return new ColorStateList(states, colors);
+    }
+}
\ No newline at end of file
diff --git a/design/honeycomb/android/support/design/widget/CoordinatorLayoutHoneycomb.java b/design/honeycomb/android/support/design/widget/CoordinatorLayoutHoneycomb.java
new file mode 100644
index 0000000..690874d
--- /dev/null
+++ b/design/honeycomb/android/support/design/widget/CoordinatorLayoutHoneycomb.java
@@ -0,0 +1,66 @@
+/*
+ * 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.design.widget;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+class CoordinatorLayoutHoneycomb {
+    private static final ThreadLocal<Matrix> sMatrix = new ThreadLocal<>();
+    private static final ThreadLocal<RectF> sRectF = new ThreadLocal<>();
+    private static final Matrix IDENTITY = new Matrix();
+
+    public static void offsetDescendantRect(ViewGroup group, View child, Rect rect) {
+        Matrix m = sMatrix.get();
+        if (m == null) {
+            m = new Matrix();
+            sMatrix.set(m);
+        } else {
+            m.set(IDENTITY);
+        }
+
+        offsetDescendantMatrix(group, child, m);
+
+        RectF rectF = sRectF.get();
+        if (rectF == null) {
+            rectF = new RectF();
+        }
+        rectF.set(rect);
+        m.mapRect(rectF);
+        rect.set((int) (rectF.left + 0.5f), (int) (rectF.top + 0.5f),
+                (int) (rectF.right + 0.5f), (int) (rectF.bottom + 0.5f));
+    }
+
+    static void offsetDescendantMatrix(ViewParent target, View view, Matrix m) {
+        final ViewParent parent = view.getParent();
+        if (parent instanceof View && parent != target) {
+            final View vp = (View) parent;
+            offsetDescendantMatrix(target, vp, m);
+            m.preTranslate(-vp.getScrollX(), -vp.getScrollY());
+        }
+
+        m.preTranslate(view.getX(), view.getY());
+
+        if (!view.getMatrix().isIdentity()) {
+            m.preConcat(view.getMatrix());
+        }
+    }
+}
diff --git a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
new file mode 100644
index 0000000..14f7989
--- /dev/null
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -0,0 +1,120 @@
+/*
+ * 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.design.widget;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.StateListAnimator;
+import android.annotation.TargetApi;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.Build;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+class FloatingActionButtonLollipop extends FloatingActionButtonImpl {
+
+    private Drawable mShapeDrawable;
+    private RippleDrawable mRippleDrawable;
+
+    private final Interpolator mInterpolator;
+
+    FloatingActionButtonLollipop(View view, ShadowViewDelegate shadowViewDelegate) {
+        super(view, shadowViewDelegate);
+
+        if (!view.isInEditMode()) {
+            mInterpolator = AnimationUtils.loadInterpolator(
+                    mView.getContext(), android.R.interpolator.fast_out_slow_in);
+        } else {
+            mInterpolator = null;
+        }
+    }
+
+    @Override
+    void setBackgroundDrawable(Drawable originalBackground, ColorStateList backgroundTint,
+            PorterDuff.Mode backgroundTintMode, int rippleColor) {
+        mShapeDrawable = DrawableCompat.wrap(originalBackground);
+
+        DrawableCompat.setTintList(mShapeDrawable, backgroundTint);
+        if (backgroundTintMode != null) {
+            DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode);
+        }
+
+        mRippleDrawable = new RippleDrawable(ColorStateList.valueOf(rippleColor),
+                mShapeDrawable, null);
+
+        mShadowViewDelegate.setBackgroundDrawable(mRippleDrawable);
+        mShadowViewDelegate.setShadowPadding(0, 0, 0, 0);
+    }
+
+    @Override
+    void setBackgroundTintList(ColorStateList tint) {
+        DrawableCompat.setTintList(mShapeDrawable, tint);
+    }
+
+    @Override
+    void setBackgroundTintMode(PorterDuff.Mode tintMode) {
+        DrawableCompat.setTintMode(mShapeDrawable, tintMode);
+    }
+
+    @Override
+    void setRippleColor(int rippleColor) {
+        DrawableCompat.setTint(mRippleDrawable, rippleColor);
+    }
+
+    @Override
+    public void setElevation(float elevation) {
+        ViewCompat.setElevation(mView, elevation);
+    }
+
+    @Override
+    void setPressedTranslationZ(float translationZ) {
+        StateListAnimator stateListAnimator = new StateListAnimator();
+
+        // Animate translationZ to our value when pressed or focused
+        stateListAnimator.addState(PRESSED_ENABLED_STATE_SET,
+                setupAnimator(ObjectAnimator.ofFloat(mView, "translationZ", translationZ)));
+        stateListAnimator.addState(FOCUSED_ENABLED_STATE_SET,
+                setupAnimator(ObjectAnimator.ofFloat(mView, "translationZ", translationZ)));
+        // Animate translationZ to 0 otherwise
+        stateListAnimator.addState(EMPTY_STATE_SET,
+                setupAnimator(ObjectAnimator.ofFloat(mView, "translationZ", 0f)));
+
+        mView.setStateListAnimator(stateListAnimator);
+    }
+
+    @Override
+    void onDrawableStateChanged(int[] state) {
+        // no-op
+    }
+
+    @Override
+    void jumpDrawableToCurrentState() {
+        // no-op
+    }
+
+    private Animator setupAnimator(Animator animator) {
+        animator.setInterpolator(mInterpolator);
+        return animator;
+    }
+}
diff --git a/design/res/anim/snackbar_in.xml b/design/res/anim/snackbar_in.xml
new file mode 100644
index 0000000..a40524c
--- /dev/null
+++ b/design/res/anim/snackbar_in.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+           android:fromYDelta="100%"
+           android:toYDelta="0"/>
diff --git a/design/res/anim/snackbar_out.xml b/design/res/anim/snackbar_out.xml
new file mode 100644
index 0000000..eb55cc0
--- /dev/null
+++ b/design/res/anim/snackbar_out.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+           android:fromYDelta="0"
+           android:toYDelta="100%"/>
\ No newline at end of file
diff --git a/design/res/drawable/fab_background.xml b/design/res/drawable/fab_background.xml
new file mode 100644
index 0000000..43afd5c
--- /dev/null
+++ b/design/res/drawable/fab_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+        android:shape="oval">
+    <solid android:color="@android:color/white" />
+</shape>
\ No newline at end of file
diff --git a/design/res/drawable/snackbar_background.xml b/design/res/drawable/snackbar_background.xml
new file mode 100644
index 0000000..739b516
--- /dev/null
+++ b/design/res/drawable/snackbar_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <corners android:radius="@dimen/snackbar_background_corner_radius"/>
+    <solid android:color="@color/snackbar_background_color"/>
+</shape>
\ No newline at end of file
diff --git a/design/res/layout-sw600dp/layout_snackbar.xml b/design/res/layout-sw600dp/layout_snackbar.xml
new file mode 100644
index 0000000..6605408
--- /dev/null
+++ b/design/res/layout-sw600dp/layout_snackbar.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+      class="android.support.design.widget.Snackbar$SnackbarLayout"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="bottom|center_vertical"
+      style="@style/Widget.Design.Snackbar" />
\ No newline at end of file
diff --git a/design/res/layout/design_navigation_item.xml b/design/res/layout/design_navigation_item.xml
new file mode 100644
index 0000000..6f86719
--- /dev/null
+++ b/design/res/layout/design_navigation_item.xml
@@ -0,0 +1,26 @@
+<?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.design.internal.NavigationMenuItemView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="?attr/listPreferredItemHeightSmall"
+        android:paddingLeft="?attr/listPreferredItemPaddingLeft"
+        android:paddingRight="?attr/listPreferredItemPaddingRight"
+        android:drawablePadding="@dimen/navigation_icon_padding"
+        android:gravity="center_vertical|start"
+        android:maxLines="1"
+        android:textAppearance="?attr/textAppearanceListItem"/>
diff --git a/design/res/layout/design_navigation_item_separator.xml b/design/res/layout/design_navigation_item_separator.xml
new file mode 100644
index 0000000..fb786aa
--- /dev/null
+++ b/design/res/layout/design_navigation_item_separator.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:paddingTop="@dimen/navigation_separator_vertical_padding"
+             android:paddingBottom="@dimen/navigation_separator_vertical_padding">
+
+    <View android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="?android:attr/listDivider"/>
+
+</FrameLayout>
diff --git a/design/res/layout/design_navigation_item_space.xml b/design/res/layout/design_navigation_item_space.xml
new file mode 100644
index 0000000..e8e7b2f
--- /dev/null
+++ b/design/res/layout/design_navigation_item_space.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="match_parent"
+      android:layout_height="16dp"
+      android:visibility="invisible" />
diff --git a/design/res/layout/design_navigation_item_subheader.xml b/design/res/layout/design_navigation_item_subheader.xml
new file mode 100644
index 0000000..9a1a810
--- /dev/null
+++ b/design/res/layout/design_navigation_item_subheader.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="match_parent"
+          android:layout_height="?attr/listPreferredItemHeightSmall"
+          android:gravity="center_vertical|start"
+          android:maxLines="1"
+          android:paddingLeft="?attr/listPreferredItemPaddingLeft"
+          android:paddingRight="?attr/listPreferredItemPaddingRight"
+          android:textAppearance="?attr/textAppearanceListItem"
+          android:textColor="?android:textColorSecondary"/>
diff --git a/design/res/layout/design_navigation_menu.xml b/design/res/layout/design_navigation_menu.xml
new file mode 100644
index 0000000..6c75423
--- /dev/null
+++ b/design/res/layout/design_navigation_menu.xml
@@ -0,0 +1,24 @@
+<?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.design.internal.NavigationMenuView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingTop="@dimen/navigation_padding_top_default"
+        android:clipToPadding="false"
+        android:divider="@null"
+        android:listSelector="?attr/selectableItemBackground"/>
diff --git a/design/res/layout/layout_snackbar.xml b/design/res/layout/layout_snackbar.xml
new file mode 100644
index 0000000..604aafc
--- /dev/null
+++ b/design/res/layout/layout_snackbar.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+      class="android.support.design.widget.Snackbar$SnackbarLayout"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_gravity="bottom"
+      style="@style/Widget.Design.Snackbar" />
\ No newline at end of file
diff --git a/design/res/layout/layout_snackbar_include.xml b/design/res/layout/layout_snackbar_include.xml
new file mode 100644
index 0000000..0cf2002
--- /dev/null
+++ b/design/res/layout/layout_snackbar_include.xml
@@ -0,0 +1,49 @@
+<?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.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <TextView
+            android:id="@+id/snackbar_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingTop="@dimen/snackbar_padding_vertical"
+            android:paddingBottom="@dimen/snackbar_padding_vertical"
+            android:paddingLeft="@dimen/snackbar_padding_horizontal"
+            android:paddingRight="@dimen/snackbar_padding_horizontal"
+            android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
+            android:maxLines="@integer/snackbar_text_max_lines"
+            android:layout_gravity="center_vertical|left|start"
+            android:ellipsize="end"/>
+
+    <TextView
+            android:id="@+id/snackbar_action"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/snackbar_extra_spacing_horizontal"
+            android:layout_marginStart="@dimen/snackbar_extra_spacing_horizontal"
+            android:layout_gravity="center_vertical|right|end"
+            android:background="?attr/selectableItemBackground"
+            android:paddingTop="@dimen/snackbar_padding_vertical"
+            android:paddingBottom="@dimen/snackbar_padding_vertical"
+            android:paddingLeft="@dimen/snackbar_padding_horizontal"
+            android:paddingRight="@dimen/snackbar_padding_horizontal"
+            android:visibility="gone"
+            android:textAppearance="@style/TextAppearance.Design.Snackbar.Action"/>
+
+</merge>
\ No newline at end of file
diff --git a/design/res/values-land/styles.xml b/design/res/values-land/styles.xml
new file mode 100644
index 0000000..622a5e3
--- /dev/null
+++ b/design/res/values-land/styles.xml
@@ -0,0 +1,25 @@
+<?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>
+
+    <style name="Widget.Design.TabLayout" parent="Base.Widget.Design.TabLayout">
+        <item name="tabGravity">center</item>
+        <item name="tabMode">fixed</item>
+    </style>
+
+</resources>
+
diff --git a/design/res/values-sw600dp/config.xml b/design/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..baac13b
--- /dev/null
+++ b/design/res/values-sw600dp/config.xml
@@ -0,0 +1,22 @@
+<?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>
+
+    <integer name="snackbar_text_max_lines">1</integer>
+
+</resources>
\ No newline at end of file
diff --git a/design/res/values-sw600dp/dimens.xml b/design/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..37c3ff5
--- /dev/null
+++ b/design/res/values-sw600dp/dimens.xml
@@ -0,0 +1,29 @@
+<?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>
+
+    <dimen name="tab_min_width">160dp</dimen>
+
+    <dimen name="snackbar_min_width">320dp</dimen>
+    <dimen name="snackbar_max_width">576dp</dimen>
+    <dimen name="snackbar_padding_vertical_2lines">@dimen/snackbar_padding_vertical</dimen>
+    <dimen name="snackbar_extra_spacing_horizontal">24dp</dimen>
+    <dimen name="snackbar_background_corner_radius">2dp</dimen>
+    <dimen name="snackbar_action_inline_max_width">0dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/design/res/values-sw600dp/styles.xml b/design/res/values-sw600dp/styles.xml
new file mode 100644
index 0000000..622a5e3
--- /dev/null
+++ b/design/res/values-sw600dp/styles.xml
@@ -0,0 +1,25 @@
+<?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>
+
+    <style name="Widget.Design.TabLayout" parent="Base.Widget.Design.TabLayout">
+        <item name="tabGravity">center</item>
+        <item name="tabMode">fixed</item>
+    </style>
+
+</resources>
+
diff --git a/design/res/values-v21/dimens.xml b/design/res/values-v21/dimens.xml
new file mode 100644
index 0000000..2a67937
--- /dev/null
+++ b/design/res/values-v21/dimens.xml
@@ -0,0 +1,19 @@
+<?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>
+    <dimen name="navigation_padding_top_default">24dp</dimen>
+</resources>
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
new file mode 100644
index 0000000..bf81b54
--- /dev/null
+++ b/design/res/values/attrs.xml
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+<resources>
+
+    <declare-styleable name="FloatingActionButton">
+        <!-- Background for the FloatingActionButton -->
+        <attr name="android:background" />
+        <attr name="backgroundTint" />
+        <attr name="backgroundTintMode" />
+
+        <!-- Ripple color for the FAB. -->
+        <attr name="rippleColor" format="color|reference" />
+        <!-- Size for the FAB. -->
+        <attr name="fabSize">
+            <enum name="normal" value="0" />
+            <enum name="mini" value="1" />
+        </attr>
+        <!-- Elevation value for the FAB -->
+        <attr name="elevation" />
+        <!-- TranslationZ value for the FAB when pressed-->
+        <attr name="pressedTranslationZ" format="dimension|reference" />
+    </declare-styleable>
+
+    <declare-styleable name="ScrimInsetsFrameLayout">
+        <attr name="insetForeground" format="color|reference" />
+    </declare-styleable>
+
+    <declare-styleable name="NavigationView">
+        <attr name="android:elevation" />
+        <attr name="android:background" />
+        <attr name="android:fitsSystemWindows" />
+        <attr name="android:maxWidth" />
+        <attr name="itemTint" format="color|reference" />
+        <attr name="itemBackground" format="reference" />
+    </declare-styleable>
+
+    <declare-styleable name="TabLayout">
+        <attr name="tabIndicatorColor" format="color" />
+        <attr name="tabIndicatorHeight" format="dimension" />
+        <attr name="tabContentStart" format="dimension" />
+
+        <attr name="tabBackground" format="reference" />
+
+        <attr name="tabMode">
+            <enum name="scrollable" value="0" />
+            <enum name="fixed" value="1" />
+        </attr>
+
+        <!-- Standard gravity constant that a child supplies to its parent.
+             Defines how the child view should be positioned, on both the X and Y axes,
+             within its enclosing layout. -->
+        <attr name="tabGravity">
+            <enum name="fill" value="0"/>
+            <enum name="center" value="1"/>
+        </attr>
+
+        <attr name="tabMinWidth" format="dimension" />
+        <attr name="tabMaxWidth" format="dimension" />
+
+        <attr name="tabTextAppearance" format="reference" />
+        <attr name="tabSelectedTextColor" format="color" />
+
+        <attr name="tabPaddingStart" format="dimension" />
+        <attr name="tabPaddingTop" format="dimension" />
+        <attr name="tabPaddingEnd" format="dimension" />
+        <attr name="tabPaddingBottom" format="dimension" />
+        <attr name="tabPadding" format="dimension" />
+    </declare-styleable>
+
+    <declare-styleable name="CoordinatorLayout">
+        <!-- A reference to an array of integers representing the
+             locations of horizontal keylines in dp from the starting edge.
+             Child views can refer to these keylines for alignment using
+             layout_keyline="index" where index is a 0-based index into
+             this array. -->
+        <attr name="keylines" format="reference" />
+    </declare-styleable>
+
+    <declare-styleable name="CoordinatorLayout_LayoutParams">
+        <attr name="android:layout_gravity" />
+        <!-- The class name of a Behavior class defining special runtime behavior
+             for this child view. -->
+        <attr name="layout_behavior" format="string" />
+        <!-- The id of an anchor view that this view should position relative to. -->
+        <attr name="layout_anchor" format="reference" />
+        <!-- The index of a keyline this view should position relative to.
+             android:layout_gravity will affect how the view aligns to the
+             specified keyline. -->
+        <attr name="layout_keyline" format="integer" />
+
+        <!-- Specifies how an object should position relative to an anchor, on both the X and Y axes,
+             within its parent's bounds.  -->
+        <attr name="layout_anchorGravity">
+            <!-- Push object to the top of its container, not changing its size. -->
+            <flag name="top" value="0x30" />
+            <!-- Push object to the bottom of its container, not changing its size. -->
+            <flag name="bottom" value="0x50" />
+            <!-- Push object to the left of its container, not changing its size. -->
+            <flag name="left" value="0x03" />
+            <!-- Push object to the right of its container, not changing its size. -->
+            <flag name="right" value="0x05" />
+            <!-- Place object in the vertical center of its container, not changing its size. -->
+            <flag name="center_vertical" value="0x10" />
+            <!-- Grow the vertical size of the object if needed so it completely fills its container. -->
+            <flag name="fill_vertical" value="0x70" />
+            <!-- Place object in the horizontal center of its container, not changing its size. -->
+            <flag name="center_horizontal" value="0x01" />
+            <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->
+            <flag name="fill_horizontal" value="0x07" />
+            <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->
+            <flag name="center" value="0x11" />
+            <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->
+            <flag name="fill" value="0x77" />
+            <!-- Additional option that can be set to have the top and/or bottom edges of
+                 the child clipped to its container's bounds.
+                 The clip will be based on the vertical gravity: a top gravity will clip the bottom
+                 edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->
+            <flag name="clip_vertical" value="0x80" />
+            <!-- Additional option that can be set to have the left and/or right edges of
+                 the child clipped to its container's bounds.
+                 The clip will be based on the horizontal gravity: a left gravity will clip the right
+                 edge, a right gravity will clip the left edge, and neither will clip both edges. -->
+            <flag name="clip_horizontal" value="0x08" />
+            <!-- Push object to the beginning of its container, not changing its size. -->
+            <flag name="start" value="0x00800003" />
+            <!-- Push object to the end of its container, not changing its size. -->
+            <flag name="end" value="0x00800005" />
+        </attr>
+    </declare-styleable>
+
+    <declare-styleable name="TextInputLayout">
+        <attr name="hintTextAppearance" format="reference" />
+        <!-- The hint to display in the floating label -->
+        <attr name="android:hint" />
+        <!-- Whether the layout is laid out as if an error will be displayed -->
+        <attr name="errorEnabled" format="boolean" />
+        <!-- TextAppearance of any error message displayed -->
+        <attr name="errorTextAppearance" format="reference" />
+    </declare-styleable>
+
+    <declare-styleable name="SnackbarLayout">
+        <attr name="android:maxWidth" />
+        <attr name="maxActionInlineWidth" format="dimension" />
+    </declare-styleable>
+
+</resources>
+
diff --git a/design/res/values/colors.xml b/design/res/values/colors.xml
new file mode 100644
index 0000000..f46e0ce
--- /dev/null
+++ b/design/res/values/colors.xml
@@ -0,0 +1,31 @@
+<?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>
+
+    <!-- Shadow color for the first pixels of a shadow -->
+    <color name="shadow_start_color">#44000000</color>
+    <!-- Shadow color for the middle pixels of a shadow -->
+    <color name="shadow_mid_color">#14000000</color>
+    <!-- Shadow color for the furthest pixels of a shadow -->
+    <color name="shadow_end_color">@android:color/transparent</color>
+
+    <color name="error_color">#FFDD2C00</color>
+
+    <color name="snackbar_background_color">#323232</color>
+
+</resources>
\ No newline at end of file
diff --git a/design/res/values/config.xml b/design/res/values/config.xml
new file mode 100644
index 0000000..2ff276a
--- /dev/null
+++ b/design/res/values/config.xml
@@ -0,0 +1,22 @@
+<?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>
+
+    <integer name="snackbar_text_max_lines">2</integer>
+
+</resources>
\ No newline at end of file
diff --git a/design/res/values/dimens.xml b/design/res/values/dimens.xml
new file mode 100644
index 0000000..23d9c0d
--- /dev/null
+++ b/design/res/values/dimens.xml
@@ -0,0 +1,52 @@
+<?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>
+
+    <dimen name="fab_elevation">8dp</dimen>
+    <dimen name="fab_translation_z_pressed">6dp</dimen>
+    <dimen name="fab_content_size">24dp</dimen>
+    <dimen name="fab_size_normal">56dp</dimen>
+    <dimen name="fab_size_mini">40dp</dimen>
+
+    <dimen name="navigation_max_width">320dp</dimen>
+    <dimen name="navigation_elevation">12dp</dimen>
+    <dimen name="navigation_icon_padding">32dp</dimen>
+    <dimen name="navigation_icon_size">24dp</dimen>
+    <dimen name="navigation_separator_vertical_padding">8dp</dimen>
+    <dimen name="navigation_padding_top_default">0dp</dimen>
+
+    <dimen name="tab_min_width">72dp</dimen>
+    <dimen name="tab_max_width">264dp</dimen>
+
+    <dimen name="snackbar_min_width">-1px</dimen>
+    <dimen name="snackbar_max_width">-1px</dimen>
+    <dimen name="snackbar_elevation">2dp</dimen>
+    <dimen name="snackbar_background_corner_radius">0dp</dimen>
+
+    <dimen name="snackbar_padding_horizontal">12dp</dimen>
+    <dimen name="snackbar_padding_vertical">14dp</dimen>
+    <dimen name="snackbar_padding_vertical_2lines">24dp</dimen>
+
+    <!-- Extra spacing between the action and message views -->
+    <dimen name="snackbar_extra_spacing_horizontal">0dp</dimen>
+    <!-- The maximum width for a Snackbar's inline action. If the view is width than this then
+         the Snackbar will change to vertical stacking -->
+    <dimen name="snackbar_action_inline_max_width">128dp</dimen>
+
+    <dimen name="snackbar_text_size">14sp</dimen>
+
+</resources>
diff --git a/design/res/values/styles.xml b/design/res/values/styles.xml
new file mode 100644
index 0000000..46e2144
--- /dev/null
+++ b/design/res/values/styles.xml
@@ -0,0 +1,96 @@
+<?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>
+
+    <style name="Widget.Design.FloatingActionButton" parent="android:Widget">
+        <item name="android:background">@drawable/fab_background</item>
+        <item name="backgroundTint">?attr/colorAccent</item>
+
+        <item name="fabSize">normal</item>
+        <item name="elevation">@dimen/fab_elevation</item>
+        <item name="pressedTranslationZ">@dimen/fab_translation_z_pressed</item>
+        <item name="rippleColor">?attr/colorControlHighlight</item>
+    </style>
+
+    <style name="Widget.Design.ScrimInsetsFrameLayout" parent="">
+        <item name="insetForeground">#4000</item>
+    </style>
+
+    <style name="Widget.Design.NavigationView" parent="">
+        <item name="android:elevation">@dimen/navigation_elevation</item>
+        <item name="android:background">?android:attr/windowBackground</item>
+        <item name="android:fitsSystemWindows">true</item>
+        <item name="android:maxWidth">@dimen/navigation_max_width</item>
+    </style>
+
+    <style name="Widget.Design.TabLayout" parent="Base.Widget.Design.TabLayout">
+        <item name="tabGravity">fill</item>
+        <item name="tabMode">fixed</item>
+    </style>
+
+    <style name="Base.Widget.Design.TabLayout" parent="android:Widget">
+        <item name="tabMaxWidth">@dimen/tab_max_width</item>
+        <item name="tabIndicatorColor">?attr/colorAccent</item>
+        <item name="tabIndicatorHeight">2dp</item>
+        <item name="tabPaddingStart">12dp</item>
+        <item name="tabPaddingEnd">12dp</item>
+        <item name="tabBackground">?attr/selectableItemBackground</item>
+        <item name="tabTextAppearance">@style/TextAppearance.Design.Tab</item>
+        <item name="tabSelectedTextColor">?android:textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Design.Tab" parent="TextAppearance.AppCompat.Button">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">?android:textColorSecondary</item>
+        <item name="textAllCaps">true</item>
+    </style>
+
+    <style name="Widget.Design.TextInputLayout" parent="android:Widget">
+        <item name="hintTextAppearance">@style/TextAppearance.Design.Hint</item>
+        <item name="errorTextAppearance">@style/TextAppearance.Design.Error</item>
+    </style>
+
+    <style name="TextAppearance.Design.Hint" parent="TextAppearance.AppCompat.Caption">
+        <item name="android:textColor">?attr/colorControlActivated</item>
+    </style>
+
+    <style name="TextAppearance.Design.Error" parent="TextAppearance.AppCompat.Caption">
+        <item name="android:textColor">@color/error_color</item>
+    </style>
+
+    <style name="TextAppearance.Design.Snackbar.Message" parent="android:TextAppearance">
+        <item name="android:textSize">@dimen/snackbar_text_size</item>
+        <item name="android:textColor">?android:textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Design.Snackbar.Action" parent="TextAppearance.AppCompat.Button">
+        <item name="android:textColor">?colorAccent</item>
+    </style>
+
+    <style name="Widget.Design.Snackbar" parent="android:Widget">
+        <item name="android:theme">@style/ThemeOverlay.AppCompat.Dark</item>
+        <item name="android:minWidth">@dimen/snackbar_min_width</item>
+        <item name="android:maxWidth">@dimen/snackbar_max_width</item>
+        <item name="android:background">@drawable/snackbar_background</item>
+        <item name="android:paddingLeft">@dimen/snackbar_padding_horizontal</item>
+        <item name="android:paddingRight">@dimen/snackbar_padding_horizontal</item>
+        <item name="android:elevation">@dimen/snackbar_elevation</item>
+        <item name="maxActionInlineWidth">@dimen/snackbar_action_inline_max_width</item>
+    </style>
+
+</resources>
+
diff --git a/design/src/.readme b/design/src/.readme
new file mode 100644
index 0000000..4bcebad
--- /dev/null
+++ b/design/src/.readme
@@ -0,0 +1,2 @@
+This hidden file is there to ensure there is an src folder.
+Once we support binary library this will go away.
\ No newline at end of file
diff --git a/design/src/android/support/design/internal/NavigationMenuItemView.java b/design/src/android/support/design/internal/NavigationMenuItemView.java
new file mode 100644
index 0000000..0db8f62
--- /dev/null
+++ b/design/src/android/support/design/internal/NavigationMenuItemView.java
@@ -0,0 +1,187 @@
+/*
+ * 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.design.internal;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
+import android.support.design.R;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v4.widget.TextViewCompat;
+import android.support.v7.internal.view.menu.MenuItemImpl;
+import android.support.v7.internal.view.menu.MenuView;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * @hide
+ */
+public class NavigationMenuItemView extends TextView implements MenuView.ItemView {
+
+    private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
+
+    private static final int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled};
+
+    private int mIconSize;
+
+    private MenuItemImpl mItemData;
+
+    private ColorStateList mTintList;
+
+    public NavigationMenuItemView(Context context) {
+        this(context, null);
+    }
+
+    public NavigationMenuItemView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NavigationMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mIconSize = context.getResources().getDimensionPixelSize(R.dimen.navigation_icon_size);
+    }
+
+    @Override
+    public void initialize(MenuItemImpl itemData, int menuType) {
+        mItemData = itemData;
+
+        setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
+
+        if (mTintList == null && (itemData.isChecked() || !itemData.isEnabled())) {
+            mTintList = createDefaultTintList();
+            setTextColor(mTintList);
+        }
+        if (getBackground() == null) {
+            setBackgroundDrawable(createDefaultBackground());
+        }
+
+        setCheckable(itemData.isCheckable());
+        setChecked(itemData.isChecked());
+        setTitle(itemData.getTitle());
+        setIcon(itemData.getIcon());
+        setEnabled(itemData.isEnabled());
+    }
+
+    private ColorStateList createDefaultTintList() {
+        TypedValue value = new TypedValue();
+        if (!getContext().getTheme()
+                .resolveAttribute(android.R.attr.textColorPrimary, value, true)) {
+            return null;
+        }
+        ColorStateList base = getResources().getColorStateList(value.resourceId);
+        if (!getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true)) {
+            return null;
+        }
+        int colorPrimary = value.data;
+        int defaultColor = base.getDefaultColor();
+        return new ColorStateList(new int[][]{
+                DISABLED_STATE_SET,
+                CHECKED_STATE_SET,
+                EMPTY_STATE_SET
+        }, new int[]{
+                base.getColorForState(DISABLED_STATE_SET, defaultColor),
+                colorPrimary,
+                defaultColor
+        });
+    }
+
+    private StateListDrawable createDefaultBackground() {
+        TypedValue value = new TypedValue();
+        if (getContext().getTheme()
+                .resolveAttribute(R.attr.colorControlHighlight, value, true)) {
+            StateListDrawable drawable = new StateListDrawable();
+            drawable.addState(CHECKED_STATE_SET, new ColorDrawable(value.data));
+            drawable.addState(EMPTY_STATE_SET, new ColorDrawable(Color.TRANSPARENT));
+            return drawable;
+        }
+        return null;
+    }
+
+    @Override
+    public MenuItemImpl getItemData() {
+        return mItemData;
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        setText(title);
+    }
+
+    @Override
+    public void setCheckable(boolean checkable) {
+        if (checkable && mTintList != null) {
+            setTextColor(mTintList);
+        }
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        refreshDrawableState();
+    }
+
+    @Override
+    public void setShortcut(boolean showShortcut, char shortcutKey) {
+    }
+
+    @Override
+    public void setIcon(Drawable icon) {
+        if (icon != null) {
+            icon = DrawableCompat.wrap(icon);
+            icon.setBounds(0, 0, mIconSize, mIconSize);
+            icon = icon.mutate();
+            if (mItemData.isChecked() || !mItemData.isEnabled()) {
+                DrawableCompat.setTintList(icon, mTintList);
+            } else {
+                DrawableCompat.setTintList(icon, null);
+            }
+        }
+        TextViewCompat.setCompoundDrawablesRelative(this, icon, null, null, null);
+    }
+
+    @Override
+    public boolean prefersCondensedTitle() {
+        return false;
+    }
+
+    @Override
+    public boolean showsIcon() {
+        return true;
+    }
+
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+        if (mItemData != null && mItemData.isChecked()) {
+            return mergeDrawableStates(super.onCreateDrawableState(extraSpace + 1),
+                    CHECKED_STATE_SET);
+        } else {
+            return super.onCreateDrawableState(extraSpace);
+        }
+    }
+
+    public void setTintList(ColorStateList tintList) {
+        mTintList = tintList;
+        if (tintList != null) {
+            setTextColor(tintList);
+        }
+    }
+
+}
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
new file mode 100644
index 0000000..2014b72
--- /dev/null
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -0,0 +1,416 @@
+/*
+ * 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.design.internal;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.R;
+import android.support.v7.internal.view.menu.MenuBuilder;
+import android.support.v7.internal.view.menu.MenuItemImpl;
+import android.support.v7.internal.view.menu.MenuPresenter;
+import android.support.v7.internal.view.menu.MenuView;
+import android.support.v7.internal.view.menu.SubMenuBuilder;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ */
+public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnItemClickListener {
+
+    private static final String STATE_HIERARCHY = "android:menu:list";
+
+    private NavigationMenuView mMenuView;
+
+    private Callback mCallback;
+
+    private MenuBuilder mMenu;
+
+    private ArrayList<NavigationMenuItem> mItems = new ArrayList<>();
+
+    private int mId;
+
+    private NavigationMenuAdapter mAdapter;
+
+    private LayoutInflater mLayoutInflater;
+
+    private View mSpace;
+
+    private ColorStateList mItemTintList;
+
+    private int mItemBackgroundResource;
+
+    private ColorDrawable mTransparentIcon;
+
+    /**
+     * Padding to be inserted at the top of the list to avoid the first menu item
+     * from being placed underneath the status bar.
+     */
+    private int mPaddingTopDefault;
+
+    @Override
+    public void initForMenu(Context context, MenuBuilder menu) {
+        mLayoutInflater = LayoutInflater.from(context);
+        mMenu = menu;
+        mPaddingTopDefault = context.getResources().getDimensionPixelOffset(
+                R.dimen.navigation_padding_top_default);
+    }
+
+    @Override
+    public MenuView getMenuView(ViewGroup root) {
+        if (mMenuView == null) {
+            mMenuView = (NavigationMenuView) mLayoutInflater.inflate(
+                    R.layout.design_navigation_menu, root, false);
+            if (mAdapter == null) {
+                mAdapter = new NavigationMenuAdapter();
+            }
+            mMenuView.setAdapter(mAdapter);
+            mMenuView.setOnItemClickListener(this);
+        }
+        return mMenuView;
+    }
+
+    @Override
+    public void updateMenuView(boolean cleared) {
+        if (mAdapter != null) {
+            prepareMenuItems();
+            mAdapter.notifyDataSetChanged();
+        }
+    }
+
+    /**
+     * Flattens the visible menu items of {@link #mMenu} into {@link #mItems},
+     * while inserting separators between items when necessary.
+     */
+    private void prepareMenuItems() {
+        mItems.clear();
+        int currentGroupId = -1;
+        int currentGroupStart = 0;
+        boolean currentGroupHasIcon = false;
+        for (int i = 0, totalSize = mMenu.getVisibleItems().size(); i < totalSize; i++) {
+            MenuItemImpl item = mMenu.getVisibleItems().get(i);
+            if (item.hasSubMenu()) {
+                SubMenu subMenu = item.getSubMenu();
+                if (subMenu.hasVisibleItems()) {
+                    if (i != 0) {
+                        mItems.add(NavigationMenuItem.SEPARATOR);
+                    }
+                    mItems.add(NavigationMenuItem.of(item));
+                    boolean subMenuHasIcon = false;
+                    int subMenuStart = mItems.size();
+                    for (int j = 0, size = subMenu.size(); j < size; j++) {
+                        MenuItem subMenuItem = subMenu.getItem(j);
+                        if (subMenuItem.isVisible()) {
+                            if (!subMenuHasIcon && subMenuItem.getIcon() != null) {
+                                subMenuHasIcon = true;
+                            }
+                            mItems.add(NavigationMenuItem.of((MenuItemImpl) subMenuItem));
+                        }
+                    }
+                    if (subMenuHasIcon) {
+                        appendTransparentIconIfMissing(subMenuStart, mItems.size());
+                    }
+                }
+            } else {
+                int groupId = item.getGroupId();
+                if (groupId != currentGroupId) { // first item in group
+                    currentGroupStart = mItems.size();
+                    currentGroupHasIcon = item.getIcon() != null;
+                    if (i != 0) {
+                        currentGroupStart++;
+                        mItems.add(NavigationMenuItem.SEPARATOR);
+                    }
+                } else if (!currentGroupHasIcon && item.getIcon() != null) {
+                    currentGroupHasIcon = true;
+                    appendTransparentIconIfMissing(currentGroupStart, mItems.size());
+                }
+                if (currentGroupHasIcon && item.getIcon() == null) {
+                    item.setIcon(android.R.color.transparent);
+                }
+                mItems.add(NavigationMenuItem.of(item));
+                currentGroupId = groupId;
+            }
+        }
+    }
+
+    private void appendTransparentIconIfMissing(int startIndex, int endIndex) {
+        for (int i = startIndex; i < endIndex; i++) {
+            MenuItem item = mItems.get(i).getMenuItem();
+            if (item.getIcon() == null) {
+                if (mTransparentIcon == null) {
+                    mTransparentIcon = new ColorDrawable(android.R.color.transparent);
+                }
+                item.setIcon(mTransparentIcon);
+            }
+        }
+    }
+
+    @Override
+    public void setCallback(Callback cb) {
+        mCallback = cb;
+    }
+
+    @Override
+    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+        return false;
+    }
+
+    @Override
+    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+        if (mCallback != null) {
+            mCallback.onCloseMenu(menu, allMenusAreClosing);
+        }
+    }
+
+    @Override
+    public boolean flagActionItems() {
+        return false;
+    }
+
+    @Override
+    public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
+        return false;
+    }
+
+    @Override
+    public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
+        return false;
+    }
+
+    @Override
+    public int getId() {
+        return mId;
+    }
+
+    public void setId(int id) {
+        mId = id;
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        Bundle state = new Bundle();
+        SparseArray<Parcelable> hierarchy = new SparseArray<>();
+        if (mMenuView != null) {
+            mMenuView.saveHierarchyState(hierarchy);
+        }
+        state.putSparseParcelableArray(STATE_HIERARCHY, hierarchy);
+        return state;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable parcelable) {
+        Bundle state = (Bundle) parcelable;
+        SparseArray<Parcelable> hierarchy = state.getSparseParcelableArray(STATE_HIERARCHY);
+        if (hierarchy != null) {
+            mMenuView.restoreHierarchyState(hierarchy);
+        }
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        int positionInAdapter = position - mMenuView.getHeaderViewsCount();
+        if (positionInAdapter >= 0) {
+            mMenu.performItemAction(mAdapter.getItem(positionInAdapter).getMenuItem(), this, 0);
+        }
+    }
+
+    public View inflateHeaderView(@LayoutRes int res) {
+        View view = mLayoutInflater.inflate(res, mMenuView, false);
+        addHeaderView(view);
+        onHeaderAdded();
+        return view;
+    }
+
+    public void addHeaderView(@NonNull View view) {
+        mMenuView.addHeaderView(view);
+        onHeaderAdded();
+    }
+
+    private void onHeaderAdded() {
+        // If we have just added the first header, we also need to insert a space
+        // between the header and the menu items.
+        if (mMenuView.getHeaderViewsCount() == 1) {
+            mSpace = mLayoutInflater.inflate(R.layout.design_navigation_item_space, mMenuView,
+                    false);
+            mMenuView.addHeaderView(mSpace);
+        }
+        // The padding on top should be cleared.
+        mMenuView.setPadding(0, 0, 0, 0);
+    }
+
+    public void removeHeaderView(@NonNull View view) {
+        if (mMenuView.removeHeaderView(view)) {
+            // Remove the space if it is the only remained header
+            if (mMenuView.getHeaderViewsCount() == 1) {
+                mMenuView.removeHeaderView(mSpace);
+                mMenuView.setPadding(0, mPaddingTopDefault, 0, 0);
+            }
+        }
+    }
+
+    @Nullable
+    public ColorStateList getItemTintList() {
+        return mItemTintList;
+    }
+
+    public void setItemTintList(@Nullable ColorStateList itemTintList) {
+        mItemTintList = itemTintList;
+    }
+
+    @DrawableRes
+    public int getItemBackgroundResource() {
+        return mItemBackgroundResource;
+    }
+
+    public void setItemBackgroundResource(@DrawableRes int itemBackgroundResource) {
+        mItemBackgroundResource = itemBackgroundResource;
+    }
+
+    private class NavigationMenuAdapter extends BaseAdapter {
+
+        private static final int VIEW_TYPE_NORMAL = 0;
+
+        private static final int VIEW_TYPE_SUBHEADER = 1;
+
+        private static final int VIEW_TYPE_SEPARATOR = 2;
+
+        @Override
+        public int getCount() {
+            return mItems.size();
+        }
+
+        @Override
+        public NavigationMenuItem getItem(int position) {
+            return mItems.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 3;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            NavigationMenuItem item = getItem(position);
+            if (item.isSeparator()) {
+                return VIEW_TYPE_SEPARATOR;
+            } else if (item.getMenuItem().hasSubMenu()) {
+                return VIEW_TYPE_SUBHEADER;
+            } else {
+                return VIEW_TYPE_NORMAL;
+            }
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            NavigationMenuItem item = getItem(position);
+            int viewType = getItemViewType(position);
+            switch (viewType) {
+                case VIEW_TYPE_NORMAL:
+                    if (convertView == null) {
+                        convertView = mLayoutInflater.inflate(R.layout.design_navigation_item,
+                                parent, false);
+                    }
+                    NavigationMenuItemView itemView = (NavigationMenuItemView) convertView;
+                    itemView.setTintList(mItemTintList);
+                    itemView.setBackgroundResource(mItemBackgroundResource);
+                    itemView.initialize(item.getMenuItem(), 0);
+                    break;
+                case VIEW_TYPE_SUBHEADER:
+                    if (convertView == null) {
+                        convertView = mLayoutInflater.inflate(
+                                R.layout.design_navigation_item_subheader, parent, false);
+                    }
+                    TextView subHeader = (TextView) convertView;
+                    subHeader.setText(item.getMenuItem().getTitle());
+                    break;
+                case VIEW_TYPE_SEPARATOR:
+                    if (convertView == null) {
+                        convertView = mLayoutInflater.inflate(
+                                R.layout.design_navigation_item_separator, parent, false);
+                    }
+                    break;
+            }
+            return convertView;
+        }
+
+        @Override
+        public boolean areAllItemsEnabled() {
+            return false;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return getItem(position).isEnabled();
+        }
+
+    }
+
+    /**
+     * Wraps {@link MenuItemImpl}. This allows separators to be counted as items in list.
+     */
+    private static class NavigationMenuItem {
+
+        private static final NavigationMenuItem SEPARATOR = new NavigationMenuItem(null);
+
+        private final MenuItemImpl mMenuItem;
+
+        private NavigationMenuItem(MenuItemImpl item) {
+            mMenuItem = item;
+        }
+
+        public static NavigationMenuItem of(MenuItemImpl item) {
+            return new NavigationMenuItem(item);
+        }
+
+        public boolean isSeparator() {
+            return this == SEPARATOR;
+        }
+
+        public MenuItemImpl getMenuItem() {
+            return mMenuItem;
+        }
+
+        public boolean isEnabled() {
+            // Separators and subheaders never respond to click
+            return mMenuItem != null && !mMenuItem.hasSubMenu() && mMenuItem.isEnabled();
+        }
+
+    }
+
+}
diff --git a/design/src/android/support/design/internal/NavigationMenuView.java b/design/src/android/support/design/internal/NavigationMenuView.java
new file mode 100644
index 0000000..054b800
--- /dev/null
+++ b/design/src/android/support/design/internal/NavigationMenuView.java
@@ -0,0 +1,52 @@
+/*
+ * 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.design.internal;
+
+import android.content.Context;
+import android.support.v7.internal.view.menu.MenuBuilder;
+import android.support.v7.internal.view.menu.MenuView;
+import android.util.AttributeSet;
+import android.widget.ListView;
+
+/**
+ * @hide
+ */
+public class NavigationMenuView extends ListView implements MenuView {
+
+    public NavigationMenuView(Context context) {
+        this(context, null);
+    }
+
+    public NavigationMenuView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NavigationMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public void initialize(MenuBuilder menu) {
+
+    }
+
+    @Override
+    public int getWindowAnimations() {
+        return 0;
+    }
+
+}
diff --git a/design/src/android/support/design/internal/ScrimInsetsFrameLayout.java b/design/src/android/support/design/internal/ScrimInsetsFrameLayout.java
new file mode 100644
index 0000000..246d5b3
--- /dev/null
+++ b/design/src/android/support/design/internal/ScrimInsetsFrameLayout.java
@@ -0,0 +1,130 @@
+/*
+ * 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.design.internal;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.design.R;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.WindowInsetsCompat;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * @hide
+ */
+public class ScrimInsetsFrameLayout extends FrameLayout {
+
+    private Drawable mInsetForeground;
+
+    private Rect mInsets;
+
+    private Rect mTempRect = new Rect();
+
+    public ScrimInsetsFrameLayout(Context context) {
+        this(context, null);
+    }
+
+    public ScrimInsetsFrameLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ScrimInsetsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.ScrimInsetsFrameLayout, defStyleAttr,
+                R.style.Widget_Design_ScrimInsetsFrameLayout);
+        mInsetForeground = a.getDrawable(R.styleable.ScrimInsetsFrameLayout_insetForeground);
+        a.recycle();
+        setWillNotDraw(true); // No need to draw until the insets are adjusted
+
+        ViewCompat.setOnApplyWindowInsetsListener(this,
+                new android.support.v4.view.OnApplyWindowInsetsListener() {
+                    @Override
+                    public WindowInsetsCompat onApplyWindowInsets(View v,
+                            WindowInsetsCompat insets) {
+                        if (null == mInsets) {
+                            mInsets = new Rect();
+                        }
+                        mInsets.set(insets.getSystemWindowInsetLeft(),
+                                insets.getSystemWindowInsetTop(),
+                                insets.getSystemWindowInsetRight(),
+                                insets.getSystemWindowInsetBottom());
+                        setWillNotDraw(mInsets.isEmpty() || mInsetForeground == null);
+                        ViewCompat.postInvalidateOnAnimation(ScrimInsetsFrameLayout.this);
+                        return insets.consumeSystemWindowInsets();
+                    }
+                });
+    }
+
+    @Override
+    public void draw(@NonNull Canvas canvas) {
+        super.draw(canvas);
+
+        int width = getWidth();
+        int height = getHeight();
+        if (mInsets != null && mInsetForeground != null) {
+            int sc = canvas.save();
+            canvas.translate(getScrollX(), getScrollY());
+
+            // Top
+            mTempRect.set(0, 0, width, mInsets.top);
+            mInsetForeground.setBounds(mTempRect);
+            mInsetForeground.draw(canvas);
+
+            // Bottom
+            mTempRect.set(0, height - mInsets.bottom, width, height);
+            mInsetForeground.setBounds(mTempRect);
+            mInsetForeground.draw(canvas);
+
+            // Left
+            mTempRect.set(0, mInsets.top, mInsets.left, height - mInsets.bottom);
+            mInsetForeground.setBounds(mTempRect);
+            mInsetForeground.draw(canvas);
+
+            // Right
+            mTempRect.set(width - mInsets.right, mInsets.top, width, height - mInsets.bottom);
+            mInsetForeground.setBounds(mTempRect);
+            mInsetForeground.draw(canvas);
+
+            canvas.restoreToCount(sc);
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mInsetForeground != null) {
+            mInsetForeground.setCallback(this);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mInsetForeground != null) {
+            mInsetForeground.setCallback(null);
+        }
+    }
+
+}
diff --git a/design/src/android/support/design/widget/CollapsingTextHelper.java b/design/src/android/support/design/widget/CollapsingTextHelper.java
new file mode 100644
index 0000000..2afe4b6
--- /dev/null
+++ b/design/src/android/support/design/widget/CollapsingTextHelper.java
@@ -0,0 +1,481 @@
+/*
+ * 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.design.widget;
+
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Build;
+import android.support.design.R;
+import android.support.v4.view.ViewCompat;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+final class CollapsingTextHelper {
+
+    // Pre-JB-MR2 doesn't support HW accelerated canvas scaled text so we will workaround it
+    // by using our own texture
+    private static final boolean USE_SCALING_TEXTURE = Build.VERSION.SDK_INT < 18;
+
+    private static final boolean DEBUG_DRAW = false;
+    private static final Paint DEBUG_DRAW_PAINT;
+    static {
+        DEBUG_DRAW_PAINT = DEBUG_DRAW ? new Paint() : null;
+        if (DEBUG_DRAW_PAINT != null) {
+            DEBUG_DRAW_PAINT.setAntiAlias(true);
+            DEBUG_DRAW_PAINT.setColor(Color.MAGENTA);
+        }
+    }
+
+    private final View mView;
+
+    private float mExpandedFraction;
+
+    private final Rect mExpandedBounds;
+    private final Rect mCollapsedBounds;
+    private int mExpandedTextVerticalGravity = Gravity.CENTER_VERTICAL;
+    private int mCollapsedTextVerticalGravity = Gravity.CENTER_VERTICAL;
+    private float mExpandedTextSize;
+    private float mCollapsedTextSize;
+    private int mExpandedTextColor;
+    private int mCollapsedTextColor;
+
+    private float mExpandedTop;
+    private float mCollapsedTop;
+
+    private CharSequence mText;
+    private CharSequence mTextToDraw;
+    private float mTextWidth;
+
+    private boolean mUseTexture;
+    private Bitmap mExpandedTitleTexture;
+    private Paint mTexturePaint;
+    private float mTextureAscent;
+    private float mTextureDescent;
+
+    private float mCurrentLeft;
+    private float mCurrentRight;
+    private float mCurrentTop;
+    private float mScale;
+
+    private final TextPaint mTextPaint;
+
+    private Interpolator mPositionInterpolator;
+    private Interpolator mTextSizeInterpolator;
+
+    public CollapsingTextHelper(View view) {
+        mView = view;
+
+        mTextPaint = new TextPaint();
+        mTextPaint.setAntiAlias(true);
+
+        mCollapsedBounds = new Rect();
+        mExpandedBounds = new Rect();
+    }
+
+    void setTextSizeInterpolator(Interpolator interpolator) {
+        mTextSizeInterpolator = interpolator;
+        recalculate();
+    }
+
+    void setPositionInterpolator(Interpolator interpolator) {
+        mPositionInterpolator = interpolator;
+        recalculate();
+    }
+
+    void setExpandedTextSize(float textSize) {
+        if (mExpandedTextSize != textSize) {
+            mExpandedTextSize = textSize;
+            recalculate();
+        }
+    }
+
+    void setCollapsedTextSize(float textSize) {
+        if (mCollapsedTextSize != textSize) {
+            mCollapsedTextSize = textSize;
+            recalculate();
+        }
+    }
+
+    void setCollapsedTextColor(int textColor) {
+        if (mCollapsedTextColor != textColor) {
+            mCollapsedTextColor = textColor;
+            recalculate();
+        }
+    }
+
+    void setExpandedTextColor(int textColor) {
+        if (mExpandedTextColor != textColor) {
+            mExpandedTextColor = textColor;
+            recalculate();
+        }
+    }
+
+    void setExpandedBounds(int left, int top, int right, int bottom) {
+        mExpandedBounds.set(left, top, right, bottom);
+        recalculate();
+    }
+
+    void setCollapsedBounds(int left, int top, int right, int bottom) {
+        mCollapsedBounds.set(left, top, right, bottom);
+        recalculate();
+    }
+
+    void setExpandedTextVerticalGravity(int gravity) {
+        gravity &= Gravity.VERTICAL_GRAVITY_MASK;
+
+        if (mExpandedTextVerticalGravity != gravity) {
+            mExpandedTextVerticalGravity = gravity;
+            recalculate();
+        }
+    }
+
+    void setCollapsedTextVerticalGravity(int gravity) {
+        gravity &= Gravity.VERTICAL_GRAVITY_MASK;
+
+        if (mCollapsedTextVerticalGravity != gravity) {
+            mCollapsedTextVerticalGravity = gravity;
+            recalculate();
+        }
+    }
+
+    void setCollapsedTextAppearance(int resId) {
+        TypedArray a = mView.getContext().obtainStyledAttributes(resId, R.styleable.TextAppearance);
+        if (a.hasValue(R.styleable.TextAppearance_android_textColor)) {
+            mCollapsedTextColor = a.getColor(R.styleable.TextAppearance_android_textColor, 0);
+        }
+        if (a.hasValue(R.styleable.TextAppearance_android_textSize)) {
+            mCollapsedTextSize = a.getDimensionPixelSize(
+                    R.styleable.TextAppearance_android_textSize, 0);
+        }
+        a.recycle();
+
+        recalculate();
+    }
+
+    void setExpandedTextAppearance(int resId) {
+        TypedArray a = mView.getContext().obtainStyledAttributes(resId, R.styleable.TextAppearance);
+        if (a.hasValue(R.styleable.TextAppearance_android_textColor)) {
+            mExpandedTextColor = a.getColor(R.styleable.TextAppearance_android_textColor, 0);
+        }
+        if (a.hasValue(R.styleable.TextAppearance_android_textSize)) {
+            mExpandedTextSize = a.getDimensionPixelSize(
+                    R.styleable.TextAppearance_android_textSize, 0);
+        }
+        a.recycle();
+
+        recalculate();
+    }
+
+    /**
+     * Set the value indicating the current scroll value. This decides how much of the
+     * background will be displayed, as well as the title metrics/positioning.
+     *
+     * A value of {@code 0.0} indicates that the layout is fully expanded.
+     * A value of {@code 1.0} indicates that the layout is fully collapsed.
+     */
+    void setExpansionFraction(float fraction) {
+        if (fraction != mExpandedFraction) {
+            mExpandedFraction = fraction;
+            calculateOffsets();
+        }
+    }
+
+    float getExpansionFraction() {
+        return mExpandedFraction;
+    }
+
+    float getCollapsedTextSize() {
+        return mCollapsedTextSize;
+    }
+
+    float getExpandedTextSize() {
+        return mExpandedTextSize;
+    }
+
+    private void calculateOffsets() {
+        final float fraction = mExpandedFraction;
+
+        mCurrentLeft = interpolate(mExpandedBounds.left, mCollapsedBounds.left,
+                fraction, mPositionInterpolator);
+        mCurrentTop = interpolate(mExpandedTop, mCollapsedTop, fraction, mPositionInterpolator);
+        mCurrentRight = interpolate(mExpandedBounds.right, mCollapsedBounds.right,
+                fraction, mPositionInterpolator);
+        setInterpolatedTextSize(interpolate(mExpandedTextSize, mCollapsedTextSize,
+                fraction, mTextSizeInterpolator));
+
+        if (mCollapsedTextColor != mExpandedTextColor) {
+            // If the collapsed and expanded text colors are different, blend them based on the
+            // fraction
+            mTextPaint.setColor(blendColors(mExpandedTextColor, mCollapsedTextColor, fraction));
+        } else {
+            mTextPaint.setColor(mCollapsedTextColor);
+        }
+
+        ViewCompat.postInvalidateOnAnimation(mView);
+    }
+
+    private void calculateBaselines() {
+        // We then calculate the collapsed text size, using the same logic
+        mTextPaint.setTextSize(mCollapsedTextSize);
+        switch (mCollapsedTextVerticalGravity) {
+            case Gravity.BOTTOM:
+                mCollapsedTop = mCollapsedBounds.bottom;
+                break;
+            case Gravity.TOP:
+                mCollapsedTop = mCollapsedBounds.top - mTextPaint.ascent();
+                break;
+            case Gravity.CENTER_VERTICAL:
+            default:
+                float textHeight = mTextPaint.descent() - mTextPaint.ascent();
+                float textOffset = (textHeight / 2) - mTextPaint.descent();
+                mCollapsedTop = mCollapsedBounds.centerY() + textOffset;
+                break;
+        }
+
+        mTextPaint.setTextSize(mExpandedTextSize);
+        switch (mExpandedTextVerticalGravity) {
+            case Gravity.BOTTOM:
+                mExpandedTop = mExpandedBounds.bottom;
+                break;
+            case Gravity.TOP:
+                mExpandedTop = mExpandedBounds.top - mTextPaint.ascent();
+                break;
+            case Gravity.CENTER_VERTICAL:
+            default:
+                float textHeight = mTextPaint.descent() - mTextPaint.ascent();
+                float textOffset = (textHeight / 2) - mTextPaint.descent();
+                mExpandedTop = mExpandedBounds.centerY() + textOffset;
+                break;
+        }
+        mTextureAscent = mTextPaint.ascent();
+        mTextureDescent = mTextPaint.descent();
+
+        // The bounds have changed so we need to clear the texture
+        clearTexture();
+    }
+
+    public void draw(Canvas canvas) {
+        final int saveCount = canvas.save();
+
+        if (mTextToDraw != null) {
+            final boolean isRtl = ViewCompat.getLayoutDirection(mView)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
+
+            float x = isRtl ? mCurrentRight : mCurrentLeft;
+            float y = mCurrentTop;
+
+            final boolean drawTexture = mUseTexture && mExpandedTitleTexture != null;
+
+            final float ascent;
+            final float descent;
+
+            if (drawTexture) {
+                ascent = mTextureAscent * mScale;
+                descent = mTextureDescent * mScale;
+            } else {
+                ascent = mTextPaint.ascent() * mScale;
+                descent = mTextPaint.descent() * mScale;
+            }
+
+            if (DEBUG_DRAW) {
+                // Just a debug tool, which drawn a Magneta rect in the text bounds
+                canvas.drawRect(mCurrentLeft, y + ascent, mCurrentRight, y + descent,
+                        DEBUG_DRAW_PAINT);
+            }
+
+            if (drawTexture) {
+                y += ascent;
+            }
+
+            if (mScale != 1f) {
+                canvas.scale(mScale, mScale, x, y);
+            }
+
+            if (isRtl) {
+                x -= mTextWidth;
+            }
+
+            if (drawTexture) {
+                // If we should use a texture, draw it instead of text
+                canvas.drawBitmap(mExpandedTitleTexture, x, y, mTexturePaint);
+            } else {
+                canvas.drawText(mTextToDraw, 0, mTextToDraw.length(), x, y, mTextPaint);
+            }
+        }
+
+        canvas.restoreToCount(saveCount);
+    }
+
+    private void setInterpolatedTextSize(final float textSize) {
+        if (mText == null) return;
+
+        boolean textSizeChanged;
+        float availableWidth;
+
+        if (isClose(textSize, mCollapsedTextSize)) {
+            textSizeChanged = mTextPaint.getTextSize() != mCollapsedTextSize;
+            mTextPaint.setTextSize(mCollapsedTextSize);
+            mScale = 1f;
+            availableWidth = mCollapsedBounds.width();
+        } else {
+            textSizeChanged = mTextPaint.getTextSize() != mExpandedTextSize;
+            mTextPaint.setTextSize(mExpandedTextSize);
+
+            if (isClose(textSize, mExpandedTextSize)) {
+                // If we're close to the expanded text size, snap to it and use a scale of 1
+                mScale = 1f;
+            } else {
+                // Else, we'll scale down from the expanded text size
+                mScale = textSize / mExpandedTextSize;
+            }
+            availableWidth = mExpandedBounds.width();
+        }
+
+        if (mTextToDraw == null || textSizeChanged) {
+            // If we don't currently have text to draw, or the text size has changed, ellipsize...
+            final CharSequence title = TextUtils.ellipsize(mText, mTextPaint,
+                    availableWidth, TextUtils.TruncateAt.END);
+            if (mTextToDraw == null || !mTextToDraw.equals(title)) {
+                mTextToDraw = title;
+            }
+            mTextWidth = mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length());
+        }
+
+        // Use our texture if the scale isn't 1.0
+        mUseTexture = USE_SCALING_TEXTURE && mScale != 1f;
+
+        if (mUseTexture) {
+            // Make sure we have an expanded texture if needed
+            ensureExpandedTexture();
+        }
+
+        ViewCompat.postInvalidateOnAnimation(mView);
+    }
+
+    private void ensureExpandedTexture() {
+        if (mExpandedTitleTexture != null || mExpandedBounds.isEmpty()
+                || TextUtils.isEmpty(mTextToDraw)) {
+            return;
+        }
+
+        mTextPaint.setTextSize(mExpandedTextSize);
+        mTextPaint.setColor(mExpandedTextColor);
+
+        final int w = Math.round(mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()));
+        final int h = Math.round(mTextPaint.descent() - mTextPaint.ascent());
+        mTextWidth = w;
+
+        if (w <= 0 && h <= 0) {
+            return; // If the width or height are 0, return
+        }
+
+        mExpandedTitleTexture = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+
+        Canvas c = new Canvas(mExpandedTitleTexture);
+        c.drawText(mTextToDraw, 0, mTextToDraw.length(), 0, h - mTextPaint.descent(), mTextPaint);
+
+        if (mTexturePaint == null) {
+            // Make sure we have a paint
+            mTexturePaint = new Paint();
+            mTexturePaint.setAntiAlias(true);
+            mTexturePaint.setFilterBitmap(true);
+        }
+    }
+
+    private void recalculate() {
+        if (ViewCompat.isLaidOut(mView)) {
+            // If we've already been laid out, calculate everything now otherwise we'll wait
+            // until a layout
+            calculateBaselines();
+            calculateOffsets();
+        }
+    }
+
+    /**
+     * Set the title to display
+     *
+     * @param text
+     */
+    void setText(CharSequence text) {
+        if (text == null || !text.equals(mText)) {
+            mText = text;
+            clearTexture();
+            recalculate();
+        }
+    }
+
+    CharSequence getText() {
+        return mText;
+    }
+
+    private void clearTexture() {
+        if (mExpandedTitleTexture != null) {
+            mExpandedTitleTexture.recycle();
+            mExpandedTitleTexture = null;
+        }
+    }
+
+    public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        recalculate();
+    }
+
+    /**
+     * Returns true if {@code value} is 'close' to it's closest decimal value. Close is currently
+     * defined as it's difference being < 0.001.
+     */
+    private static boolean isClose(float value, float targetValue) {
+        return Math.abs(value - targetValue) < 0.001f;
+    }
+
+    int getExpandedTextColor() {
+        return mExpandedTextColor;
+    }
+
+    int getCollapsedTextColor() {
+        return mCollapsedTextColor;
+    }
+
+    /**
+     * Blend {@code color1} and {@code color2} using the given ratio.
+     *
+     * @param ratio of which to blend. 0.0 will return {@code color1}, 0.5 will give an even blend,
+     *              1.0 will return {@code color2}.
+     */
+    private static int blendColors(int color1, int color2, float ratio) {
+        final float inverseRatio = 1f - ratio;
+        float a = (Color.alpha(color1) * inverseRatio) + (Color.alpha(color2) * ratio);
+        float r = (Color.red(color1) * inverseRatio) + (Color.red(color2) * ratio);
+        float g = (Color.green(color1) * inverseRatio) + (Color.green(color2) * ratio);
+        float b = (Color.blue(color1) * inverseRatio) + (Color.blue(color2) * ratio);
+        return Color.argb((int) a, (int) r, (int) g, (int) b);
+    }
+
+    private static float interpolate(float startValue, float endValue, float fraction,
+            Interpolator interpolator) {
+        if (interpolator != null) {
+            fraction = interpolator.getInterpolation(fraction);
+        }
+        return AnimationUtils.lerp(startValue, endValue, fraction);
+    }
+}
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
new file mode 100644
index 0000000..2c671ff
--- /dev/null
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -0,0 +1,2163 @@
+/*
+ * 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.design.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.SystemClock;
+import android.support.design.R;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.view.NestedScrollingParent;
+import android.support.v4.view.NestedScrollingParentHelper;
+import android.support.v4.view.ViewCompat;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.ViewTreeObserver;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CoordinatorLayout is a super-powered {@link android.widget.FrameLayout FrameLayout}.
+ *
+ * <p>CoordinatorLayout is intended for two primary use cases:</p>
+ * <ol>
+ *     <li>As a top-level application decor or chrome layout</li>
+ *     <li>As a container for a specific interaction with one or more child views</li>
+ * </ol>
+ *
+ * <p>By specifying {@link CoordinatorLayout.Behavior Behaviors} for child views of a
+ * CoordinatorLayout you can provide many different interactions within a single parent and those
+ * views can also interact with one another. View classes can specify a default behavior when
+ * used as a child of a CoordinatorLayout using the
+ * {@link CoordinatorLayout.DefaultBehavior DefaultBehavior} annotation.</p>
+ *
+ * <p>Behaviors may be used to implement a variety of interactions and additional layout
+ * modifications ranging from sliding drawers and panels to swipe-dismissable elements and buttons
+ * that stick to other elements as they move and animate.</p>
+ *
+ * <p>Children of a CoordinatorLayout may have an
+ * {@link CoordinatorLayout.LayoutParams#setAnchorId(int) anchor}. This view id must correspond
+ * to an arbitrary descendant of the CoordinatorLayout, but it may not be the anchored child itself
+ * or a descendant of the anchored child. This can be used to place floating views relative to
+ * other arbitrary content panes.</p>
+ */
+public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent {
+    static final String TAG = "CoordinatorLayout";
+    static final String WIDGET_PACKAGE_NAME = CoordinatorLayout.class.getPackage().getName();
+
+    interface CoordinatorLayoutImpl {
+        void offsetDescendantRect(ViewGroup parent, View child, Rect rect);
+    }
+
+    static class CoordinatorLayoutImplBase implements CoordinatorLayoutImpl {
+        @Override
+        public void offsetDescendantRect(ViewGroup parent, View child, Rect rect) {
+            parent.offsetDescendantRectToMyCoords(child, rect);
+        }
+    }
+
+    static class CoordinatorLayoutImplHoneycomb implements CoordinatorLayoutImpl {
+        @Override
+        public void offsetDescendantRect(ViewGroup parent, View child, Rect rect) {
+            CoordinatorLayoutHoneycomb.offsetDescendantRect(parent, child, rect);
+        }
+    }
+
+    static final CoordinatorLayoutImpl IMPL;
+
+    static {
+        final int version = Build.VERSION.SDK_INT;
+        if (version >= 11) {
+            IMPL = new CoordinatorLayoutImplHoneycomb();
+        } else {
+            IMPL = new CoordinatorLayoutImplBase();
+        }
+
+        if (version >= 21) {
+            TOP_SORTED_CHILDREN_COMPARATOR = new ViewElevationComparator();
+        } else {
+            TOP_SORTED_CHILDREN_COMPARATOR = null;
+        }
+    }
+
+    static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] {
+            Context.class,
+            AttributeSet.class
+    };
+
+    static final ThreadLocal<Map<String, Constructor<Behavior>>> sConstructors =
+            new ThreadLocal<>();
+
+    final Comparator<View> mLayoutDependencyComparator = new Comparator<View>() {
+        @Override
+        public int compare(View lhs, View rhs) {
+            if (lhs == rhs) {
+                return 0;
+            } else if (((LayoutParams) lhs.getLayoutParams()).dependsOn(
+                    CoordinatorLayout.this, lhs, rhs)) {
+                return 1;
+            } else if (((LayoutParams) rhs.getLayoutParams()).dependsOn(
+                    CoordinatorLayout.this, rhs, lhs)) {
+                return -1;
+            } else {
+                return 0;
+            }
+        }
+    };
+
+    static final Comparator<View> TOP_SORTED_CHILDREN_COMPARATOR;
+
+    private final List<View> mDependencySortedChildren = new ArrayList<View>();
+    private final List<View> mTempList = new ArrayList<>();
+    private final Rect mTempRect1 = new Rect();
+    private final Rect mTempRect2 = new Rect();
+    private final Rect mTempRect3 = new Rect();
+    private final int[] mTempIntPair = new int[2];
+    private Paint mScrimPaint;
+
+    private boolean mIsAttachedToWindow;
+
+    private int[] mKeylines;
+
+    private View mBehaviorTouchView;
+    private View mNestedScrollingDirectChild;
+    private View mNestedScrollingTarget;
+
+    private OnPreDrawListener mOnPreDrawListener;
+    private boolean mNeedsPreDrawListener;
+
+    private final NestedScrollingParentHelper mNestedScrollingParentHelper =
+            new NestedScrollingParentHelper(this);
+
+    public CoordinatorLayout(Context context) {
+        this(context, null);
+    }
+
+    public CoordinatorLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CoordinatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CoordinatorLayout,
+                defStyleAttr, 0);
+        final int keylineArrayRes = a.getResourceId(R.styleable.CoordinatorLayout_keylines, 0);
+        if (keylineArrayRes != 0) {
+            final Resources res = context.getResources();
+            mKeylines = res.getIntArray(keylineArrayRes);
+            final float density = res.getDisplayMetrics().density;
+            final int count = mKeylines.length;
+            for (int i = 0; i < count; i++) {
+                mKeylines[i] *= density;
+            }
+        }
+        a.recycle();
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        resetTouchBehaviors();
+        if (mNeedsPreDrawListener) {
+            if (mOnPreDrawListener == null) {
+                mOnPreDrawListener = new OnPreDrawListener();
+            }
+            final ViewTreeObserver vto = getViewTreeObserver();
+            vto.addOnPreDrawListener(mOnPreDrawListener);
+        }
+        mIsAttachedToWindow = true;
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        resetTouchBehaviors();
+        if (mNeedsPreDrawListener && mOnPreDrawListener != null) {
+            final ViewTreeObserver vto = getViewTreeObserver();
+            vto.removeOnPreDrawListener(mOnPreDrawListener);
+        }
+        if (mNestedScrollingTarget != null) {
+            onStopNestedScroll(mNestedScrollingTarget);
+        }
+        mIsAttachedToWindow = false;
+    }
+
+    /**
+     * Reset all Behavior-related tracking records either to clean up or in preparation
+     * for a new event stream. This should be called when attached or detached from a window,
+     * in response to an UP or CANCEL event, when intercept is request-disallowed
+     * and similar cases where an event stream in progress will be aborted.
+     */
+    private void resetTouchBehaviors() {
+        if (mBehaviorTouchView != null) {
+            final Behavior b = ((LayoutParams) mBehaviorTouchView.getLayoutParams()).getBehavior();
+            if (b != null) {
+                final long now = SystemClock.uptimeMillis();
+                final MotionEvent cancelEvent = MotionEvent.obtain(now, now,
+                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+                b.onTouchEvent(this, mBehaviorTouchView, cancelEvent);
+                cancelEvent.recycle();
+            }
+            mBehaviorTouchView = null;
+        }
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            lp.resetTouchBehaviorTracking();
+        }
+    }
+
+    /**
+     * Populate a list with the current child views, sorted such that the topmost views
+     * in z-order are at the front of the list. Useful for hit testing and event dispatch.
+     */
+    private void getTopSortedChildren(List<View> out) {
+        out.clear();
+
+        final boolean useCustomOrder = isChildrenDrawingOrderEnabled();
+        final int childCount = getChildCount();
+        for (int i = childCount - 1; i >= 0; i--) {
+            final int childIndex = useCustomOrder ? getChildDrawingOrder(childCount, i) : i;
+            final View child = getChildAt(childIndex);
+            out.add(child);
+        }
+
+        if (TOP_SORTED_CHILDREN_COMPARATOR != null) {
+            Collections.sort(out, TOP_SORTED_CHILDREN_COMPARATOR);
+        }
+    }
+
+    private boolean performIntercept(MotionEvent ev) {
+        boolean intercepted = false;
+        boolean newBlock = false;
+
+        MotionEvent cancelEvent = null;
+
+        final int action = ev.getActionMasked();
+
+        final List<View> topmostChildList = mTempList;
+        getTopSortedChildren(topmostChildList);
+
+        // Let topmost child views inspect first
+        final int childCount = topmostChildList.size();
+        for (int i = 0; i < childCount; i++) {
+            final View child = topmostChildList.get(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            final Behavior b = lp.getBehavior();
+
+            if ((intercepted || newBlock) && action != MotionEvent.ACTION_DOWN) {
+                // Cancel all behaviors beneath the one that intercepted.
+                // If the event is "down" then we don't have anything to cancel yet.
+                if (b != null) {
+                    if (cancelEvent != null) {
+                        final long now = SystemClock.uptimeMillis();
+                        cancelEvent = MotionEvent.obtain(now, now,
+                                MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+                    }
+                    b.onInterceptTouchEvent(this, child, cancelEvent);
+                }
+                continue;
+            }
+
+            if (!intercepted && b != null
+                    && (intercepted = b.onInterceptTouchEvent(this, child, ev))) {
+                mBehaviorTouchView = child;
+            }
+
+            // Don't keep going if we're not allowing interaction below this.
+            // Setting newBlock will make sure we cancel the rest of the behaviors.
+            final boolean wasBlocking = lp.didBlockInteraction();
+            final boolean isBlocking = lp.isBlockingInteractionBelow(this, child);
+            newBlock = isBlocking && !wasBlocking;
+            if (isBlocking && !newBlock) {
+                // Stop here since we don't have anything more to cancel - we already did
+                // when the behavior first started blocking things below this point.
+                break;
+            }
+        }
+
+        topmostChildList.clear();
+
+        return intercepted;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        MotionEvent cancelEvent = null;
+
+        final int action = ev.getActionMasked();
+
+        // Make sure we reset in case we had missed a previous important event.
+        if (action == MotionEvent.ACTION_DOWN) {
+            resetTouchBehaviors();
+        }
+
+        final boolean intercepted = performIntercept(ev);
+
+        if (cancelEvent != null) {
+            cancelEvent.recycle();
+        }
+
+        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+            resetTouchBehaviors();
+        }
+
+        return intercepted;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean handled = false;
+        boolean cancelSuper = false;
+        MotionEvent cancelEvent = null;
+
+        final int action = ev.getActionMasked();
+
+        if (mBehaviorTouchView != null || (cancelSuper = performIntercept(ev))) {
+            // Safe since performIntercept guarantees that
+            // mBehaviorTouchView != null if it returns true
+            final LayoutParams lp = (LayoutParams) mBehaviorTouchView.getLayoutParams();
+            final Behavior b = lp.getBehavior();
+            if (b != null) {
+                b.onTouchEvent(this, mBehaviorTouchView, ev);
+            }
+        }
+
+        // Keep the super implementation correct
+        if (mBehaviorTouchView == null) {
+            handled |= super.onTouchEvent(ev);
+        } else if (cancelSuper) {
+            if (cancelEvent != null) {
+                final long now = SystemClock.uptimeMillis();
+                cancelEvent = MotionEvent.obtain(now, now,
+                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+            }
+            super.onTouchEvent(cancelEvent);
+        }
+
+        if (!handled && action == MotionEvent.ACTION_DOWN) {
+
+        }
+
+        if (cancelEvent != null) {
+            cancelEvent.recycle();
+        }
+
+        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+            resetTouchBehaviors();
+        }
+
+        return handled;
+    }
+
+    @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
+        if (disallowIntercept) {
+            resetTouchBehaviors();
+        }
+    }
+
+    private int getKeyline(int index) {
+        if (mKeylines == null) {
+            Log.e(TAG, "No keylines defined for " + this + " - attempted index lookup " + index);
+            return 0;
+        }
+
+        if (index < 0 || index >= mKeylines.length) {
+            Log.e(TAG, "Keyline index " + index + " out of range for " + this);
+            return 0;
+        }
+
+        return mKeylines[index];
+    }
+
+    static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
+        if (TextUtils.isEmpty(name)) {
+            return null;
+        }
+
+        final String fullName;
+        if (name.startsWith(".")) {
+            // Relative to the app package. Prepend the app package name.
+            fullName = context.getPackageName() + name;
+        } else if (name.indexOf('.') >= 0) {
+            // Fully qualified package name.
+            fullName = name;
+        } else {
+            // Assume stock behavior in this package.
+            fullName = WIDGET_PACKAGE_NAME + '.' + name;
+        }
+
+        try {
+            Map<String, Constructor<Behavior>> constructors = sConstructors.get();
+            if (constructors == null) {
+                constructors = new HashMap<>();
+                sConstructors.set(constructors);
+            }
+            Constructor<Behavior> c = constructors.get(fullName);
+            if (c == null) {
+                final Class<Behavior> clazz = (Class<Behavior>) Class.forName(fullName, true,
+                        context.getClassLoader());
+                c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
+                constructors.put(fullName, c);
+            }
+            return c.newInstance(context, attrs);
+        } catch (Exception e) {
+            throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
+        }
+    }
+
+    LayoutParams getResolvedLayoutParams(View child) {
+        final LayoutParams result = (LayoutParams) child.getLayoutParams();
+        if (!result.mBehaviorResolved) {
+            final Class<?> childClass = child.getClass();
+            final DefaultBehavior defaultBehavior = childClass.getAnnotation(DefaultBehavior.class);
+            if (defaultBehavior != null) {
+                try {
+                    result.setBehavior(defaultBehavior.value().newInstance());
+                } catch (Exception e) {
+                    Log.e(TAG, "Default behavior class " + defaultBehavior.value().getName() +
+                            " could not be instantiated. Did you forget a default constructor?", e);
+                }
+            }
+            result.mBehaviorResolved = true;
+        }
+        return result;
+    }
+
+    private void prepareChildren() {
+        final int childCount = getChildCount();
+
+        boolean resortRequired = mDependencySortedChildren.size() != childCount;
+
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = getResolvedLayoutParams(child);
+            if (!resortRequired && lp.isDirty(this, child)) {
+                resortRequired = true;
+            }
+            lp.findAnchorView(this, child);
+        }
+
+        if (resortRequired) {
+            mDependencySortedChildren.clear();
+            for (int i = 0; i < childCount; i++) {
+                mDependencySortedChildren.add(getChildAt(i));
+            }
+            Collections.sort(mDependencySortedChildren, mLayoutDependencyComparator);
+        }
+    }
+
+    /**
+     * This is a port of the common
+     * {@link ViewGroup#offsetDescendantRectToMyCoords(android.view.View, android.graphics.Rect)}
+     * from the framework, but adapted to take transformations into account. The result
+     * will be the bounding rect of the real transformed rect.
+     *
+     * @param descendant view defining the original coordinate system of rect
+     * @param rect (in/out) the rect to offset from descendant to this view's coordinate system
+     */
+    void offsetDescendantRect(View descendant, Rect rect) {
+        IMPL.offsetDescendantRect(this, descendant, rect);
+    }
+
+    /**
+     * Retrieve the transformed bounding rect of an arbitrary descendant view.
+     * This does not need to be a direct child.
+     *
+     * @param descendant descendant view to reference
+     * @param out rect to set to the bounds of the descendant view
+     */
+    void getDescendantRect(View descendant, Rect out) {
+        out.set(0, 0, descendant.getWidth(), descendant.getHeight());
+        offsetDescendantRect(descendant, out);
+    }
+
+    @Override
+    protected int getSuggestedMinimumWidth() {
+        return Math.max(super.getSuggestedMinimumWidth(), getPaddingLeft() + getPaddingRight());
+    }
+
+    @Override
+    protected int getSuggestedMinimumHeight() {
+        return Math.max(super.getSuggestedMinimumHeight(), getPaddingTop() + getPaddingBottom());
+    }
+
+    /**
+     * Called to measure each individual child view unless a
+     * {@link CoordinatorLayout.Behavior Behavior} is present. The Behavior may choose to delegate
+     * child measurement to this method.
+     *
+     * @param child the child to measure
+     * @param parentWidthMeasureSpec the width requirements for this view
+     * @param widthUsed extra space that has been used up by the parent
+     *        horizontally (possibly by other children of the parent)
+     * @param parentHeightMeasureSpec the height requirements for this view
+     * @param heightUsed extra space that has been used up by the parent
+     *        vertically (possibly by other children of the parent)
+     */
+    public void onMeasureChild(View child, int parentWidthMeasureSpec, int widthUsed,
+            int parentHeightMeasureSpec, int heightUsed) {
+        measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
+                parentHeightMeasureSpec, heightUsed);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        prepareChildren();
+        ensurePreDrawListener();
+
+        final int paddingLeft = getPaddingLeft();
+        final int paddingTop = getPaddingTop();
+        final int paddingRight = getPaddingRight();
+        final int paddingBottom = getPaddingBottom();
+        final int layoutDirection = ViewCompat.getLayoutDirection(this);
+        final boolean isRtl = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL;
+        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+
+        final int widthPadding = paddingLeft + paddingRight;
+        final int heightPadding = paddingTop + paddingBottom;
+        int widthUsed = getSuggestedMinimumWidth();
+        int heightUsed = getSuggestedMinimumHeight();
+        int childState = 0;
+
+        final int childCount = mDependencySortedChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            final View child = mDependencySortedChildren.get(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            int keylineWidthUsed = 0;
+            if (lp.keyline >= 0 && widthMode != MeasureSpec.UNSPECIFIED) {
+                final int keylinePos = getKeyline(lp.keyline);
+                final int keylineGravity = GravityCompat.getAbsoluteGravity(
+                        resolveKeylineGravity(lp.gravity), layoutDirection)
+                        & Gravity.HORIZONTAL_GRAVITY_MASK;
+                if ((keylineGravity == Gravity.LEFT && !isRtl)
+                        || (keylineGravity == Gravity.RIGHT && isRtl)) {
+                    keylineWidthUsed = Math.max(0, widthSize - paddingRight - keylinePos);
+                } else if ((keylineGravity == Gravity.RIGHT && !isRtl)
+                        || (keylineGravity == Gravity.LEFT && isRtl)) {
+                    keylineWidthUsed = Math.max(0, keylinePos - paddingLeft);
+                }
+            }
+
+            final Behavior b = lp.getBehavior();
+            if (b == null || !b.onMeasureChild(this, child, widthMeasureSpec, keylineWidthUsed,
+                    heightMeasureSpec, 0)) {
+                onMeasureChild(child, widthMeasureSpec, keylineWidthUsed,
+                        heightMeasureSpec, 0);
+            }
+
+            widthUsed = Math.max(widthUsed, widthPadding + child.getMeasuredWidth() +
+                    lp.leftMargin + lp.rightMargin);
+
+            heightUsed = Math.max(heightUsed, heightPadding + child.getMeasuredHeight() +
+                    lp.topMargin + lp.bottomMargin);
+            childState = ViewCompat.combineMeasuredStates(childState,
+                    ViewCompat.getMeasuredState(child));
+        }
+
+        final int width = ViewCompat.resolveSizeAndState(widthUsed, widthMeasureSpec,
+                childState & ViewCompat.MEASURED_STATE_MASK);
+        final int height = ViewCompat.resolveSizeAndState(heightUsed, heightMeasureSpec,
+                childState << ViewCompat.MEASURED_HEIGHT_STATE_SHIFT);
+        setMeasuredDimension(width, height);
+    }
+
+    /**
+     * Called to lay out each individual child view unless a
+     * {@link CoordinatorLayout.Behavior Behavior} is present. The Behavior may choose to
+     * delegate child measurement to this method.
+     *
+     * @param child child view to lay out
+     * @param layoutDirection the resolved layout direction for the CoordinatorLayout, such as
+     *                        {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
+     *                        {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
+     */
+    public void onLayoutChild(View child, int layoutDirection) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        if (lp.checkAnchorChanged()) {
+            throw new IllegalStateException("An anchor may not be changed after CoordinatorLayout"
+                    + " measurement begins before layout is complete.");
+        }
+        if (lp.mAnchorView != null) {
+            layoutChildWithAnchor(child, lp.mAnchorView, layoutDirection);
+        } else if (lp.keyline >= 0) {
+            layoutChildWithKeyline(child, lp.keyline, layoutDirection);
+        } else {
+            layoutChild(child, layoutDirection);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        final int layoutDirection = ViewCompat.getLayoutDirection(this);
+        final int childCount = mDependencySortedChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            final View child = mDependencySortedChildren.get(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            final Behavior behavior = lp.getBehavior();
+
+            if (behavior == null || !behavior.onLayoutChild(this, child, layoutDirection)) {
+                onLayoutChild(child, layoutDirection);
+            }
+        }
+    }
+
+    /**
+     * Mark the last known child position rect for the given child view.
+     * This will be used when checking if a child view's position has changed between frames.
+     * The rect used here should be one returned by
+     * {@link #getChildRect(android.view.View, boolean, android.graphics.Rect)}, with translation
+     * disabled.
+     *
+     * @param child child view to set for
+     * @param r rect to set
+     */
+    void recordLastChildRect(View child, Rect r) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        lp.setLastChildRect(r);
+    }
+
+    /**
+     * Get the last known child rect recorded by
+     * {@link #recordLastChildRect(android.view.View, android.graphics.Rect)}.
+     *
+     * @param child child view to retrieve from
+     * @param out rect to set to the outpur values
+     */
+    void getLastChildRect(View child, Rect out) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        out.set(lp.getLastChildRect());
+    }
+
+    /**
+     * Get the position rect for the given child. If the child has currently requested layout
+     * or has a visibility of GONE.
+     *
+     * @param child child view to check
+     * @param transform true to include transformation in the output rect, false to
+     *                        only account for the base position
+     * @param out rect to set to the output values
+     */
+    void getChildRect(View child, boolean transform, Rect out) {
+        if (child.isLayoutRequested() || child.getVisibility() == View.GONE) {
+            out.set(0, 0, 0, 0);
+            return;
+        }
+        if (transform) {
+            getDescendantRect(child, out);
+        } else {
+            out.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
+        }
+    }
+
+    /**
+     * Calculate the desired child rect relative to an anchor rect, respecting both
+     * gravity and anchorGravity.
+     *
+     * @param child child view to calculate a rect for
+     * @param layoutDirection the desired layout direction for the CoordinatorLayout
+     * @param anchorRect rect in CoordinatorLayout coordinates of the anchor view area
+     * @param out rect to set to the output values
+     */
+    void getDesiredAnchoredChildRect(View child, int layoutDirection, Rect anchorRect, Rect out) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        final int absGravity = GravityCompat.getAbsoluteGravity(
+                resolveAnchoredChildGravity(lp.gravity), layoutDirection);
+        final int absAnchorGravity = GravityCompat.getAbsoluteGravity(
+                resolveGravity(lp.anchorGravity),
+                layoutDirection);
+
+        final int hgrav = absGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+        final int vgrav = absGravity & Gravity.VERTICAL_GRAVITY_MASK;
+        final int anchorHgrav = absAnchorGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+        final int anchorVgrav = absAnchorGravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+        final int childWidth = child.getMeasuredWidth();
+        final int childHeight = child.getMeasuredHeight();
+
+        int left;
+        int top;
+
+        // Align to the anchor
+        switch (anchorHgrav) {
+            default:
+            case Gravity.LEFT:
+                left = anchorRect.left;
+                break;
+            case Gravity.RIGHT:
+                left = anchorRect.right - childWidth;
+                break;
+            case Gravity.CENTER_HORIZONTAL:
+                left = anchorRect.left + (anchorRect.width() - childWidth) / 2;
+                break;
+        }
+
+        switch (anchorVgrav) {
+            default:
+            case Gravity.TOP:
+                top = anchorRect.top;
+                break;
+            case Gravity.BOTTOM:
+                top = anchorRect.bottom - childHeight;
+                break;
+            case Gravity.CENTER_VERTICAL:
+                top = anchorRect.top + (anchorRect.height() - childHeight) / 2;
+                break;
+        }
+
+        // Offset by the child view's gravity itself
+        switch (hgrav) {
+            default:
+            case Gravity.LEFT:
+                // Do nothing, we're already in position.
+                break;
+            case Gravity.RIGHT:
+                left += childWidth;
+                break;
+            case Gravity.CENTER_HORIZONTAL:
+                left += childWidth / 2;
+                break;
+        }
+
+        switch (vgrav) {
+            default:
+            case Gravity.TOP:
+                // Do nothing, we're already in position.
+                break;
+            case Gravity.BOTTOM:
+                top += childHeight;
+                break;
+            case Gravity.CENTER_VERTICAL:
+                top += childHeight / 2;
+                break;
+        }
+
+        final int width = getWidth();
+        final int height = getHeight();
+
+        // Obey margins and padding
+        left = Math.max(getPaddingLeft() + lp.leftMargin,
+                Math.min(left,
+                        width - getPaddingRight() - childWidth - lp.rightMargin));
+        top = Math.max(getPaddingTop() + lp.topMargin,
+                Math.min(top,
+                        height - getPaddingBottom() - childHeight - lp.bottomMargin));
+
+        out.set(left, top, left + childWidth, top + childHeight);
+    }
+
+    /**
+     * CORE ASSUMPTION: anchor has been laid out by the time this is called for a given child view.
+     *
+     * @param child child to lay out
+     * @param anchor view to anchor child relative to; already laid out.
+     * @param layoutDirection ViewCompat constant for layout direction
+     */
+    private void layoutChildWithAnchor(View child, View anchor, int layoutDirection) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+        final Rect anchorRect = mTempRect1;
+        final Rect childRect = mTempRect2;
+        getDescendantRect(anchor, anchorRect);
+        getDesiredAnchoredChildRect(child, layoutDirection, anchorRect, childRect);
+
+        child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
+    }
+
+    /**
+     * Lay out a child view with respect to a keyline.
+     *
+     * <p>The keyline represents a horizontal offset from the unpadded starting edge of
+     * the CoordinatorLayout. The child's gravity will affect how it is positioned with
+     * respect to the keyline.</p>
+     *
+     * @param child child to lay out
+     * @param keyline offset from the starting edge in pixels of the keyline to align with
+     * @param layoutDirection ViewCompat constant for layout direction
+     */
+    private void layoutChildWithKeyline(View child, int keyline, int layoutDirection) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        final int absGravity = GravityCompat.getAbsoluteGravity(
+                resolveKeylineGravity(lp.gravity), layoutDirection);
+
+        final int hgrav = absGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+        final int vgrav = absGravity & Gravity.VERTICAL_GRAVITY_MASK;
+        final int width = getWidth();
+        final int height = getHeight();
+        final int childWidth = child.getMeasuredWidth();
+        final int childHeight = child.getMeasuredHeight();
+
+        if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
+            keyline = width - keyline;
+        }
+
+        int left = getKeyline(keyline) - childWidth;
+        int top = 0;
+
+        switch (hgrav) {
+            default:
+            case Gravity.LEFT:
+                // Nothing to do.
+                break;
+            case Gravity.RIGHT:
+                left += childWidth;
+                break;
+            case Gravity.CENTER_HORIZONTAL:
+                left += childWidth / 2;
+                break;
+        }
+
+        switch (vgrav) {
+            default:
+            case Gravity.TOP:
+                // Do nothing, we're already in position.
+                break;
+            case Gravity.BOTTOM:
+                top += childHeight;
+                break;
+            case Gravity.CENTER_VERTICAL:
+                top += childHeight / 2;
+                break;
+        }
+
+        // Obey margins and padding
+        left = Math.max(getPaddingLeft() + lp.leftMargin,
+                Math.min(left,
+                        width - getPaddingRight() - childWidth - lp.rightMargin));
+        top = Math.max(getPaddingTop() + lp.topMargin,
+                Math.min(top,
+                        height - getPaddingBottom() - childHeight - lp.bottomMargin));
+
+        child.layout(left, top, left + childWidth, top + childHeight);
+    }
+
+    /**
+     * Lay out a child view with no special handling. This will position the child as
+     * if it were within a FrameLayout or similar simple frame.
+     *
+     * @param child child view to lay out
+     * @param layoutDirection ViewCompat constant for the desired layout direction
+     */
+    private void layoutChild(View child, int layoutDirection) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        final Rect parent = mTempRect1;
+        parent.set(getPaddingLeft() + lp.leftMargin,
+                getPaddingTop() + lp.topMargin,
+                getWidth() - getPaddingRight() - lp.rightMargin,
+                getHeight() - getPaddingBottom() - lp.bottomMargin);
+        final Rect out = mTempRect2;
+        GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
+                child.getMeasuredHeight(), parent, out, layoutDirection);
+        child.layout(out.left, out.top, out.right, out.bottom);
+    }
+
+    /**
+     * Return the given gravity value or the default if the passed value is NO_GRAVITY.
+     * This should be used for children that are not anchored to another view or a keyline.
+     */
+    private static int resolveGravity(int gravity) {
+        return gravity == Gravity.NO_GRAVITY ? GravityCompat.START | Gravity.TOP : gravity;
+    }
+
+    /**
+     * Return the given gravity value or the default if the passed value is NO_GRAVITY.
+     * This should be used for children that are positioned relative to a keyline.
+     */
+    private static int resolveKeylineGravity(int gravity) {
+        return gravity == Gravity.NO_GRAVITY ? GravityCompat.END | Gravity.TOP : gravity;
+    }
+
+    /**
+     * Return the given gravity value or the default if the passed value is NO_GRAVITY.
+     * This should be used for children that are anchored to another view.
+     */
+    private static int resolveAnchoredChildGravity(int gravity) {
+        return gravity == Gravity.NO_GRAVITY ? Gravity.CENTER : gravity;
+    }
+
+    @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        if (lp.mBehavior != null && lp.mBehavior.getScrimOpacity(this, child) > 0.f) {
+            if (mScrimPaint == null) {
+                mScrimPaint = new Paint();
+            }
+            mScrimPaint.setColor(lp.mBehavior.getScrimColor(this, child));
+
+            // TODO: Set the clip appropriately to avoid unnecessary overdraw.
+            canvas.drawRect(getPaddingLeft(), getPaddingTop(),
+                    getWidth() - getPaddingRight(), getHeight() - getPaddingBottom(), mScrimPaint);
+        }
+        return super.drawChild(canvas, child, drawingTime);
+    }
+
+    /**
+     * Runs as part of the pre-draw step when at least one child view has a reported
+     * dependency on another view. This allows CoordinatorLayout to account for layout
+     * changes and animations that occur outside of the normal layout pass.
+     *
+     * The offsetting behavior implemented here does not store the computed offset in
+     * the LayoutParams; instead it expects that the layout process will always reconstruct
+     * the proper positioning.
+     */
+    void performPreDraw() {
+        final int layoutDirection = ViewCompat.getLayoutDirection(this);
+        final int childCount = mDependencySortedChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            final View child = mDependencySortedChildren.get(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            // Check child views before for anchor
+            for (int j = 0; j < i; j++) {
+                final View checkChild = mDependencySortedChildren.get(j);
+
+                if (lp.mAnchorDirectChild == checkChild) {
+                    offsetChildToAnchor(child, layoutDirection);
+                }
+            }
+
+            // Did it change? if not continue
+            final Rect oldRect = mTempRect1;
+            final Rect newRect = mTempRect2;
+            getLastChildRect(child, oldRect);
+            getChildRect(child, true, newRect);
+            if (oldRect.equals(newRect)) {
+                continue;
+            }
+            recordLastChildRect(child, newRect);
+
+            // Update any behavior-dependent views for the change
+            for (int j = i + 1; j < childCount; j++) {
+                final View checkChild = mDependencySortedChildren.get(j);
+                final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
+                final Behavior b = checkLp.getBehavior();
+
+                if (b != null && b.layoutDependsOn(this, checkChild, child)) {
+                    b.onDependentViewChanged(this, checkChild, child);
+                }
+            }
+        }
+    }
+
+    /**
+     * Add or remove the pre-draw listener as necessary.
+     */
+    void ensurePreDrawListener() {
+        boolean hasDependencies = false;
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            if (hasDependencies(child)) {
+                hasDependencies = true;
+                break;
+            }
+        }
+
+        if (hasDependencies != mNeedsPreDrawListener) {
+            if (hasDependencies) {
+                addPreDrawListener();
+            } else {
+                removePreDrawListener();
+            }
+        }
+    }
+
+    /**
+     * Check if the given child has any layout dependencies on other child views.
+     */
+    boolean hasDependencies(View child) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        if (lp.mAnchorView != null) {
+            return true;
+        }
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View other = getChildAt(i);
+            if (other == child) {
+                continue;
+            }
+            if (lp.dependsOn(this, child, other)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Add the pre-draw listener if we're attached to a window and mark that we currently
+     * need it when attached.
+     */
+    void addPreDrawListener() {
+        if (mIsAttachedToWindow) {
+            // Add the listener
+            if (mOnPreDrawListener == null) {
+                mOnPreDrawListener = new OnPreDrawListener();
+            }
+            final ViewTreeObserver vto = getViewTreeObserver();
+            vto.addOnPreDrawListener(mOnPreDrawListener);
+        }
+
+        // Record that we need the listener regardless of whether or not we're attached.
+        // We'll add the real listener when we become attached.
+        mNeedsPreDrawListener = true;
+    }
+
+    /**
+     * Remove the pre-draw listener if we're attached to a window and mark that we currently
+     * do not need it when attached.
+     */
+    void removePreDrawListener() {
+        if (mIsAttachedToWindow) {
+            if (mOnPreDrawListener != null) {
+                final ViewTreeObserver vto = getViewTreeObserver();
+                vto.removeOnPreDrawListener(mOnPreDrawListener);
+            }
+        }
+        mNeedsPreDrawListener = false;
+    }
+
+    /**
+     * Adjust the child left, top, right, bottom rect to the correct anchor view position,
+     * respecting gravity and anchor gravity.
+     *
+     * Note that child translation properties are ignored in this process, allowing children
+     * to be animated away from their anchor. However, if the anchor view is animated,
+     * the child will be offset to match the anchor's translated position.
+     */
+    void offsetChildToAnchor(View child, int layoutDirection) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        if (lp.mAnchorView != null) {
+            final Rect anchorRect = mTempRect1;
+            final Rect childRect = mTempRect2;
+            final Rect desiredChildRect = mTempRect3;
+
+            getDescendantRect(lp.mAnchorView, anchorRect);
+            getChildRect(child, false, childRect);
+            getDesiredAnchoredChildRect(child, layoutDirection, anchorRect, desiredChildRect);
+
+            final int dx = desiredChildRect.left - childRect.left;
+            final int dy = desiredChildRect.top - childRect.top;
+
+            if (dx != 0) {
+                child.offsetLeftAndRight(dx);
+            }
+            if (dy != 0) {
+                child.offsetTopAndBottom(dy);
+            }
+        }
+    }
+
+    /**
+     * Check if a given point in the CoordinatorLayout's coordinates are within the view bounds
+     * of the given direct child view.
+     *
+     * @param child child view to test
+     * @param x X coordinate to test, in the CoordinatorLayout's coordinate system
+     * @param y Y coordinate to test, in the CoordinatorLayout's coordinate system
+     * @return true if the point is within the child view's bounds, false otherwise
+     */
+    public boolean isPointInChildBounds(View child, int x, int y) {
+        final Rect r = mTempRect1;
+        getDescendantRect(child, r);
+        return r.contains(x, y);
+    }
+
+    /**
+     * Check whether two views overlap each other. The views need to be descendants of this
+     * {@link CoordinatorLayout} in the view hierarchy.
+     *
+     * @param first first child view to test
+     * @param second second child view to test
+     * @return true if both views are visible and overlap each other
+     */
+    public boolean doViewsOverlap(View first, View second) {
+        if (first.getVisibility() == VISIBLE && second.getVisibility() == VISIBLE) {
+            final Rect firstRect = mTempRect1;
+            getChildRect(first, first.getParent() != this, firstRect);
+            final Rect secondRect = mTempRect2;
+            getChildRect(second, second.getParent() != this, secondRect);
+
+            return !(firstRect.left > secondRect.right || firstRect.top > secondRect.bottom
+                    || firstRect.right < secondRect.left || firstRect.bottom < secondRect.top);
+        }
+        return false;
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        if (p instanceof LayoutParams) {
+            return new LayoutParams((LayoutParams) p);
+        } else if (p instanceof MarginLayoutParams) {
+            return new LayoutParams((MarginLayoutParams) p);
+        }
+        return new LayoutParams(p);
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams && super.checkLayoutParams(p);
+    }
+
+    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+        boolean handled = false;
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View view = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            final Behavior viewBehavior = lp.getBehavior();
+            if (viewBehavior != null) {
+                final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child, target,
+                        nestedScrollAxes);
+                handled |= accepted;
+
+                lp.acceptNestedScroll(accepted);
+            } else {
+                lp.acceptNestedScroll(false);
+            }
+        }
+        return handled;
+    }
+
+    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
+        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
+        mNestedScrollingDirectChild = child;
+        mNestedScrollingTarget = target;
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View view = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            if (!lp.isNestedScrollAccepted()) {
+                continue;
+            }
+
+            final Behavior viewBehavior = lp.getBehavior();
+            if (viewBehavior != null) {
+                viewBehavior.onNestedScrollAccepted(this, view, child, target, nestedScrollAxes);
+            }
+        }
+    }
+
+    public void onStopNestedScroll(View target) {
+        mNestedScrollingParentHelper.onStopNestedScroll(target);
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View view = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            if (!lp.isNestedScrollAccepted()) {
+                continue;
+            }
+
+            final Behavior viewBehavior = lp.getBehavior();
+            if (viewBehavior != null) {
+                viewBehavior.onStopNestedScroll(this, view, target);
+            }
+            lp.resetNestedScroll();
+        }
+
+        mNestedScrollingDirectChild = null;
+        mNestedScrollingTarget = null;
+    }
+
+    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed) {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View view = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            if (!lp.isNestedScrollAccepted()) {
+                continue;
+            }
+
+            final Behavior viewBehavior = lp.getBehavior();
+            if (viewBehavior != null) {
+                viewBehavior.onNestedScroll(this, view, target, dxConsumed, dyConsumed,
+                        dxUnconsumed, dyUnconsumed);
+            }
+        }
+    }
+
+    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
+        int xConsumed = 0;
+        int yConsumed = 0;
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View view = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            if (!lp.isNestedScrollAccepted()) {
+                continue;
+            }
+
+            final Behavior viewBehavior = lp.getBehavior();
+            if (viewBehavior != null) {
+                mTempIntPair[0] = mTempIntPair[1] = 0;
+                viewBehavior.onNestedPreScroll(this, view, target, dx, dy, mTempIntPair);
+
+                xConsumed = dx > 0 ? Math.max(xConsumed, mTempIntPair[0])
+                        : Math.min(xConsumed, mTempIntPair[0]);
+                yConsumed = dy > 0 ? Math.max(yConsumed, mTempIntPair[1])
+                        : Math.min(yConsumed, mTempIntPair[1]);
+            }
+        }
+
+        consumed[0] = xConsumed;
+        consumed[1] = yConsumed;
+    }
+
+    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+        boolean handled = false;
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View view = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            if (!lp.isNestedScrollAccepted()) {
+                continue;
+            }
+
+            final Behavior viewBehavior = lp.getBehavior();
+            if (viewBehavior != null) {
+                handled |= viewBehavior.onNestedFling(this, view, target, velocityX, velocityY,
+                        consumed);
+            }
+        }
+        return handled;
+    }
+
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+        boolean handled = false;
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View view = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            if (!lp.isNestedScrollAccepted()) {
+                continue;
+            }
+
+            final Behavior viewBehavior = lp.getBehavior();
+            if (viewBehavior != null) {
+                handled |= viewBehavior.onNestedPreFling(this, view, target, velocityX, velocityY);
+            }
+        }
+        return handled;
+    }
+
+    public int getNestedScrollAxes() {
+        return mNestedScrollingParentHelper.getNestedScrollAxes();
+    }
+
+    class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
+        @Override
+        public boolean onPreDraw() {
+            performPreDraw();
+            return true;
+        }
+    }
+
+    /**
+     * Sorts child views with higher Z values to the beginning of a collection.
+     */
+    static class ViewElevationComparator implements Comparator<View> {
+        @Override
+        public int compare(View lhs, View rhs) {
+            final float lz = ViewCompat.getZ(lhs);
+            final float rz = ViewCompat.getZ(rhs);
+            if (lz > rz) {
+                return -1;
+            } else if (lz < rz) {
+                return 1;
+            }
+            return 0;
+        }
+    }
+
+    /**
+     * Defines the default {@link Behavior} of a {@link View} class.
+     *
+     * <p>When writing a custom view, use this annotation to define the default behavior
+     * when used as a direct child of an {@link CoordinatorLayout}. The default behavior
+     * can be overridden using {@link LayoutParams#setBehavior}.</p>
+     *
+     * <p>Example: <code>@DefaultBehavior(MyBehavior.class)</code></p>
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface DefaultBehavior {
+        Class<? extends Behavior> value();
+    }
+
+    /**
+     * Interaction behavior plugin for child views of {@link CoordinatorLayout}.
+     *
+     * <p>A Behavior implements one or more interactions that a user can take on a child view.
+     * These interactions may include drags, swipes, flings, or any other gestures.</p>
+     *
+     * @param <V> The View type that this Behavior operates on
+     */
+    public static abstract class Behavior<V extends View> {
+
+        /**
+         * Default constructor for instantiating Behaviors.
+         */
+        public Behavior() {
+        }
+
+        /**
+         * Default constructor for inflating Behaviors from layout. The Behavior will have
+         * the opportunity to parse specially defined layout parameters. These parameters will
+         * appear on the child view tag.
+         *
+         * @param context
+         * @param attrs
+         */
+        public Behavior(Context context, AttributeSet attrs) {
+        }
+
+        /**
+         * Respond to CoordinatorLayout touch events before they are dispatched to child views.
+         *
+         * <p>Behaviors can use this to monitor inbound touch events until one decides to
+         * intercept the rest of the event stream to take an action on its associated child view.
+         * This method will return false until it detects the proper intercept conditions, then
+         * return true once those conditions have occurred.</p>
+         *
+         * <p>Once a Behavior intercepts touch events, the rest of the event stream will
+         * be sent to the {@link #onTouchEvent} method.</p>
+         *
+         * <p>The default implementation of this method always returns false.</p>
+         *
+         * @param parent the parent view currently receiving this touch event
+         * @param child the child view associated with this Behavior
+         * @param ev the MotionEvent describing the touch event being processed
+         * @return true if this Behavior would like to intercept and take over the event stream.
+         *         The default always returns false.
+         */
+        public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
+            return false;
+        }
+
+        /**
+         * Respond to CoordinatorLayout touch events after this Behavior has started
+         * {@link #onInterceptTouchEvent intercepting} them.
+         *
+         * <p>Behaviors may intercept touch events in order to help the CoordinatorLayout
+         * manipulate its child views. For example, a Behavior may allow a user to drag a
+         * UI pane open or closed. This method should perform actual mutations of view
+         * layout state.</p>
+         *
+         * @param parent the parent view currently receiving this touch event
+         * @param child the child view associated with this Behavior
+         * @param ev the MotionEvent describing the touch event being processed
+         * @return true if this Behavior handled this touch event and would like to continue
+         *         receiving events in this stream. The default always returns false.
+         */
+        public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
+            return false;
+        }
+
+        /**
+         * Supply a scrim color that will be painted behind the associated child view.
+         *
+         * <p>A scrim may be used to indicate that the other elements beneath it are not currently
+         * interactive or actionable, drawing user focus and attention to the views above the scrim.
+         * </p>
+         *
+         * <p>The default implementation returns {@link Color#BLACK}.</p>
+         *
+         * @param parent the parent view of the given child
+         * @param child the child view above the scrim
+         * @return the desired scrim color in 0xAARRGGBB format. The default return value is
+         *         {@link Color#BLACK}.
+         * @see #getScrimOpacity(CoordinatorLayout, android.view.View)
+         */
+        public final int getScrimColor(CoordinatorLayout parent, V child) {
+            return Color.BLACK;
+        }
+
+        /**
+         * Determine the current opacity of the scrim behind a given child view
+         *
+         * <p>A scrim may be used to indicate that the other elements beneath it are not currently
+         * interactive or actionable, drawing user focus and attention to the views above the scrim.
+         * </p>
+         *
+         * <p>The default implementation returns 0.0f.</p>
+         *
+         * @param parent the parent view of the given child
+         * @param child the child view above the scrim
+         * @return the desired scrim opacity from 0.0f to 1.0f. The default return value is 0.0f.
+         */
+        public final float getScrimOpacity(CoordinatorLayout parent, V child) {
+            return 0.f;
+        }
+
+        /**
+         * Determine whether interaction with views behind the given child in the child order
+         * should be blocked.
+         *
+         * <p>The default implementation returns true if
+         * {@link #getScrimOpacity(CoordinatorLayout, android.view.View)} would return > 0.0f.</p>
+         *
+         * @param parent the parent view of the given child
+         * @param child the child view to test
+         * @return true if {@link #getScrimOpacity(CoordinatorLayout, android.view.View)} would
+         *         return > 0.0f.
+         */
+        public boolean blocksInteractionBelow(CoordinatorLayout parent, V child) {
+            return getScrimOpacity(parent, child) > 0.f;
+        }
+
+        /**
+         * Determine whether the supplied child view has another specific sibling view as a
+         * layout dependency.
+         *
+         * <p>This method will be called at least once in response to a layout request. If it
+         * returns true for a given child and dependency view pair, the parent CoordinatorLayout
+         * will:</p>
+         * <ol>
+         *     <li>Always lay out this child after the dependent child is laid out, regardless
+         *     of child order.</li>
+         *     <li>Call {@link #onDependentViewChanged} when the dependency view's layout or
+         *     position changes.</li>
+         * </ol>
+         *
+         * @param parent the parent view of the given child
+         * @param child the child view to test
+         * @param dependency the proposed dependency of child
+         * @return true if child's layout depends on the proposed dependency's layout,
+         *         false otherwise
+         *
+         * @see #onDependentViewChanged(CoordinatorLayout, android.view.View, android.view.View)
+         */
+        public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
+            return false;
+        }
+
+        /**
+         * Respond to a change in a child's dependent sibling view
+         *
+         * <p>This method is called whenever a dependent view (as determined by
+         * {@link #layoutDependsOn(CoordinatorLayout, android.view.View, android.view.View)}
+         * changes in size or position outside of the standard layout flow. A Behavior may
+         * use this method to appropriately update the child view in response.</p>
+         *
+         * <p>Note that if a Behavior changes the layout of a child via this method, it should
+         * also be able to reconstruct the correct position in
+         * {@link #onLayoutChild(CoordinatorLayout, android.view.View, int) onLayoutChild}.
+         * <code>onDependentViewChanged</code> will not be called during normal layout since
+         * the layout of each child view will always happen in dependency order.</p>
+         *
+         * <p>If the Behavior changes the child view's size or position, it should return true.
+         * The default implementation returns false.</p>
+         *
+         * @param parent the parent view of the given child
+         * @param child the child view to manipulate
+         * @param dependency the dependent view that changed
+         * @return true if the Behavior changed the child view's size or position, false otherwise
+         */
+        public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
+            return false;
+        }
+
+        /**
+         * Determine whether the given child view should be considered dirty.
+         *
+         * <p>If a property determined by the Behavior such as other dependent views would change,
+         * the Behavior should report a child view as dirty. This will prompt the CoordinatorLayout
+         * to re-query Behavior-determined properties as appropriate.</p>
+         *
+         * @param parent the parent view of the given child
+         * @param child the child view to check
+         * @return true if child is dirty
+         */
+        public boolean isDirty(CoordinatorLayout parent, V child) {
+            return false;
+        }
+
+        /**
+         * Called when the parent CoordinatorLayout is about to measure the given child view.
+         *
+         * <p>This method can be used to perform custom or modified measurement of a child view
+         * in place of the default child measurement behavior. The Behavior's implementation
+         * can delegate to the standard CoordinatorLayout measurement behavior by calling
+         * {@link CoordinatorLayout#onMeasureChild(android.view.View, int, int, int, int)
+         * parent.onMeasureChild}.</p>
+         *
+         * @param parent the parent CoordinatorLayout
+         * @param child the child to measure
+         * @param parentWidthMeasureSpec the width requirements for this view
+         * @param widthUsed extra space that has been used up by the parent
+         *        horizontally (possibly by other children of the parent)
+         * @param parentHeightMeasureSpec the height requirements for this view
+         * @param heightUsed extra space that has been used up by the parent
+         *        vertically (possibly by other children of the parent)
+         * @return true if the Behavior measured the child view, false if the CoordinatorLayout
+         *         should perform its default measurement
+         */
+        public boolean onMeasureChild(CoordinatorLayout parent, V child,
+                int parentWidthMeasureSpec, int widthUsed,
+                int parentHeightMeasureSpec, int heightUsed) {
+            return false;
+        }
+
+        /**
+         * Called when the parent CoordinatorLayout is about the lay out the given child view.
+         *
+         * <p>This method can be used to perform custom or modified layout of a child view
+         * in place of the default child layout behavior. The Behavior's implementation can
+         * delegate to the standard CoordinatorLayout measurement behavior by calling
+         * {@link CoordinatorLayout#onLayoutChild(android.view.View, int)
+         * parent.onMeasureChild}.</p>
+         *
+         * <p>If a Behavior implements
+         * {@link #onDependentViewChanged(CoordinatorLayout, android.view.View, android.view.View)}
+         * to change the position of a view in response to a dependent view changing, it
+         * should also implement <code>onLayoutChild</code> in such a way that respects those
+         * dependent views. <code>onLayoutChild</code> will always be called for a dependent view
+         * <em>after</em> its dependency has been laid out.</p>
+         *
+         * @param parent the parent CoordinatorLayout
+         * @param child child view to lay out
+         * @param layoutDirection the resolved layout direction for the CoordinatorLayout, such as
+         *                        {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
+         *                        {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
+         * @return true if the Behavior performed layout of the child view, false to request
+         *         default layout behavior
+         */
+        public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
+            return false;
+        }
+
+        // Utility methods for accessing child-specific, behavior-modifiable properties.
+
+        /**
+         * Associate a Behavior-specific tag object with the given child view.
+         * This object will be stored with the child view's LayoutParams.
+         *
+         * @param child child view to set tag with
+         * @param tag tag object to set
+         */
+        public static void setTag(View child, Object tag) {
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            lp.mBehaviorTag = tag;
+        }
+
+        /**
+         * Get the behavior-specific tag object with the given child view.
+         * This object is stored with the child view's LayoutParams.
+         *
+         * @param child child view to get tag with
+         * @return the previously stored tag object
+         */
+        public static Object getTag(View child) {
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            return lp.mBehaviorTag;
+        }
+
+
+        /**
+         * Called when a descendant of the CoordinatorLayout attempts to initiate a nested scroll.
+         *
+         * <p>Any Behavior associated with any direct child of the CoordinatorLayout may respond
+         * to this event and return true to indicate that the CoordinatorLayout should act as
+         * a nested scrolling parent for this scroll. Only Behaviors that return true from
+         * this method will receive subsequent nested scroll events.</p>
+         *
+         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
+         *                          associated with
+         * @param child the child view of the CoordinatorLayout this Behavior is associated with
+         * @param directTargetChild the child view of the CoordinatorLayout that either is or
+         *                          contains the target of the nested scroll operation
+         * @param target the descendant view of the CoordinatorLayout initiating the nested scroll
+         * @param nestedScrollAxes the axes that this nested scroll applies to. See
+         *                         {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+         *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}
+         * @return true if the Behavior wishes to accept this nested scroll
+         *
+         * @see NestedScrollingParent#onStartNestedScroll(View, View, int)
+         */
+        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,
+                V child, View directTargetChild, View target, int nestedScrollAxes) {
+            return false;
+        }
+
+        /**
+         * Called when a nested scroll has been accepted by the CoordinatorLayout.
+         *
+         * <p>Any Behavior associated with any direct child of the CoordinatorLayout may elect
+         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
+         * that returned true will receive subsequent nested scroll events for that nested scroll.
+         * </p>
+         *
+         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
+         *                          associated with
+         * @param child the child view of the CoordinatorLayout this Behavior is associated with
+         * @param directTargetChild the child view of the CoordinatorLayout that either is or
+         *                          contains the target of the nested scroll operation
+         * @param target the descendant view of the CoordinatorLayout initiating the nested scroll
+         * @param nestedScrollAxes the axes that this nested scroll applies to. See
+         *                         {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+         *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}
+         *
+         * @see NestedScrollingParent#onNestedScrollAccepted(View, View, int)
+         */
+        public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child,
+                View directTargetChild, View target, int nestedScrollAxes) {
+            // Do nothing
+        }
+
+        /**
+         * Called when a nested scroll has ended.
+         *
+         * <p>Any Behavior associated with any direct child of the CoordinatorLayout may elect
+         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
+         * that returned true will receive subsequent nested scroll events for that nested scroll.
+         * </p>
+         *
+         * <p><code>onStopNestedScroll</code> marks the end of a single nested scroll event
+         * sequence. This is a good place to clean up any state related to the nested scroll.
+         * </p>
+         *
+         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
+         *                          associated with
+         * @param child the child view of the CoordinatorLayout this Behavior is associated with
+         * @param target the descendant view of the CoordinatorLayout that initiated
+         *               the nested scroll
+         *
+         * @see NestedScrollingParent#onStopNestedScroll(View)
+         */
+        public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
+            // Do nothing
+        }
+
+        /**
+         * Called when a nested scroll in progress has updated and the target has scrolled or
+         * attempted to scroll.
+         *
+         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
+         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
+         * that returned true will receive subsequent nested scroll events for that nested scroll.
+         * </p>
+         *
+         * <p><code>onNestedScroll</code> is called each time the nested scroll is updated by the
+         * nested scrolling child, with both consumed and unconsumed components of the scroll
+         * supplied in pixels. <em>Each Behavior responding to the nested scroll will receive the
+         * same values.</em>
+         * </p>
+         *
+         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
+         *                          associated with
+         * @param child the child view of the CoordinatorLayout this Behavior is associated with
+         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
+         * @param dxConsumed horizontal pixels consumed by the target's own scrolling operation
+         * @param dyConsumed vertical pixels consumed by the target's own scrolling operation
+         * @param dxUnconsumed horizontal pixels not consumed by the target's own scrolling
+         *                     operation, but requested by the user
+         * @param dyUnconsumed vertical pixels not consumed by the target's own scrolling operation,
+         *                     but requested by the user
+         *
+         * @see NestedScrollingParent#onNestedScroll(View, int, int, int, int)
+         */
+        public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target,
+                int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
+            // Do nothing
+        }
+
+        /**
+         * Called when a nested scroll in progress is about to update, before the target has
+         * consumed any of the scrolled distance.
+         *
+         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
+         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
+         * that returned true will receive subsequent nested scroll events for that nested scroll.
+         * </p>
+         *
+         * <p><code>onNestedPreScroll</code> is called each time the nested scroll is updated
+         * by the nested scrolling child, before the nested scrolling child has consumed the scroll
+         * distance itself. <em>Each Behavior responding to the nested scroll will receive the
+         * same values.</em> The CoordinatorLayout will report as consumed the maximum number
+         * of pixels in either direction that any Behavior responding to the nested scroll reported
+         * as consumed.</p>
+         *
+         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
+         *                          associated with
+         * @param child the child view of the CoordinatorLayout this Behavior is associated with
+         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
+         * @param dx the raw horizontal number of pixels that the user attempted to scroll
+         * @param dy the raw vertical number of pixels that the user attempted to scroll
+         * @param consumed out parameter. consumed[0] should be set to the distance of dx that
+         *                 was consumed, consumed[1] should be set to the distance of dy that
+         *                 was consumed
+         *
+         * @see NestedScrollingParent#onNestedPreScroll(View, int, int, int[])
+         */
+        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
+                int dx, int dy, int[] consumed) {
+            // Do nothing
+        }
+
+        /**
+         * Called when a nested scrolling child is starting a fling or an action that would
+         * be a fling.
+         *
+         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
+         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
+         * that returned true will receive subsequent nested scroll events for that nested scroll.
+         * </p>
+         *
+         * <p><code>onNestedFling</code> is called when the current nested scrolling child view
+         * detects the proper conditions for a fling. It reports if the child itself consumed
+         * the fling. If it did not, the child is expected to show some sort of overscroll
+         * indication. This method should return true if it consumes the fling, so that a child
+         * that did not itself take an action in response can choose not to show an overfling
+         * indication.</p>
+         *
+         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
+         *                          associated with
+         * @param child the child view of the CoordinatorLayout this Behavior is associated with
+         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
+         * @param velocityX horizontal velocity of the attempted fling
+         * @param velocityY vertical velocity of the attempted fling
+         * @param consumed true if the nested child view consumed the fling
+         * @return true if the Behavior consumed the fling
+         *
+         * @see NestedScrollingParent#onNestedFling(View, float, float, boolean)
+         */
+        public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target,
+                float velocityX, float velocityY, boolean consumed) {
+            return false;
+        }
+
+        /**
+         * Called when a nested scrolling child is about to start a fling.
+         *
+         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
+         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
+         * that returned true will receive subsequent nested scroll events for that nested scroll.
+         * </p>
+         *
+         * <p><code>onNestedPreFling</code> is called when the current nested scrolling child view
+         * detects the proper conditions for a fling, but it has not acted on it yet. A
+         * Behavior can return true to indicate that it consumed the fling. If at least one
+         * Behavior returns true, the fling should not be acted upon by the child.</p>
+         *
+         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
+         *                          associated with
+         * @param child the child view of the CoordinatorLayout this Behavior is associated with
+         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
+         * @param velocityX horizontal velocity of the attempted fling
+         * @param velocityY vertical velocity of the attempted fling
+         * @return true if the Behavior consumed the fling
+         *
+         * @see NestedScrollingParent#onNestedPreFling(View, float, float)
+         */
+        public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,
+                float velocityX, float velocityY) {
+            return false;
+        }
+    }
+
+    /**
+     * Parameters describing the desired layout for a child of a {@link CoordinatorLayout}.
+     */
+    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
+        /**
+         * A {@link Behavior} that the child view should obey.
+         */
+        Behavior mBehavior;
+
+        boolean mBehaviorResolved = false;
+
+        /**
+         * A {@link Gravity} value describing how this child view should lay out.
+         * If an {@link #setAnchorId(int) anchor} is also specified, the gravity describes
+         * how this child view should be positioned relative to its anchored position.
+         */
+        public int gravity = Gravity.NO_GRAVITY;
+
+        /**
+         * A {@link Gravity} value describing which edge of a child view's
+         * {@link #getAnchorId() anchor} view the child should position itself relative to.
+         */
+        public int anchorGravity = Gravity.NO_GRAVITY;
+
+        /**
+         * The index of the horizontal keyline specified to the parent CoordinatorLayout that this
+         * child should align relative to. If an {@link #setAnchorId(int) anchor} is present the
+         * keyline will be ignored.
+         */
+        public int keyline = -1;
+
+        /**
+         * A {@link View#getId() view id} of a descendant view of the CoordinatorLayout that
+         * this child should position relative to.
+         */
+        int mAnchorId = View.NO_ID;
+
+        View mAnchorView;
+        View mAnchorDirectChild;
+
+        private boolean mDidBlockInteraction;
+        private boolean mDidAcceptNestedScroll;
+
+        final Rect mLastChildRect = new Rect();
+
+        Object mBehaviorTag;
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        LayoutParams(Context context, AttributeSet attrs) {
+            super(context, attrs);
+
+            final TypedArray a = context.obtainStyledAttributes(attrs,
+                    R.styleable.CoordinatorLayout_LayoutParams);
+
+            this.gravity = a.getInteger(
+                    R.styleable.CoordinatorLayout_LayoutParams_android_layout_gravity,
+                    Gravity.NO_GRAVITY);
+            mAnchorId = a.getResourceId(R.styleable.CoordinatorLayout_LayoutParams_layout_anchor,
+                    View.NO_ID);
+            this.anchorGravity = a.getInteger(
+                    R.styleable.CoordinatorLayout_LayoutParams_layout_anchorGravity,
+                    Gravity.NO_GRAVITY);
+
+            this.keyline = a.getInteger(R.styleable.CoordinatorLayout_LayoutParams_layout_keyline,
+                    -1);
+
+            mBehaviorResolved = a.hasValue(
+                    R.styleable.CoordinatorLayout_LayoutParams_layout_behavior);
+            if (mBehaviorResolved) {
+                mBehavior = parseBehavior(context, attrs, a.getString(
+                        R.styleable.CoordinatorLayout_LayoutParams_layout_behavior));
+            }
+
+            a.recycle();
+        }
+
+        public LayoutParams(LayoutParams p) {
+            super(p);
+        }
+
+        public LayoutParams(MarginLayoutParams p) {
+            super(p);
+        }
+
+        public LayoutParams(ViewGroup.LayoutParams p) {
+            super(p);
+        }
+
+        /**
+         * Get the id of this view's anchor.
+         *
+         * <p>The view with this id must be a descendant of the CoordinatorLayout containing
+         * the child view this LayoutParams belongs to. It may not be the child view with
+         * this LayoutParams or a descendant of it.</p>
+         *
+         * @return A {@link View#getId() view id} or {@link View#NO_ID} if there is no anchor
+         */
+        public int getAnchorId() {
+            return mAnchorId;
+        }
+
+        /**
+         * Get the id of this view's anchor.
+         *
+         * <p>The view with this id must be a descendant of the CoordinatorLayout containing
+         * the child view this LayoutParams belongs to. It may not be the child view with
+         * this LayoutParams or a descendant of it.</p>
+         *
+         * @param id The {@link View#getId() view id} of the anchor or
+         *           {@link View#NO_ID} if there is no anchor
+         */
+        public void setAnchorId(int id) {
+            invalidateAnchor();
+            mAnchorId = id;
+        }
+
+        /**
+         * Get the behavior governing the layout and interaction of the child view within
+         * a parent CoordinatorLayout.
+         *
+         * @return The current behavior or null if no behavior is specified
+         */
+        public Behavior getBehavior() {
+            return mBehavior;
+        }
+
+        /**
+         * Set the behavior governing the layout and interaction of the child view within
+         * a parent CoordinatorLayout.
+         *
+         * <p>Setting a new behavior will remove any currently associated
+         * {@link Behavior#setTag(android.view.View, Object) Behavior tag}.</p>
+         *
+         * @param behavior The behavior to set or null for no special behavior
+         */
+        public void setBehavior(Behavior behavior) {
+            if (mBehavior != behavior) {
+                mBehavior = behavior;
+                mBehaviorTag = null;
+                mBehaviorResolved = true;
+            }
+        }
+
+        /**
+         * Set the last known position rect for this child view
+         * @param r the rect to set
+         */
+        void setLastChildRect(Rect r) {
+            mLastChildRect.set(r);
+        }
+
+        /**
+         * Get the last known position rect for this child view.
+         * Note: do not mutate the result of this call.
+         */
+        Rect getLastChildRect() {
+            return mLastChildRect;
+        }
+
+        /**
+         * Returns true if the anchor id changed to another valid view id since the anchor view
+         * was resolved.
+         */
+        boolean checkAnchorChanged() {
+            return mAnchorView == null && mAnchorId != View.NO_ID;
+        }
+
+        /**
+         * Returns true if the associated Behavior previously blocked interaction with other views
+         * below the associated child since the touch behavior tracking was last
+         * {@link #resetTouchBehaviorTracking() reset}.
+         *
+         * @see #isBlockingInteractionBelow(CoordinatorLayout, android.view.View)
+         */
+        boolean didBlockInteraction() {
+            if (mBehavior == null) {
+                mDidBlockInteraction = false;
+            }
+            return mDidBlockInteraction;
+        }
+
+        /**
+         * Check if the associated Behavior wants to block interaction below the given child
+         * view. The given child view should be the child this LayoutParams is associated with.
+         *
+         * <p>Once interaction is blocked, it will remain blocked until touch interaction tracking
+         * is {@link #resetTouchBehaviorTracking() reset}.</p>
+         *
+         * @param parent the parent CoordinatorLayout
+         * @param child the child view this LayoutParams is associated with
+         * @return true to block interaction below the given child
+         */
+        boolean isBlockingInteractionBelow(CoordinatorLayout parent, View child) {
+            if (mDidBlockInteraction) {
+                return true;
+            }
+
+            return mDidBlockInteraction |= mBehavior != null
+                    ? mBehavior.blocksInteractionBelow(parent, child)
+                    : false;
+        }
+
+        /**
+         * Reset tracking of Behavior-specific touch interactions. This includes
+         * interaction blocking.
+         *
+         * @see #isBlockingInteractionBelow(CoordinatorLayout, android.view.View)
+         * @see #didBlockInteraction()
+         */
+        void resetTouchBehaviorTracking() {
+            mDidBlockInteraction = false;
+        }
+
+        void resetNestedScroll() {
+            mDidAcceptNestedScroll = false;
+        }
+
+        void acceptNestedScroll(boolean accept) {
+            mDidAcceptNestedScroll = accept;
+        }
+
+        boolean isNestedScrollAccepted() {
+            return mDidAcceptNestedScroll;
+        }
+
+        /**
+         * Check if an associated child view depends on another child view of the CoordinatorLayout.
+         *
+         * @param parent the parent CoordinatorLayout
+         * @param child the child to check
+         * @param dependency the proposed dependency to check
+         * @return true if child depends on dependency
+         */
+        boolean dependsOn(CoordinatorLayout parent, View child, View dependency) {
+            return dependency == mAnchorDirectChild
+                    || (mBehavior != null && mBehavior.layoutDependsOn(parent, child, dependency));
+        }
+
+        /**
+         * Invalidate the cached anchor view and direct child ancestor of that anchor.
+         * The anchor will need to be
+         * {@link #findAnchorView(CoordinatorLayout, android.view.View) found} before
+         * being used again.
+         */
+        void invalidateAnchor() {
+            mAnchorView = mAnchorDirectChild = null;
+        }
+
+        /**
+         * Locate the appropriate anchor view by the current {@link #setAnchorId(int) anchor id}
+         * or return the cached anchor view if already known.
+         *
+         * @param parent the parent CoordinatorLayout
+         * @param forChild the child this LayoutParams is associated with
+         * @return the located descendant anchor view, or null if the anchor id is
+         *         {@link View#NO_ID}.
+         */
+        View findAnchorView(CoordinatorLayout parent, View forChild) {
+            if (mAnchorId == View.NO_ID) {
+                mAnchorView = mAnchorDirectChild = null;
+                return null;
+            }
+
+            if (mAnchorView == null || !verifyAnchorView(forChild, parent)) {
+                resolveAnchorView(forChild, parent);
+            }
+            return mAnchorView;
+        }
+
+        /**
+         * Check if the child associated with this LayoutParams is currently considered
+         * "dirty" and needs to be updated. A Behavior should consider a child dirty
+         * whenever a property returned by another Behavior method would have changed,
+         * such as dependencies.
+         *
+         * @param parent the parent CoordinatorLayout
+         * @param child the child view associated with this LayoutParams
+         * @return true if this child view should be considered dirty
+         */
+        boolean isDirty(CoordinatorLayout parent, View child) {
+            return mBehavior != null && mBehavior.isDirty(parent, child);
+        }
+
+        /**
+         * Determine the anchor view for the child view this LayoutParams is assigned to.
+         * Assumes mAnchorId is valid.
+         */
+        private void resolveAnchorView(View forChild, CoordinatorLayout parent) {
+            mAnchorView = parent.findViewById(mAnchorId);
+            if (mAnchorView != null) {
+                View directChild = mAnchorView;
+                for (ViewParent p = mAnchorView.getParent();
+                        p != parent && p != null;
+                        p = p.getParent()) {
+                    if (p == forChild) {
+                        if (parent.isInEditMode()) {
+                            mAnchorView = mAnchorDirectChild = null;
+                            return;
+                        }
+                        throw new IllegalStateException(
+                                "Anchor must not be a descendant of the anchored view");
+                    }
+                    if (p instanceof View) {
+                        directChild = (View) p;
+                    }
+                }
+                mAnchorDirectChild = directChild;
+            } else {
+                if (parent.isInEditMode()) {
+                    mAnchorView = mAnchorDirectChild = null;
+                    return;
+                }
+                throw new IllegalStateException("Could not find CoordinatorLayout descendant view"
+                        + " with id " + parent.getResources().getResourceName(mAnchorId)
+                        + " to anchor view " + forChild);
+            }
+        }
+
+        /**
+         * Verify that the previously resolved anchor view is still valid - that it is still
+         * a descendant of the expected parent view, it is not the child this LayoutParams
+         * is assigned to or a descendant of it, and it has the expected id.
+         */
+        private boolean verifyAnchorView(View forChild, CoordinatorLayout parent) {
+            if (mAnchorView.getId() != mAnchorId) {
+                return false;
+            }
+
+            View directChild = mAnchorView;
+            for (ViewParent p = mAnchorView.getParent();
+                    p != parent;
+                    p = p.getParent()) {
+                if (p == null || p == forChild) {
+                    mAnchorView = mAnchorDirectChild = null;
+                    return false;
+                }
+                if (p instanceof View) {
+                    directChild = (View) p;
+                }
+            }
+            mAnchorDirectChild = directChild;
+            return true;
+        }
+    }
+}
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
new file mode 100644
index 0000000..e40e635
--- /dev/null
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -0,0 +1,437 @@
+/*
+ * 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.design.widget;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.ColorInt;
+import android.support.annotation.Nullable;
+import android.support.design.R;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.ImageView;
+
+import java.lang.ref.WeakReference;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Floating action buttons are used for a special type of promoted action. They are distinguished
+ * by a circled icon floating above the UI and have special motion behaviors related to morphing,
+ * launching, and the transferring anchor point.
+ *
+ * Floating action buttons come in two sizes: the default, which should be used in most cases, and
+ * the mini, which should only be used to create visual continuity with other elements on the
+ * screen.
+ */
+@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
+public class FloatingActionButton extends ImageView {
+
+    // These values must match those in the attrs declaration
+    private static final int SIZE_MINI = 1;
+    private static final int SIZE_NORMAL = 0;
+
+    private ColorStateList mBackgroundTint;
+    private PorterDuff.Mode mBackgroundTintMode;
+
+    private int mRippleColor;
+    private int mSize;
+    private int mContentPadding;
+
+    private final Rect mShadowPadding;
+
+    private final FloatingActionButtonImpl mImpl;
+
+    public FloatingActionButton(Context context) {
+        this(context, null);
+    }
+
+    public FloatingActionButton(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mShadowPadding = new Rect();
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.FloatingActionButton, defStyleAttr,
+                R.style.Widget_Design_FloatingActionButton);
+        Drawable background = a.getDrawable(R.styleable.FloatingActionButton_android_background);
+        mBackgroundTint = a.getColorStateList(R.styleable.FloatingActionButton_backgroundTint);
+        mBackgroundTintMode = parseTintMode(a.getInt(
+                R.styleable.FloatingActionButton_backgroundTintMode, -1), null);
+        mRippleColor = a.getColor(R.styleable.FloatingActionButton_rippleColor, 0);
+        mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, SIZE_NORMAL);
+        final float elevation = a.getDimension(R.styleable.FloatingActionButton_elevation, 0f);
+        final float pressedTranslationZ = a.getDimension(
+                R.styleable.FloatingActionButton_pressedTranslationZ, 0f);
+        a.recycle();
+
+        final ShadowViewDelegate delegate = new ShadowViewDelegate() {
+            @Override
+            public float getRadius() {
+                return getSizeDimension() / 2f;
+            }
+
+            @Override
+            public void setShadowPadding(int left, int top, int right, int bottom) {
+                mShadowPadding.set(left, top, right, bottom);
+
+                setPadding(left + mContentPadding, top + mContentPadding,
+                        right + mContentPadding, bottom + mContentPadding);
+            }
+
+            @Override
+            public void setBackgroundDrawable(Drawable background) {
+                FloatingActionButton.super.setBackgroundDrawable(background);
+            }
+        };
+
+        if (Build.VERSION.SDK_INT >= 21) {
+            mImpl = new FloatingActionButtonLollipop(this, delegate);
+        } else {
+            mImpl = new FloatingActionButtonEclairMr1(this, delegate);
+        }
+
+        final int maxContentSize = (int) getResources().getDimension(R.dimen.fab_content_size);
+        mContentPadding = (getSizeDimension() - maxContentSize) / 2;
+
+        mImpl.setBackgroundDrawable(background, mBackgroundTint,
+                mBackgroundTintMode, mRippleColor);
+        mImpl.setElevation(elevation);
+        mImpl.setPressedTranslationZ(pressedTranslationZ);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int preferredSize = getSizeDimension();
+
+        final int w = resolveAdjustedSize(preferredSize, widthMeasureSpec);
+        final int h = resolveAdjustedSize(preferredSize, heightMeasureSpec);
+
+        // As we want to stay circular, we set both dimensions to be the
+        // smallest resolved dimension
+        final int d = Math.min(w, h);
+
+        // We add the shadow's padding to the measured dimension
+        setMeasuredDimension(
+                d + mShadowPadding.left + mShadowPadding.right,
+                d + mShadowPadding.top + mShadowPadding.bottom);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        updateOffset();
+    }
+
+    /**
+     * Set the ripple color for this {@link FloatingActionButton}.
+     * <p>
+     * When running on devices with KitKat or below, we draw a fill rather than a ripple.
+     *
+     * @param color ARGB color to use for the ripple.
+     */
+    public void setRippleColor(@ColorInt int color) {
+        if (mRippleColor != color) {
+            mRippleColor = color;
+            mImpl.setRippleColor(color);
+        }
+    }
+
+    /**
+     * Return the tint applied to the background drawable, if specified.
+     *
+     * @return the tint applied to the background drawable
+     * @see #setBackgroundTintList(ColorStateList)
+     */
+    @Nullable
+    @Override
+    public ColorStateList getBackgroundTintList() {
+        return mBackgroundTint;
+    }
+
+    /**
+     * Applies a tint to the background drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     */
+    public void setBackgroundTintList(@Nullable ColorStateList tint) {
+        mImpl.setBackgroundTintList(tint);
+    }
+
+
+    /**
+     * Return the blending mode used to apply the tint to the background
+     * drawable, if specified.
+     *
+     * @return the blending mode used to apply the tint to the background
+     *         drawable
+     * @see #setBackgroundTintMode(PorterDuff.Mode)
+     */
+    @Nullable
+    @Override
+    public PorterDuff.Mode getBackgroundTintMode() {
+        return mBackgroundTintMode;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setBackgroundTintList(ColorStateList)}} to the background
+     * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     */
+    public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+        mImpl.setBackgroundTintMode(tintMode);
+    }
+
+    @Override
+    public void setBackgroundDrawable(Drawable background) {
+        mImpl.setBackgroundDrawable(background, mBackgroundTint, mBackgroundTintMode, mRippleColor);
+    }
+
+    final int getSizeDimension() {
+        switch (mSize) {
+            case SIZE_MINI:
+                return getResources().getDimensionPixelSize(R.dimen.fab_size_mini);
+            case SIZE_NORMAL:
+            default:
+                return getResources().getDimensionPixelSize(R.dimen.fab_size_normal);
+        }
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        mImpl.onDrawableStateChanged(getDrawableState());
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        mImpl.jumpDrawableToCurrentState();
+    }
+
+    private static int resolveAdjustedSize(int desiredSize, int measureSpec) {
+        int result = desiredSize;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+        switch (specMode) {
+            case MeasureSpec.UNSPECIFIED:
+                // Parent says we can be as big as we want. Just don't be larger
+                // than max size imposed on ourselves.
+                result = desiredSize;
+                break;
+            case MeasureSpec.AT_MOST:
+                // Parent says we can be as big as we want, up to specSize.
+                // Don't be larger than specSize, and don't be larger than
+                // the max size imposed on ourselves.
+                result = Math.min(desiredSize, specSize);
+                break;
+            case MeasureSpec.EXACTLY:
+                // No choice. Do what we are told.
+                result = specSize;
+                break;
+        }
+        return result;
+    }
+
+    static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
+        switch (value) {
+            case 3:
+                return PorterDuff.Mode.SRC_OVER;
+            case 5:
+                return PorterDuff.Mode.SRC_IN;
+            case 9:
+                return PorterDuff.Mode.SRC_ATOP;
+            case 14:
+                return PorterDuff.Mode.MULTIPLY;
+            case 15:
+                return PorterDuff.Mode.SCREEN;
+            default:
+                return defaultMode;
+        }
+    }
+
+    /**
+     * Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method
+     * offsets our layout position so that we're positioned correctly if we're on one of
+     * our parent's edges.
+     */
+    private void updateOffset() {
+        if (mShadowPadding.centerX() != 0 || mShadowPadding.centerY() != 0) {
+            int offsetTB = 0, offsetLR = 0;
+
+            if (isOnRightParentEdge()) {
+                offsetLR = mShadowPadding.right;
+            } else if (isOnLeftParentEdge()) {
+                offsetLR = -mShadowPadding.left;
+            }
+            if (isOnBottomParentEdge()) {
+                offsetTB = mShadowPadding.bottom;
+            } else if (isOnTopParentEdge()) {
+                offsetTB = -mShadowPadding.top;
+            }
+
+            offsetTopAndBottom(offsetTB);
+            offsetLeftAndRight(offsetLR);
+        }
+    }
+
+    private boolean isOnLeftParentEdge() {
+        final int margin = getLayoutParams() instanceof ViewGroup.MarginLayoutParams ?
+                ((ViewGroup.MarginLayoutParams) getLayoutParams()).leftMargin : 0;
+        return getLeft() <= margin;
+    }
+
+    private boolean isOnTopParentEdge() {
+        final int margin = getLayoutParams() instanceof ViewGroup.MarginLayoutParams ?
+                ((ViewGroup.MarginLayoutParams) getLayoutParams()).topMargin : 0;
+        return getTop() <= margin;
+    }
+
+    private boolean isOnRightParentEdge() {
+        final int margin = getLayoutParams() instanceof ViewGroup.MarginLayoutParams ?
+                ((ViewGroup.MarginLayoutParams) getLayoutParams()).rightMargin : 0;
+
+        ViewParent parent = getParent();
+        if (parent instanceof View) {
+            return getRight() >= (((View) getParent()).getWidth() - margin);
+        }
+        return false;
+    }
+
+    private boolean isOnBottomParentEdge() {
+        final int margin = getLayoutParams() instanceof ViewGroup.MarginLayoutParams ?
+                ((ViewGroup.MarginLayoutParams) getLayoutParams()).bottomMargin : 0;
+
+        ViewParent parent = getParent();
+        if (parent instanceof View) {
+            return getBottom() >= (((View) getParent()).getHeight() - margin);
+        }
+        return false;
+    }
+
+    /**
+     * Behavior designed for use with {@link FloatingActionButton} instances. It's main function
+     * is to move {@link FloatingActionButton} views so that any displayed {@link Snackbar}s do
+     * not cover them.
+     */
+    public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
+        // We only support the FAB <> Snackbar shift movement on Honeycomb and above. This is
+        // because we can use view translation properties which greatly simplifies the code.
+        private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
+
+        private float mTranslationY;
+        private Set<WeakReference<View>> mSnackbars;
+
+        @Override
+        public boolean layoutDependsOn(CoordinatorLayout parent,
+                FloatingActionButton child,
+                View dependency) {
+            // We're dependent on all SnackbarLayouts
+            if (SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout) {
+                if (!containsView(dependency)) {
+                    if (mSnackbars == null) mSnackbars = new HashSet<>();
+                    mSnackbars.add(new WeakReference<>(dependency));
+                }
+                cleanUpSet();
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child,
+                View snackbar) {
+            updateFabTranslation(parent, child, snackbar);
+            return false;
+        }
+
+        private void updateFabTranslation(CoordinatorLayout parent, FloatingActionButton fab,
+                View snackbar) {
+            final float translationY = getTranslationYForFab(parent, fab);
+            if (translationY != mTranslationY) {
+                // First, cancel any current animation
+                ViewCompat.animate(fab).cancel();
+
+                if (Math.abs(translationY - mTranslationY) == snackbar.getHeight()) {
+                    // If we're travelling by the height of the Snackbar then we probably need to
+                    // animate to the value
+                    ViewCompat.animate(fab).translationY(translationY)
+                            .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+                } else {
+                    // Else we'll set use setTranslationY
+                    ViewCompat.setTranslationY(fab, translationY);
+                }
+                mTranslationY = translationY;
+            }
+        }
+
+        private float getTranslationYForFab(CoordinatorLayout parent, FloatingActionButton fab) {
+            float minOffset = 0;
+            if (mSnackbars != null && !mSnackbars.isEmpty()) {
+                for (WeakReference<View> viewRef : mSnackbars) {
+                    final View view = viewRef.get();
+                    if (view != null && parent.doViewsOverlap(fab, view)) {
+                        minOffset = Math.min(minOffset,
+                                ViewCompat.getTranslationY(view) - view.getHeight());
+                    }
+                }
+            }
+            return minOffset;
+        }
+
+        private void cleanUpSet() {
+            if (mSnackbars != null && !mSnackbars.isEmpty()) {
+                for (final Iterator<WeakReference<View>> i = mSnackbars.iterator(); i.hasNext();) {
+                    WeakReference<View> ref = i.next();
+                    if (ref == null || ref.get() == null) {
+                        i.remove();
+                    }
+                }
+            }
+        }
+
+        private boolean containsView(View dependency) {
+            if (mSnackbars != null && !mSnackbars.isEmpty()) {
+                for (WeakReference<View> viewRef : mSnackbars) {
+                    if (viewRef.get() == dependency) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+}
diff --git a/design/src/android/support/design/widget/NavigationView.java b/design/src/android/support/design/widget/NavigationView.java
new file mode 100644
index 0000000..eac5920
--- /dev/null
+++ b/design/src/android/support/design/widget/NavigationView.java
@@ -0,0 +1,301 @@
+/*
+ * 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.design.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.R;
+import android.support.design.internal.NavigationMenuPresenter;
+import android.support.design.internal.ScrimInsetsFrameLayout;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.internal.view.menu.MenuBuilder;
+import android.util.AttributeSet;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+/**
+ * Represents a standard navigation menu for application. The menu contents can be populated
+ * by a menu resource file.
+ * <p>NavigationView is typically placed inside a {@link android.support.v4.widget.DrawerLayout}.
+ * </p>
+ * <pre>
+ * &lt;android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ *     xmlns:app="http://schemas.android.com/apk/res-auto"
+ *     android:id="@+id/drawer_layout"
+ *     android:layout_width="match_parent"
+ *     android:layout_height="match_parent"
+ *     android:fitsSystemWindows="true"&gt;
+ *
+ *     &lt;!-- Your contents --&gt;
+ *
+ *     &lt;android.support.design.widget.NavigationView
+ *         android:id="@+id/navigation"
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="match_parent"
+ *         android:layout_gravity="start" /&gt;
+ * &lt;/android.support.v4.widget.DrawerLayout&gt;
+ * </pre>
+ */
+public class NavigationView extends ScrimInsetsFrameLayout {
+
+    private static final int PRESENTER_NAVIGATION_VIEW = 1;
+
+    private OnNavigationItemSelectedListener mListener;
+
+    private MenuBuilder mMenu;
+
+    private NavigationMenuPresenter mPresenter;
+
+    private int mMaxWidth;
+
+    public NavigationView(Context context) {
+        this(context, null);
+    }
+
+    public NavigationView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        // Custom attributes
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.NavigationView, defStyleAttr,
+                R.style.Widget_Design_NavigationView);
+
+        //noinspection deprecation
+        setBackgroundDrawable(a.getDrawable(R.styleable.NavigationView_android_background));
+        ViewCompat.setElevation(this,
+                a.getDimensionPixelSize(R.styleable.NavigationView_android_elevation, 0));
+        ViewCompat.setFitsSystemWindows(this,
+                a.getBoolean(R.styleable.NavigationView_android_fitsSystemWindows, false));
+        mMaxWidth = a.getDimensionPixelSize(R.styleable.NavigationView_android_maxWidth, 0);
+        ColorStateList itemTintList =
+                a.getColorStateList(R.styleable.NavigationView_itemTint);
+        int itemBackgroundResource =
+                a.getResourceId(R.styleable.NavigationView_itemBackground, 0);
+        a.recycle();
+
+        // Set up the menu
+        mMenu = new MenuBuilder(context);
+        mMenu.setCallback(new MenuBuilder.Callback() {
+            @Override
+            public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+                return mListener != null && mListener.onNavigationItemSelected(item);
+            }
+
+            @Override
+            public void onMenuModeChange(MenuBuilder menu) {
+
+            }
+        });
+        mPresenter = new NavigationMenuPresenter();
+        mPresenter.setId(PRESENTER_NAVIGATION_VIEW);
+        mPresenter.initForMenu(context, mMenu);
+        mPresenter.setItemTintList(itemTintList);
+        mPresenter.setItemBackgroundResource(itemBackgroundResource);
+        mMenu.addMenuPresenter(mPresenter);
+        addView((View) mPresenter.getMenuView(this));
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState state = new SavedState(superState);
+        state.menuState = new Bundle();
+        mMenu.savePresenterStates(state.menuState);
+        return state;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable savedState) {
+        SavedState state = (SavedState) savedState;
+        super.onRestoreInstanceState(state.getSuperState());
+        mMenu.restorePresenterStates(state.menuState);
+    }
+
+    /**
+     * Set a listener that will be notified when a menu item is clicked.
+     *
+     * @param listener The listener to notify
+     */
+    public void setNavigationItemSelectedListener(OnNavigationItemSelectedListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    protected void onMeasure(int widthSpec, int heightSpec) {
+        switch (MeasureSpec.getMode(widthSpec)) {
+            case MeasureSpec.EXACTLY:
+                // Nothing to do
+                break;
+            case MeasureSpec.AT_MOST:
+                widthSpec = MeasureSpec.makeMeasureSpec(
+                        Math.min(MeasureSpec.getSize(widthSpec), mMaxWidth), MeasureSpec.EXACTLY);
+                break;
+            case MeasureSpec.UNSPECIFIED:
+                widthSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY);
+                break;
+        }
+        // Let super sort out the height
+        super.onMeasure(widthSpec, heightSpec);
+    }
+
+    /**
+     * @return The {@link Menu} associated with this NavigationView.
+     */
+    public Menu getMenu() {
+        return mMenu;
+    }
+
+    /**
+     * Inflates a View and add it as a header of the navigation menu.
+     *
+     * @param res The layout resource ID.
+     * @return a newly inflated View.
+     */
+    public View inflateHeaderView(@LayoutRes int res) {
+        return mPresenter.inflateHeaderView(res);
+    }
+
+    /**
+     * Adds a View as a header of the navigation menu.
+     *
+     * @param view The view to be added as a header of the navigation menu.
+     */
+    public void addHeaderView(@NonNull View view) {
+        mPresenter.addHeaderView(view);
+    }
+
+    /**
+     * Removes a previously-added header view.
+     *
+     * @param view The view to remove
+     */
+    public void removeHeaderView(@NonNull View view) {
+        mPresenter.removeHeaderView(view);
+    }
+
+    /**
+     * Return the tint applied to the icon and text of the menu items, if specified.
+     *
+     * @return the tint applied to the icon and text of the menu items
+     * @see #setItemTintList(ColorStateList)
+     * @attr ref R.styleable#NavigationView_itemTint
+     */
+    @Nullable
+    public ColorStateList getItemTintList() {
+        return mPresenter.getItemTintList();
+    }
+
+    /**
+     * Applies a tint to the icon and text of the menu items.
+     *
+     * @param itemTintList the tint to apply, may be {@code null} to use default tint.
+     * @attr ref R.styleable#NavigationView_itemTint
+     */
+    public void setItemTintList(@Nullable ColorStateList itemTintList) {
+        mPresenter.setItemTintList(itemTintList);
+    }
+
+    /**
+     * Return the resource ID of background drawable for the menu items.
+     *
+     * @return The resource ID
+     * @see #setItemBackgroundResource(int)
+     * @attr ref R.styleable#NavigationView_itemBackground
+     */
+    @DrawableRes
+    public int getItemBackgroundResource() {
+        return mPresenter.getItemBackgroundResource();
+    }
+
+    /**
+     * Set the background of the menu items to a given resource. The resource should refer to
+     * a Drawable object or 0 to use the background background.
+     *
+     * @param itemBackground The identifier of the resource.
+     * @attr ref R.styleable#NavigationView_itemBackground
+     */
+    public void setItemBackgroundResource(@DrawableRes int itemBackground) {
+        mPresenter.setItemBackgroundResource(itemBackground);
+    }
+
+    /**
+     * Listener for handling events on navigation items.
+     */
+    public interface OnNavigationItemSelectedListener {
+
+        /**
+         * Called when an item in the navigation menu is selected.
+         *
+         * @param item The selected item
+         */
+        public boolean onNavigationItemSelected(MenuItem item);
+    }
+
+    /**
+     * User interface state that is stored by NavigationView for implementing
+     * onSaveInstanceState().
+     */
+    public static class SavedState extends BaseSavedState {
+
+        public Bundle menuState;
+
+        public SavedState(Parcel in) {
+            super(in);
+            menuState = in.readBundle();
+        }
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeBundle(menuState);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR
+                = new Parcelable.Creator<SavedState>() {
+
+            @Override
+            public SavedState createFromParcel(Parcel parcel) {
+                return new SavedState(parcel);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+
+        };
+
+    }
+
+}
diff --git a/design/src/android/support/design/widget/Snackbar.java b/design/src/android/support/design/widget/Snackbar.java
new file mode 100644
index 0000000..041985c
--- /dev/null
+++ b/design/src/android/support/design/widget/Snackbar.java
@@ -0,0 +1,587 @@
+/*
+ * 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.design.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.IntDef;
+import android.support.annotation.StringRes;
+import android.support.design.R;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import static android.support.design.widget.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR;
+
+/**
+ * Snackbars provide lightweight feedback about an operation. They show a brief message at the
+ * bottom of the screen on mobile and lower left on larger devices. Snackbars appear above all other
+ * elements on screen and only one can be displayed at a time.
+ * <p>
+ * They automatically disappear after a timeout or after user interaction elsewhere on the screen,
+ * particularly after interactions that summon a new surface or activity. Snackbars can be swiped
+ * off screen.
+ * <p>
+ * Snackbars can contain an action which is set via
+ * {@link #setAction(CharSequence, android.view.View.OnClickListener)}.
+ */
+public class Snackbar {
+
+    /**
+     * @hide
+     */
+    @IntDef({LENGTH_SHORT, LENGTH_LONG})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Duration {}
+
+    /**
+     * Show the Snackbar for a short period of time.
+     *
+     * @see #setDuration
+     */
+    public static final int LENGTH_SHORT = -1;
+
+    /**
+     * Show the Snackbar for a long period of time.
+     *
+     * @see #setDuration
+     */
+    public static final int LENGTH_LONG = 0;
+
+    private static final int ANIMATION_DURATION = 250;
+    private static final int ANIMATION_FADE_DURATION = 180;
+
+    private static final Handler sHandler;
+    private static final int MSG_SHOW = 0;
+    private static final int MSG_DISMISS = 1;
+
+    static {
+        sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
+            @Override
+            public boolean handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_SHOW:
+                        ((Snackbar) message.obj).showView();
+                        return true;
+                    case MSG_DISMISS:
+                        ((Snackbar) message.obj).hideView();
+                        return true;
+                }
+                return false;
+            }
+        });
+    }
+
+    private final ViewGroup mParent;
+    private final Context mContext;
+    private final SnackbarLayout mView;
+    private int mDuration;
+
+    Snackbar(ViewGroup parent) {
+        mParent = parent;
+        mContext = parent.getContext();
+
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        mView = (SnackbarLayout) inflater.inflate(R.layout.layout_snackbar, mParent, false);
+    }
+
+    /**
+     * Make a Snackbar to display a message.
+     *
+     * @param parent   The parent to add Snackbars to.
+     * @param text     The text to show.  Can be formatted text.
+     * @param duration How long to display the message.  Either {@link #LENGTH_SHORT} or {@link
+     *                 #LENGTH_LONG}
+     */
+    public static Snackbar make(ViewGroup parent, CharSequence text, @Duration int duration) {
+        Snackbar snackbar = new Snackbar(parent);
+        snackbar.setText(text);
+        snackbar.setDuration(duration);
+        return snackbar;
+    }
+
+    /**
+     * Make a Snackbar to display a message.
+     *
+     * @param parent   The parent to add Snackbars to.
+     * @param resId    The resource id of the string resource to use.  Can be formatted text.
+     * @param duration How long to display the message.  Either {@link #LENGTH_SHORT} or {@link
+     *                 #LENGTH_LONG}
+     */
+    public static Snackbar make(ViewGroup parent, int resId, @Duration int duration) {
+        return make(parent, parent.getResources().getText(resId), duration);
+    }
+
+    /**
+     * Set the action to be displayed in this {@link Snackbar}.
+     *
+     * @param resId    String resource to display
+     * @param listener callback to be invoked when the action is clicked
+     */
+    public Snackbar setAction(@StringRes int resId, View.OnClickListener listener) {
+        return setAction(mContext.getText(resId), listener);
+    }
+
+    /**
+     * Set the action to be displayed in this {@link Snackbar}.
+     *
+     * @param text     Text to display
+     * @param listener callback to be invoked when the action is clicked
+     */
+    public Snackbar setAction(CharSequence text, final View.OnClickListener listener) {
+        final TextView tv = mView.getActionView();
+
+        if (TextUtils.isEmpty(text) || listener == null) {
+            tv.setVisibility(View.GONE);
+            tv.setOnClickListener(null);
+        } else {
+            tv.setVisibility(View.VISIBLE);
+            tv.setText(text);
+            tv.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    listener.onClick(view);
+
+                    // Now dismiss the Snackbar
+                    dismiss();
+                }
+            });
+        }
+        return this;
+    }
+
+    /**
+     * Update the text in this {@link Snackbar}.
+     *
+     * @param message The new text for the Toast.
+     */
+    public Snackbar setText(CharSequence message) {
+        final TextView tv = mView.getMessageView();
+        tv.setText(message);
+        return this;
+    }
+
+    /**
+     * Update the text in this {@link Snackbar}.
+     *
+     * @param resId The new text for the Toast.
+     */
+    public Snackbar setText(@StringRes int resId) {
+        return setText(mContext.getText(resId));
+    }
+
+    /**
+     * Set how long to show the view for.
+     *
+     * @param duration either be one of the predefined lengths:
+     *                 {@link #LENGTH_SHORT}, {@link #LENGTH_LONG}, or a custom duration
+     *                 in milliseconds.
+     */
+    public Snackbar setDuration(@Duration int duration) {
+        mDuration = duration;
+        return this;
+    }
+
+    /**
+     * Return the duration.
+     *
+     * @see #setDuration
+     */
+    @Duration
+    public int getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Returns the {@link Snackbar}'s view.
+     */
+    public View getView() {
+        return mView;
+    }
+
+    /**
+     * Show the {@link Snackbar}.
+     */
+    public void show() {
+        SnackbarManager.getInstance().show(mDuration, mManagerCallback);
+    }
+
+    /**
+     * Dismiss the {@link Snackbar}.
+     */
+    public void dismiss() {
+        SnackbarManager.getInstance().dismiss(mManagerCallback);
+    }
+
+    private final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
+        @Override
+        public void show() {
+            sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, Snackbar.this));
+        }
+
+        @Override
+        public void dismiss() {
+            sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, Snackbar.this));
+        }
+    };
+
+    final void showView() {
+        if (mView.getParent() == null) {
+            final ViewGroup.LayoutParams lp = mView.getLayoutParams();
+
+            if (lp instanceof CoordinatorLayout.LayoutParams) {
+                // If our LayoutParams are from a CoordinatorLayout, we'll setup our Behavior
+
+                final Behavior behavior = new Behavior();
+                behavior.setStartAlphaSwipeDistance(0.1f);
+                behavior.setEndAlphaSwipeDistance(0.6f);
+                behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END);
+                behavior.setListener(new SwipeDismissBehavior.OnDismissListener() {
+                    @Override
+                    public void onDismiss(View view) {
+                        dismiss();
+                    }
+
+                    @Override
+                    public void onDragStateChanged(int state) {
+                        switch (state) {
+                            case SwipeDismissBehavior.STATE_DRAGGING:
+                            case SwipeDismissBehavior.STATE_SETTLING:
+                                // If the view is being dragged or settling, cancel the timeout
+                                SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
+                                break;
+                            case SwipeDismissBehavior.STATE_IDLE:
+                                // If the view has been released and is idle, restore the timeout
+                                SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
+                                break;
+                        }
+                    }
+                });
+                ((CoordinatorLayout.LayoutParams) lp).setBehavior(behavior);
+            }
+
+            mParent.addView(mView);
+        }
+
+        if (ViewCompat.isLaidOut(mView)) {
+            // If the view is already laid out, animate it now
+            animateViewIn();
+        } else {
+            // Otherwise, add one of our layout change listeners and animate it in when laid out
+            mView.setOnLayoutChangeListener(new SnackbarLayout.OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View view, int left, int top, int right, int bottom) {
+                    animateViewIn();
+                    mView.setOnLayoutChangeListener(null);
+                }
+            });
+        }
+    }
+
+    private void animateViewIn() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            ViewCompat.setTranslationY(mView, mView.getHeight());
+            ViewCompat.animate(mView).translationY(0f)
+                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
+                    .setDuration(ANIMATION_DURATION)
+                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(View view) {
+                            mView.animateChildrenIn(ANIMATION_DURATION - ANIMATION_FADE_DURATION,
+                                    ANIMATION_FADE_DURATION);
+                        }
+
+                        @Override
+                        public void onAnimationEnd(View view) {
+                            SnackbarManager.getInstance().onShown(mManagerCallback);
+                        }
+                    }).start();
+        } else {
+            Animation anim = AnimationUtils.loadAnimation(mView.getContext(), R.anim.snackbar_in);
+            anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+            anim.setDuration(ANIMATION_DURATION);
+            anim.setAnimationListener(new Animation.AnimationListener() {
+                @Override
+                public void onAnimationEnd(Animation animation) {
+                    SnackbarManager.getInstance().onShown(mManagerCallback);
+                }
+
+                @Override
+                public void onAnimationStart(Animation animation) {}
+
+                @Override
+                public void onAnimationRepeat(Animation animation) {}
+            });
+            mView.startAnimation(anim);
+        }
+    }
+
+    private void animateViewOut() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            ViewCompat.animate(mView).translationY(mView.getHeight())
+                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
+                    .setDuration(ANIMATION_DURATION)
+                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(View view) {
+                            mView.animateChildrenOut(0, ANIMATION_FADE_DURATION);
+                        }
+
+                        @Override
+                        public void onAnimationEnd(View view) {
+                            onViewHidden();
+                        }
+                    }).start();
+        } else {
+            Animation anim = AnimationUtils.loadAnimation(mView.getContext(), R.anim.snackbar_out);
+            anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+            anim.setDuration(ANIMATION_DURATION);
+            anim.setAnimationListener(new Animation.AnimationListener() {
+                @Override
+                public void onAnimationEnd(Animation animation) {
+                    onViewHidden();
+                }
+
+                @Override
+                public void onAnimationStart(Animation animation) {}
+
+                @Override
+                public void onAnimationRepeat(Animation animation) {}
+            });
+            mView.startAnimation(anim);
+        }
+    }
+
+    final void hideView() {
+        if (mView.getVisibility() != View.VISIBLE || isBeingDragged()) {
+            onViewHidden();
+        } else {
+            animateViewOut();
+        }
+    }
+
+    private void onViewHidden() {
+        // First remove the view from the parent
+        mParent.removeView(mView);
+        // Now, tell the SnackbarManager that it has been dismissed
+        SnackbarManager.getInstance().onDismissed(mManagerCallback);
+    }
+
+    /**
+     * @return if the view is being being dragged or settled by {@link SwipeDismissBehavior}.
+     */
+    private boolean isBeingDragged() {
+        final ViewGroup.LayoutParams lp = mView.getLayoutParams();
+
+        if (lp instanceof CoordinatorLayout.LayoutParams) {
+            final CoordinatorLayout.LayoutParams cllp = (CoordinatorLayout.LayoutParams) lp;
+            final CoordinatorLayout.Behavior behavior = cllp.getBehavior();
+
+            if (behavior instanceof SwipeDismissBehavior) {
+                return ((SwipeDismissBehavior) behavior).getDragState()
+                        != SwipeDismissBehavior.STATE_IDLE;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    public static class SnackbarLayout extends LinearLayout {
+        private TextView mMessageView;
+        private TextView mActionView;
+
+        private int mMaxWidth;
+        private int mMaxInlineActionWidth;
+
+        interface OnLayoutChangeListener {
+            public void onLayoutChange(View view, int left, int top, int right, int bottom);
+        }
+
+        private OnLayoutChangeListener mOnLayoutChangeListener;
+
+        public SnackbarLayout(Context context) {
+            this(context, null);
+        }
+
+        public SnackbarLayout(Context context, AttributeSet attrs) {
+            super(context, attrs);
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackbarLayout);
+            mMaxWidth = a.getDimensionPixelSize(R.styleable.SnackbarLayout_android_maxWidth, -1);
+            mMaxInlineActionWidth = a.getDimensionPixelSize(
+                    R.styleable.SnackbarLayout_maxActionInlineWidth, -1);
+            a.recycle();
+
+            setClickable(true);
+
+            // Now inflate our content. We need to do this manually rather than using an <include>
+            // in the layout since older versions of the Android do not inflate includes with
+            // the correct Context.
+            LayoutInflater.from(context).inflate(R.layout.layout_snackbar_include, this);
+        }
+
+        @Override
+        protected void onFinishInflate() {
+            super.onFinishInflate();
+            mMessageView = (TextView) findViewById(R.id.snackbar_text);
+            mActionView = (TextView) findViewById(R.id.snackbar_action);
+        }
+
+        TextView getMessageView() {
+            return mMessageView;
+        }
+
+        TextView getActionView() {
+            return mActionView;
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+            if (mMaxWidth > 0 && getMeasuredWidth() > mMaxWidth) {
+                widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY);
+                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
+
+            final int multiLineVPadding = getResources().getDimensionPixelSize(
+                    R.dimen.snackbar_padding_vertical_2lines);
+            final int singleLineVPadding = getResources().getDimensionPixelSize(
+                    R.dimen.snackbar_padding_vertical);
+            final boolean isMultiLine = mMessageView.getLayout().getLineCount() > 1;
+
+            boolean remeasure = false;
+            if (isMultiLine && mMaxInlineActionWidth > 0
+                    && mActionView.getMeasuredWidth() > mMaxInlineActionWidth) {
+                if (updateViewsWithinLayout(VERTICAL, multiLineVPadding,
+                        multiLineVPadding - singleLineVPadding)) {
+                    remeasure = true;
+                }
+            } else {
+                final int messagePadding = isMultiLine ? multiLineVPadding : singleLineVPadding;
+                if (updateViewsWithinLayout(HORIZONTAL, messagePadding, messagePadding)) {
+                    remeasure = true;
+                }
+            }
+
+            if (remeasure) {
+                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
+        }
+
+        void animateChildrenIn(int delay, int duration) {
+            ViewCompat.setAlpha(mMessageView, 0f);
+            ViewCompat.animate(mMessageView).alpha(1f).setDuration(duration)
+                    .setStartDelay(delay).start();
+
+            if (mActionView.getVisibility() == VISIBLE) {
+                ViewCompat.setAlpha(mActionView, 0f);
+                ViewCompat.animate(mActionView).alpha(1f).setDuration(duration)
+                        .setStartDelay(delay).start();
+            }
+        }
+
+        void animateChildrenOut(int delay, int duration) {
+            ViewCompat.setAlpha(mMessageView, 1f);
+            ViewCompat.animate(mMessageView).alpha(0f).setDuration(duration)
+                    .setStartDelay(delay).start();
+
+            if (mActionView.getVisibility() == VISIBLE) {
+                ViewCompat.setAlpha(mActionView, 1f);
+                ViewCompat.animate(mActionView).alpha(0f).setDuration(duration)
+                        .setStartDelay(delay).start();
+            }
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int l, int t, int r, int b) {
+            super.onLayout(changed, l, t, r, b);
+            if (changed && mOnLayoutChangeListener != null) {
+                mOnLayoutChangeListener.onLayoutChange(this, l, t, r, b);
+            }
+        }
+
+        void setOnLayoutChangeListener(OnLayoutChangeListener onLayoutChangeListener) {
+            mOnLayoutChangeListener = onLayoutChangeListener;
+        }
+
+        private boolean updateViewsWithinLayout(final int orientation,
+                final int messagePadTop, final int messagePadBottom) {
+            boolean changed = false;
+            if (orientation != getOrientation()) {
+                setOrientation(orientation);
+                changed = true;
+            }
+            if (mMessageView.getPaddingTop() != messagePadTop
+                    || mMessageView.getPaddingBottom() != messagePadBottom) {
+                updateTopBottomPadding(mMessageView, messagePadTop, messagePadBottom);
+                changed = true;
+            }
+            return changed;
+        }
+
+        private static void updateTopBottomPadding(View view, int topPadding, int bottomPadding) {
+            if (ViewCompat.isPaddingRelative(view)) {
+                ViewCompat.setPaddingRelative(view,
+                        ViewCompat.getPaddingStart(view), topPadding,
+                        ViewCompat.getPaddingEnd(view), bottomPadding);
+            } else {
+                view.setPadding(view.getPaddingLeft(), topPadding,
+                        view.getPaddingRight(), bottomPadding);
+            }
+        }
+    }
+
+    final class Behavior extends SwipeDismissBehavior<SnackbarLayout> {
+        @Override
+        public boolean onInterceptTouchEvent(CoordinatorLayout parent, SnackbarLayout child,
+                MotionEvent event) {
+            // We want to make sure that we disable any Snackbar timeouts if the user is
+            // currently touching the Snackbar. We restore the timeout when complete
+            if (parent.isPointInChildBounds(child, (int) event.getX(), (int) event.getY())) {
+                switch (event.getActionMasked()) {
+                    case MotionEvent.ACTION_DOWN:
+                        SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
+                        break;
+                    case MotionEvent.ACTION_UP:
+                    case MotionEvent.ACTION_CANCEL:
+                        SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
+                        break;
+                }
+            }
+
+            return super.onInterceptTouchEvent(parent, child, event);
+        }
+    }
+}
diff --git a/design/src/android/support/design/widget/SnackbarManager.java b/design/src/android/support/design/widget/SnackbarManager.java
new file mode 100644
index 0000000..3b09f17
--- /dev/null
+++ b/design/src/android/support/design/widget/SnackbarManager.java
@@ -0,0 +1,199 @@
+/*
+ * 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.design.widget;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * Manages {@link Snackbar}s.
+ */
+class SnackbarManager {
+
+    private static final int MSG_TIMEOUT = 0;
+
+    private static final int SHORT_DURATION_MS = 1500;
+    private static final int LONG_DURATION_MS = 2750;
+
+    private static SnackbarManager sSnackbarManager;
+
+    static SnackbarManager getInstance() {
+        if (sSnackbarManager == null) {
+            sSnackbarManager = new SnackbarManager();
+        }
+        return sSnackbarManager;
+    }
+
+    private final Object mLock;
+    private final Handler mHandler;
+
+    private SnackbarRecord mCurrentSnackbar;
+    private SnackbarRecord mNextSnackbar;
+
+    private SnackbarManager() {
+        mLock = new Object();
+        mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
+            @Override
+            public boolean handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_TIMEOUT:
+                        handleTimeout((SnackbarRecord) message.obj);
+                        return true;
+                }
+                return false;
+            }
+        });
+    }
+
+    interface Callback {
+        void show();
+        void dismiss();
+    }
+
+    public void show(int duration, Callback callback) {
+        synchronized (mLock) {
+            if (isCurrentSnackbar(callback)) {
+                // Means that the callback is already in the queue. We'll just update the duration
+                mCurrentSnackbar.duration = duration;
+
+                // If this is the Snackbar currently being shown, call re-schedule it's
+                // timeout
+                mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
+                scheduleTimeoutLocked(mCurrentSnackbar);
+                return;
+            } else if (isNextSnackbar(callback)) {
+                // We'll just update the duration
+                mNextSnackbar.duration = duration;
+            } else {
+                // Else, we need to create a new record and queue it
+                mNextSnackbar = new SnackbarRecord(duration, callback);
+            }
+
+            if (mCurrentSnackbar != null) {
+                // If the new Snackbar isn't in position 0, we'll cancel the current one and wait
+                // in line
+                cancelSnackbarLocked(mCurrentSnackbar);
+            } else {
+                // Otherwise, just show it now
+                showNextSnackbarLocked();
+            }
+        }
+    }
+
+    public void dismiss(Callback callback) {
+        synchronized (mLock) {
+            if (isCurrentSnackbar(callback)) {
+                cancelSnackbarLocked(mCurrentSnackbar);
+            }
+            if (isNextSnackbar(callback)) {
+                cancelSnackbarLocked(mNextSnackbar);
+            }
+        }
+    }
+
+    /**
+     * Should be called when a Snackbar is no longer displayed. This is after any exit
+     * animation has finished.
+     */
+    public void onDismissed(Callback callback) {
+        synchronized (mLock) {
+            if (isCurrentSnackbar(callback)) {
+                // If the callback is from a Snackbar currently show, remove it and show a new one
+                mCurrentSnackbar = null;
+                if (mNextSnackbar != null) {
+                    showNextSnackbarLocked();
+                }
+            }
+        }
+    }
+
+    /**
+     * Should be called when a Snackbar is being shown. This is after any entrance animation has
+     * finished.
+     */
+    public void onShown(Callback callback) {
+        synchronized (mLock) {
+            if (isCurrentSnackbar(callback)) {
+                scheduleTimeoutLocked(mCurrentSnackbar);
+            }
+        }
+    }
+
+    public void cancelTimeout(Callback callback) {
+        synchronized (mLock) {
+            if (isCurrentSnackbar(callback)) {
+                mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
+            }
+        }
+    }
+
+    public void restoreTimeout(Callback callback) {
+        synchronized (mLock) {
+            if (isCurrentSnackbar(callback)) {
+                scheduleTimeoutLocked(mCurrentSnackbar);
+            }
+        }
+    }
+
+    private static class SnackbarRecord {
+        private Callback callback;
+        private int duration;
+
+        SnackbarRecord(int duration, Callback callback) {
+            this.callback = callback;
+            this.duration = duration;
+        }
+    }
+
+    private void showNextSnackbarLocked() {
+        if (mNextSnackbar != null) {
+            mCurrentSnackbar = mNextSnackbar;
+            mCurrentSnackbar.callback.show();
+            mNextSnackbar = null;
+        }
+    }
+
+    private void cancelSnackbarLocked(SnackbarRecord record) {
+        record.callback.dismiss();
+    }
+
+    private boolean isCurrentSnackbar(Callback callback) {
+        return mCurrentSnackbar != null && mCurrentSnackbar.callback == callback;
+    }
+
+    private boolean isNextSnackbar(Callback callback) {
+        return mNextSnackbar != null && mNextSnackbar.callback == callback;
+    }
+
+    private void scheduleTimeoutLocked(SnackbarRecord r) {
+        mHandler.removeCallbacksAndMessages(r);
+        mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_TIMEOUT, r),
+                r.duration == Snackbar.LENGTH_LONG
+                        ? LONG_DURATION_MS
+                        : SHORT_DURATION_MS);
+    }
+
+    private void handleTimeout(SnackbarRecord record) {
+        synchronized (mLock) {
+            if (mCurrentSnackbar == record || mNextSnackbar == record) {
+                cancelSnackbarLocked(record);
+            }
+        }
+    }
+
+}
diff --git a/design/src/android/support/design/widget/SwipeDismissBehavior.java b/design/src/android/support/design/widget/SwipeDismissBehavior.java
new file mode 100644
index 0000000..d6c1338
--- /dev/null
+++ b/design/src/android/support/design/widget/SwipeDismissBehavior.java
@@ -0,0 +1,383 @@
+/*
+ * 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.design.widget;
+
+import android.support.annotation.IntDef;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.ViewDragHelper;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An interaction behavior plugin for child views of {@link CoordinatorLayout} to provide support
+ * for the 'swipe-to-dismiss' gesture.
+ */
+public class SwipeDismissBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
+
+    /**
+     * A view is not currently being dragged or animating as a result of a fling/snap.
+     */
+    public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;
+
+    /**
+     * A view is currently being dragged. The position is currently changing as a result
+     * of user input or simulated user input.
+     */
+    public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;
+
+    /**
+     * A view is currently settling into place as a result of a fling or
+     * predefined non-interactive motion.
+     */
+    public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING;
+
+    /** @hide */
+    @IntDef({SWIPE_DIRECTION_START_TO_END, SWIPE_DIRECTION_END_TO_START, SWIPE_DIRECTION_ANY})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface SwipeDirection {}
+
+    /**
+     * Swipe direction that only allows swiping in the direction of start-to-end. That is
+     * left-to-right in LTR, or right-to-left in RTL.
+     */
+    public static final int SWIPE_DIRECTION_START_TO_END = 0;
+
+    /**
+     * Swipe direction that only allows swiping in the direction of end-to-start. That is
+     * right-to-left in LTR or left-to-right in RTL.
+     */
+    public static final int SWIPE_DIRECTION_END_TO_START = 1;
+
+    /**
+     * Swipe direction which allows swiping in either direction.
+     */
+    public static final int SWIPE_DIRECTION_ANY = 2;
+
+    private static final float DEFAULT_DRAG_DISMISS_THRESHOLD = 0.5f;
+    private static final float DEFAULT_ALPHA_START_DISTANCE = 0f;
+    private static final float DEFAULT_ALPHA_END_DISTANCE = DEFAULT_DRAG_DISMISS_THRESHOLD;
+
+    private ViewDragHelper mViewDragHelper;
+    private OnDismissListener mListener;
+    private boolean mIgnoreEvents;
+
+    private float mSensitivity = 0f;
+    private boolean mSensitivitySet;
+
+    private int mSwipeDirection = SWIPE_DIRECTION_ANY;
+    private float mDragDismissThreshold = DEFAULT_DRAG_DISMISS_THRESHOLD;
+    private float mAlphaStartSwipeDistance = DEFAULT_ALPHA_START_DISTANCE;
+    private float mAlphaEndSwipeDistance = DEFAULT_ALPHA_END_DISTANCE;
+
+    /**
+     * Callback interface used to notify the application that the view has been dismissed.
+     */
+    public interface OnDismissListener {
+        /**
+         * Called when {@code view} has been dismissed via swiping.
+         */
+        public void onDismiss(View view);
+
+        /**
+         * Called when the drag state has changed.
+         *
+         * @param state the new state. One of
+         * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
+         */
+        public void onDragStateChanged(int state);
+    }
+
+    /**
+     * Set the listener to be used when a dismiss event occurs.
+     *
+     * @param listener the listener to use.
+     */
+    public void setListener(OnDismissListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Sets the swipe direction for this behavior.
+     *
+     * @param direction one of the {@link #SWIPE_DIRECTION_START_TO_END},
+     *                  {@link #SWIPE_DIRECTION_END_TO_START} or {@link #SWIPE_DIRECTION_ANY}
+     */
+    public void setSwipeDirection(@SwipeDirection int direction) {
+        mSwipeDirection = direction;
+    }
+
+    /**
+     * Set the threshold for telling if a view has been dragged enough to be dismissed.
+     *
+     * @param distance a ratio of a view's width, values are clamped to 0 >= x <= 1f;
+     */
+    public void setDragDismissDistance(float distance) {
+        mDragDismissThreshold = clamp(0f, distance, 1f);
+    }
+
+    /**
+     * The minimum swipe distance before the view's alpha is modified.
+     *
+     * @param fraction the distance as a fraction of the view's width.
+     */
+    public void setStartAlphaSwipeDistance(float fraction) {
+        mAlphaStartSwipeDistance = clamp(0f, fraction, 1f);
+    }
+
+    /**
+     * The maximum swipe distance for the view's alpha is modified.
+     *
+     * @param fraction the distance as a fraction of the view's width.
+     */
+    public void setEndAlphaSwipeDistance(float fraction) {
+        mAlphaEndSwipeDistance = clamp(0f, fraction, 1f);
+    }
+
+    /**
+     * Set the sensitivity used for detecting the start of a swipe. This only takes effect if
+     * no touch handling has occured yet.
+     *
+     * @param sensitivity Multiplier for how sensitive we should be about detecting
+     *                    the start of a drag. Larger values are more sensitive. 1.0f is normal.
+     */
+    public void setSensitivity(float sensitivity) {
+        mSensitivity = sensitivity;
+        mSensitivitySet = true;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mIgnoreEvents = !parent.isPointInChildBounds(child,
+                        (int) event.getX(), (int) event.getY());
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                // Reset the ignore flag
+                if (mIgnoreEvents) {
+                    mIgnoreEvents = false;
+                    return false;
+                }
+                break;
+        }
+
+        if (mIgnoreEvents) {
+            return false;
+        }
+
+        ensureViewDragHelper(parent);
+        return mViewDragHelper.shouldInterceptTouchEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+        if (mViewDragHelper != null) {
+            mViewDragHelper.processTouchEvent(event);
+            return true;
+        }
+        return false;
+    }
+
+    private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
+        private int mOriginalCapturedViewLeft;
+
+        @Override
+        public boolean tryCaptureView(View child, int pointerId) {
+            mOriginalCapturedViewLeft = child.getLeft();
+            return true;
+        }
+
+        @Override
+        public void onViewDragStateChanged(int state) {
+            if (mListener != null) {
+                mListener.onDragStateChanged(state);
+            }
+        }
+
+        @Override
+        public void onViewReleased(View child, float xvel, float yvel) {
+            final int childWidth = child.getWidth();
+            int targetLeft;
+            boolean dismiss = false;
+
+            if (shouldDismiss(child, xvel)) {
+                targetLeft = child.getLeft() < mOriginalCapturedViewLeft
+                        ? mOriginalCapturedViewLeft - childWidth
+                        : mOriginalCapturedViewLeft + childWidth;
+                dismiss = true;
+            } else {
+                // Else, reset back to the original left
+                targetLeft = mOriginalCapturedViewLeft;
+            }
+
+            if (mViewDragHelper.settleCapturedViewAt(targetLeft, child.getTop())) {
+                ViewCompat.postOnAnimation(child, new SettleRunnable(child, dismiss));
+            } else if (dismiss && mListener != null) {
+                mListener.onDismiss(child);
+            }
+        }
+
+        private boolean shouldDismiss(View child, float xvel) {
+            if (xvel != 0f) {
+                final boolean isRtl = ViewCompat.getLayoutDirection(child)
+                        == ViewCompat.LAYOUT_DIRECTION_RTL;
+
+                if (mSwipeDirection == SWIPE_DIRECTION_ANY) {
+                    // We don't care about the direction so return true
+                    return true;
+                } else if (mSwipeDirection == SWIPE_DIRECTION_START_TO_END) {
+                    // We only allow start-to-end swiping, so the fling needs to be in the
+                    // correct direction
+                    return isRtl ? xvel < 0f : xvel > 0f;
+                } else if (mSwipeDirection == SWIPE_DIRECTION_END_TO_START) {
+                    // We only allow end-to-start swiping, so the fling needs to be in the
+                    // correct direction
+                    return isRtl ? xvel > 0f : xvel < 0f;
+                }
+            } else {
+                final int distance = child.getLeft() - mOriginalCapturedViewLeft;
+                final int thresholdDistance = Math.round(child.getWidth() * mDragDismissThreshold);
+                return Math.abs(distance) >= thresholdDistance;
+            }
+
+            return false;
+        }
+
+        @Override
+        public int getViewHorizontalDragRange(View child) {
+            return child.getWidth();
+        }
+
+        @Override
+        public int clampViewPositionHorizontal(View child, int left, int dx) {
+            final boolean isRtl = ViewCompat.getLayoutDirection(child)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
+            int min, max;
+
+            if (mSwipeDirection == SWIPE_DIRECTION_START_TO_END) {
+                if (isRtl) {
+                    min = mOriginalCapturedViewLeft - child.getWidth();
+                    max = mOriginalCapturedViewLeft;
+                } else {
+                    min = mOriginalCapturedViewLeft;
+                    max = mOriginalCapturedViewLeft + child.getWidth();
+                }
+            } else if (mSwipeDirection == SWIPE_DIRECTION_END_TO_START) {
+                if (isRtl) {
+                    min = mOriginalCapturedViewLeft;
+                    max = mOriginalCapturedViewLeft + child.getWidth();
+                } else {
+                    min = mOriginalCapturedViewLeft - child.getWidth();
+                    max = mOriginalCapturedViewLeft;
+                }
+            } else {
+                min = mOriginalCapturedViewLeft - child.getWidth();
+                max = mOriginalCapturedViewLeft + child.getWidth();
+            }
+
+            return clamp(min, left, max);
+        }
+
+        @Override
+        public int clampViewPositionVertical(View child, int top, int dy) {
+            return child.getTop();
+        }
+
+        @Override
+        public void onViewPositionChanged(View child, int left, int top, int dx, int dy) {
+            final float startAlphaDistance = child.getWidth() * mAlphaStartSwipeDistance;
+            final float endAlphaDistance = child.getWidth() * mAlphaEndSwipeDistance;
+
+            if (left <= startAlphaDistance) {
+                ViewCompat.setAlpha(child, 1f);
+            } else if (left >= endAlphaDistance) {
+                ViewCompat.setAlpha(child, 0f);
+            } else {
+                // We're between the start and end distances
+                final float distance = fraction(startAlphaDistance, endAlphaDistance, left);
+                ViewCompat.setAlpha(child, clamp(0f, 1f - distance, 1f));
+            }
+        }
+    };
+
+    private void ensureViewDragHelper(ViewGroup parent) {
+        if (mViewDragHelper == null) {
+            mViewDragHelper = mSensitivitySet
+                    ? ViewDragHelper.create(parent, mSensitivity, mDragCallback)
+                    : ViewDragHelper.create(parent, mDragCallback);
+        }
+    }
+
+    private class SettleRunnable implements Runnable {
+        private final View mView;
+        private final boolean mDismiss;
+
+        SettleRunnable(View view, boolean dismiss) {
+            mView = view;
+            mDismiss = dismiss;
+        }
+
+        @Override
+        public void run() {
+            if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
+                ViewCompat.postOnAnimation(mView, this);
+            } else {
+                if (mDismiss && mListener != null) {
+                    mListener.onDismiss(mView);
+                }
+            }
+        }
+    }
+
+    private static float clamp(float min, float value, float max) {
+        return Math.min(Math.max(min, value), max);
+    }
+
+    private static int clamp(int min, int value, int max) {
+        return Math.min(Math.max(min, value), max);
+    }
+
+    /**
+     * Retrieve the current drag state of this behavior. This will return one of
+     * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
+     *
+     * @return The current drag state
+     */
+    public int getDragState() {
+        return mViewDragHelper != null ? mViewDragHelper.getViewDragState() : STATE_IDLE;
+    }
+
+    /**
+     * Linear interpolation between {@code startValue} and {@code endValue} by the fraction {@code
+     * fraction}.
+     */
+    static float lerp(float startValue, float endValue, float fraction) {
+        return startValue + (fraction * (endValue - startValue));
+    }
+
+    /**
+     * The fraction that {@code value} is between {@code startValue} and {@code endValue}.
+     */
+    static float fraction(float startValue, float endValue, float value) {
+        return (value - startValue) / (endValue - startValue);
+    }
+}
\ No newline at end of file
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
new file mode 100755
index 0000000..b0687a5
--- /dev/null
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -0,0 +1,1405 @@
+/*
+ * 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.design.widget;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.ColorInt;
+import android.support.annotation.IntDef;
+import android.support.design.R;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.ActionBar;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.widget.AppCompatTextView;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import static android.support.design.widget.AnimationUtils.lerp;
+
+/**
+ * TabLayout provides a horizontal layout to display tabs. <p> Population of the tabs to display is
+ * done through {@link Tab} instances. You create tabs via {@link #newTab()}. From there you can
+ * change the tab's label or icon via {@link Tab#setText(int)} and {@link Tab#setIcon(int)}
+ * respectively. To display the tab, you need to add it to the layout via one of the {@link
+ * #addTab(Tab)} methods. For example:
+ * <pre>
+ * TabLayout tabLayout = ...;
+ * tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
+ * tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
+ * tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
+ * </pre>
+ * You should set a listener via {@link #setOnTabSelectedListener(OnTabSelectedListener)} to be
+ * notified when any tab's selection state has been changed.
+ * <p>
+ * If you're using a {@link android.support.v4.view.ViewPager} together
+ * with this layout, you can use {@link #addTabsFromPagerAdapter(PagerAdapter)} which will populate
+ * the tabs using the {@link PagerAdapter}'s page titles. You should also use a {@link
+ * ViewPager.OnPageChangeListener} to forward the scroll and selection changes to this layout.
+ * You can use the one returned {@link #createOnPageChangeListener()} for easy implementation:
+ * <pre>
+ * ViewPager viewPager = ...;
+ * TabLayout tabLayout = ...;
+ * viewPager.setOnPageChangeListener(tabLayout.createOnPageChangeListener());
+ * </pre>
+ *
+ * @see <a href="http://www.google.com/design/spec/components/tabs.html">Tabs</a>
+ */
+public class TabLayout extends HorizontalScrollView {
+
+    private static final int MAX_TAB_TEXT_LINES = 2;
+
+    private static final int DEFAULT_HEIGHT = 48; // dps
+    private static final int TAB_MIN_WIDTH_MARGIN = 56; //dps
+    private static final int FIXED_WRAP_GUTTER_MIN = 16; //dps
+    private static final int MOTION_NON_ADJACENT_OFFSET = 24;
+
+    private static final int ANIMATION_DURATION = 300;
+
+    /**
+     * Scrollable tabs display a subset of tabs at any given moment, and can contain longer tab
+     * labels and a larger number of tabs. They are best used for browsing contexts in touch
+     * interfaces when users don’t need to directly compare the tab labels.
+     *
+     * @attr android.support.design.R.attr.tabMode
+     * @see #setTabMode(int)
+     * @see #getTabMode()
+     */
+    public static final int MODE_SCROLLABLE = 0;
+
+    /**
+     * Fixed tabs display all tabs concurrently and are best used with content that benefits from
+     * quick pivots between tabs. The maximum number of tabs is limited by the view’s width.
+     * Fixed tabs have equal width, based on the widest tab label.
+     *
+     * @attr android.support.design.R.attr.tabMode
+     * @see #setTabMode(int)
+     * @see #getTabMode()
+     */
+    public static final int MODE_FIXED = 1;
+
+    /**
+     * @hide
+     */
+    @IntDef(value = {MODE_SCROLLABLE, MODE_FIXED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Mode {}
+
+    /**
+     * Gravity used to fill the {@link TabLayout} as much as possible. This option only takes effect
+     * when used with {@link #MODE_FIXED}.
+     *
+     * @attr android.support.design.R.attr.tabGravity
+     * @see #setTabGravity(int)
+     * @see #getTabGravity()
+     */
+    public static final int GRAVITY_FILL = 0;
+
+    /**
+     * Gravity used to lay out the tabs in the center of the {@link TabLayout}.
+     *
+     * @attr android.support.design.R.attr.tabGravity
+     * @see #setTabGravity(int)
+     * @see #getTabGravity()
+     */
+    public static final int GRAVITY_CENTER = 1;
+
+    /**
+     * @hide
+     */
+    @IntDef(flag = true, value = {GRAVITY_FILL, GRAVITY_CENTER})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TabGravity {}
+
+    /**
+     * Callback interface invoked when a tab's selection state changes.
+     */
+    public interface OnTabSelectedListener {
+
+        /**
+         * Called when a tab enters the selected state.
+         *
+         * @param tab The tab that was selected
+         */
+        public void onTabSelected(Tab tab);
+
+        /**
+         * Called when a tab exits the selected state.
+         *
+         * @param tab The tab that was unselected
+         */
+        public void onTabUnselected(Tab tab);
+
+        /**
+         * Called when a tab that is already selected is chosen again by the user. Some applications
+         * may use this action to return to the top level of a category.
+         *
+         * @param tab The tab that was reselected.
+         */
+        public void onTabReselected(Tab tab);
+    }
+
+    private final ArrayList<Tab> mTabs = new ArrayList<>();
+    private Tab mSelectedTab;
+
+    private final SlidingTabStrip mTabStrip;
+
+    private int mTabPaddingStart;
+    private int mTabPaddingTop;
+    private int mTabPaddingEnd;
+    private int mTabPaddingBottom;
+
+    private final int mTabTextAppearance;
+    private int mTabSelectedTextColor;
+    private boolean mTabSelectedTextColorSet;
+    private final int mTabBackgroundResId;
+
+    private final int mTabMinWidth;
+    private int mTabMaxWidth;
+    private final int mRequestedTabMaxWidth;
+
+    private int mContentInsetStart;
+
+    private int mTabGravity;
+    private int mMode;
+
+    private OnTabSelectedListener mOnTabSelectedListener;
+    private View.OnClickListener mTabClickListener;
+
+    public TabLayout(Context context) {
+        this(context, null);
+    }
+
+    public TabLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        // Disable the Scroll Bar
+        setHorizontalScrollBarEnabled(false);
+        // Set us to fill the View port
+        setFillViewport(true);
+
+        // Add the TabStrip
+        mTabStrip = new SlidingTabStrip(context);
+        addView(mTabStrip, LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout,
+                defStyleAttr, R.style.Widget_Design_TabLayout);
+
+        mTabStrip.setSelectedIndicatorHeight(
+                a.getDimensionPixelSize(R.styleable.TabLayout_tabIndicatorHeight, 0));
+        mTabStrip.setSelectedIndicatorColor(a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0));
+
+        mTabTextAppearance = a.getResourceId(R.styleable.TabLayout_tabTextAppearance,
+                R.style.TextAppearance_Design_Tab);
+
+        mTabPaddingStart = mTabPaddingTop = mTabPaddingEnd = mTabPaddingBottom = a
+                .getDimensionPixelSize(R.styleable.TabLayout_tabPadding, 0);
+        mTabPaddingStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingStart,
+                mTabPaddingStart);
+        mTabPaddingTop = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingTop,
+                mTabPaddingTop);
+        mTabPaddingEnd = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingEnd,
+                mTabPaddingEnd);
+        mTabPaddingBottom = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingBottom,
+                mTabPaddingBottom);
+
+        if (a.hasValue(R.styleable.TabLayout_tabSelectedTextColor)) {
+            mTabSelectedTextColor = a.getColor(R.styleable.TabLayout_tabSelectedTextColor, 0);
+            mTabSelectedTextColorSet = true;
+        }
+
+        mTabMinWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMinWidth, 0);
+        mRequestedTabMaxWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMaxWidth, 0);
+        mTabBackgroundResId = a.getResourceId(R.styleable.TabLayout_tabBackground, 0);
+        mContentInsetStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabContentStart, 0);
+        mMode = a.getInt(R.styleable.TabLayout_tabMode, MODE_FIXED);
+        mTabGravity = a.getInt(R.styleable.TabLayout_tabGravity, GRAVITY_FILL);
+        a.recycle();
+
+        // Now apply the tab mode and gravity
+        applyModeAndGravity();
+    }
+
+    /**
+     * Set the scroll position of the tabs. This is useful for when the tabs are being displayed as
+     * part of a scrolling container such as {@link ViewPager}.
+     * <p>
+     * Calling this method does not update the selected tab, it is only used for drawing purposes.
+     *
+     * @param position current scroll position
+     * @param positionOffset Value from [0, 1) indicating the offset from {@code position}.
+     * @param updateSelectedText Whether to update the text's selected state.
+     */
+    public void setScrollPosition(int position, float positionOffset, boolean updateSelectedText) {
+        if (isAnimationRunning(getAnimation())) {
+            return;
+        }
+        if (position < 0 || position >= mTabStrip.getChildCount()) {
+            return;
+        }
+
+        // Set the indicator position and update the scroll to match
+        mTabStrip.setIndicatorPositionFromTabPosition(position, positionOffset);
+        scrollTo(calculateScrollXForTab(position, positionOffset), 0);
+
+        // Update the 'selected state' view as we scroll
+        if (updateSelectedText) {
+            setSelectedTabView(Math.round(position + positionOffset));
+        }
+    }
+
+    /**
+     * Add new {@link Tab}s populated from a {@link PagerAdapter}. Each tab will have it's text set
+     * to the value returned from {@link PagerAdapter#getPageTitle(int)}.
+     *
+     * @param adapter the adapter to populate from
+     */
+    public void addTabsFromPagerAdapter(PagerAdapter adapter) {
+        for (int i = 0, count = adapter.getCount(); i < count; i++) {
+            addTab(newTab().setText(adapter.getPageTitle(i)));
+        }
+    }
+
+    /**
+     * Create a {@link ViewPager.OnPageChangeListener} which implements the
+     * necessary calls back to this layout so that the tabs position is kept in sync.
+     * <p>
+     * If you need to have a custom {@link ViewPager.OnPageChangeListener} for your own
+     * purposes, you can still use the instance returned from this method, but making sure to call
+     * through to all of the methods.
+     */
+    public ViewPager.OnPageChangeListener createOnPageChangeListener() {
+        return new ViewPager.OnPageChangeListener() {
+            private int mScrollState;
+
+            @Override
+            public void onPageScrollStateChanged(int state) {
+                mScrollState = state;
+            }
+
+            @Override
+            public void onPageScrolled(int position, float positionOffset,
+                    int positionOffsetPixels) {
+                // Update the scroll position, only update the text selection if we're being
+                // dragged
+                setScrollPosition(position, positionOffset,
+                        mScrollState == ViewPager.SCROLL_STATE_DRAGGING);
+            }
+
+            @Override
+            public void onPageSelected(int position) {
+                getTabAt(position).select();
+            }
+        };
+    }
+
+    /**
+     * Add a tab to this layout. The tab will be added at the end of the list.
+     * If this is the first tab to be added it will become the selected tab.
+     *
+     * @param tab Tab to add
+     */
+    public void addTab(Tab tab) {
+        addTab(tab, mTabs.isEmpty());
+    }
+
+    /**
+     * Add a tab to this layout. The tab will be inserted at <code>position</code>.
+     * If this is the first tab to be added it will become the selected tab.
+     *
+     * @param tab The tab to add
+     * @param position The new position of the tab
+     */
+    public void addTab(Tab tab, int position) {
+        addTab(tab, position, mTabs.isEmpty());
+    }
+
+    /**
+     * Add a tab to this layout. The tab will be added at the end of the list.
+     *
+     * @param tab Tab to add
+     * @param setSelected True if the added tab should become the selected tab.
+     */
+    public void addTab(Tab tab, boolean setSelected) {
+        if (tab.mParent != this) {
+            throw new IllegalArgumentException("Tab belongs to a different TabLayout.");
+        }
+
+        addTabView(tab, setSelected);
+        configureTab(tab, mTabs.size());
+        if (setSelected) {
+            tab.select();
+        }
+    }
+
+    /**
+     * Add a tab to this layout. The tab will be inserted at <code>position</code>.
+     *
+     * @param tab The tab to add
+     * @param position The new position of the tab
+     * @param setSelected True if the added tab should become the selected tab.
+     */
+    public void addTab(Tab tab, int position, boolean setSelected) {
+        if (tab.mParent != this) {
+            throw new IllegalArgumentException("Tab belongs to a different TabLayout.");
+        }
+
+        addTabView(tab, position, setSelected);
+        configureTab(tab, position);
+        if (setSelected) {
+            tab.select();
+        }
+    }
+
+    /**
+     * Set the {@link android.support.design.widget.TabLayout.OnTabSelectedListener} that will handle switching to and from tabs.
+     *
+     * @param onTabSelectedListener Listener to handle tab selection events
+     */
+    public void setOnTabSelectedListener(OnTabSelectedListener onTabSelectedListener) {
+        mOnTabSelectedListener = onTabSelectedListener;
+    }
+
+    /**
+     * Create and return a new {@link Tab}. You need to manually add this using
+     * {@link #addTab(Tab)} or a related method.
+     *
+     * @return A new Tab
+     * @see #addTab(Tab)
+     */
+    public Tab newTab() {
+        return new Tab(this);
+    }
+
+    /**
+     * Returns the number of tabs currently registered with the action bar.
+     *
+     * @return Tab count
+     */
+    public int getTabCount() {
+        return mTabs.size();
+    }
+
+    /**
+     * Returns the tab at the specified index.
+     */
+    public Tab getTabAt(int index) {
+        return mTabs.get(index);
+    }
+
+    /**
+     * Remove a tab from the layout. If the removed tab was selected it will be deselected
+     * and another tab will be selected if present.
+     *
+     * @param tab The tab to remove
+     */
+    public void removeTab(Tab tab) {
+        if (tab.mParent != this) {
+            throw new IllegalArgumentException("Tab does not belong to this TabLayout.");
+        }
+
+        removeTabAt(tab.getPosition());
+    }
+
+    /**
+     * Remove a tab from the layout. If the removed tab was selected it will be deselected
+     * and another tab will be selected if present.
+     *
+     * @param position Position of the tab to remove
+     */
+    public void removeTabAt(int position) {
+        final int selectedTabPosition = mSelectedTab != null ? mSelectedTab.getPosition() : 0;
+        removeTabViewAt(position);
+
+        Tab removedTab = mTabs.remove(position);
+        if (removedTab != null) {
+            removedTab.setPosition(Tab.INVALID_POSITION);
+        }
+
+        final int newTabCount = mTabs.size();
+        for (int i = position; i < newTabCount; i++) {
+            mTabs.get(i).setPosition(i);
+        }
+
+        if (selectedTabPosition == position) {
+            selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
+        }
+    }
+
+    /**
+     * Remove all tabs from the action bar and deselect the current tab.
+     */
+    public void removeAllTabs() {
+        // Remove all the views
+        mTabStrip.removeAllViews();
+
+        for (Iterator<Tab> i = mTabs.iterator(); i.hasNext(); ) {
+            Tab tab = i.next();
+            tab.setPosition(Tab.INVALID_POSITION);
+            i.remove();
+        }
+    }
+
+    /**
+     * Set the behavior mode for the Tabs in this layout. The valid input options are:
+     * <ul>
+     * <li>{@link #MODE_FIXED}: Fixed tabs display all tabs concurrently and are best used
+     * with content that benefits from quick pivots between tabs.</li>
+     * <li>{@link #MODE_SCROLLABLE}: Scrollable tabs display a subset of tabs at any given moment,
+     * and can contain longer tab labels and a larger number of tabs. They are best used for
+     * browsing contexts in touch interfaces when users don’t need to directly compare the tab
+     * labels. This mode is commonly used with a {@link ViewPager}.</li>
+     * </ul>
+     *
+     * @param mode one of {@link #MODE_FIXED} or {@link #MODE_SCROLLABLE}.
+     */
+    public void setTabMode(@Mode int mode) {
+        if (mode != mMode) {
+            mMode = mode;
+            applyModeAndGravity();
+        }
+    }
+
+    /**
+     * Returns the current mode used by this {@link TabLayout}.
+     *
+     * @see #setTabMode(int)
+     */
+    @Mode
+    public int getTabMode() {
+        return mMode;
+    }
+
+    /**
+     * Set the gravity to use when laying out the tabs.
+     *
+     * @param gravity one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}.
+     */
+    public void setTabGravity(@TabGravity int gravity) {
+        if (mTabGravity != gravity) {
+            mTabGravity = gravity;
+            applyModeAndGravity();
+        }
+    }
+
+    /**
+     * The current gravity used for laying out tabs.
+     *
+     * @return one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}.
+     */
+    @TabGravity
+    public int getTabGravity() {
+        return mTabGravity;
+    }
+
+    /**
+     * Set the text color to use when a tab is selected.
+     *
+     * @param textColor
+     */
+    public void setTabSelectedTextColor(@ColorInt int textColor) {
+        if (!mTabSelectedTextColorSet || mTabSelectedTextColor != textColor) {
+            mTabSelectedTextColor = textColor;
+            mTabSelectedTextColorSet = true;
+
+            for (int i = 0, z = mTabStrip.getChildCount(); i < z; i++) {
+                updateTab(i);
+            }
+        }
+    }
+
+    /**
+     * Returns the text color currently used when a tab is selected.
+     */
+    @ColorInt
+    public int getTabSelectedTextColor() {
+        return mTabSelectedTextColor;
+    }
+
+    private TabView createTabView(Tab tab) {
+        final TabView tabView = new TabView(getContext(), tab);
+        tabView.setFocusable(true);
+
+        if (mTabClickListener == null) {
+            mTabClickListener = new View.OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    TabView tabView = (TabView) view;
+                    tabView.getTab().select();
+                }
+            };
+        }
+        tabView.setOnClickListener(mTabClickListener);
+        return tabView;
+    }
+
+    private void configureTab(Tab tab, int position) {
+        tab.setPosition(position);
+        mTabs.add(position, tab);
+
+        final int count = mTabs.size();
+        for (int i = position + 1; i < count; i++) {
+            mTabs.get(i).setPosition(i);
+        }
+    }
+
+    private void updateTab(int position) {
+        final TabView view = (TabView) mTabStrip.getChildAt(position);
+        if (view != null) {
+            view.update();
+        }
+    }
+
+    private void addTabView(Tab tab, boolean setSelected) {
+        final TabView tabView = createTabView(tab);
+        mTabStrip.addView(tabView, createLayoutParamsForTabs());
+        if (setSelected) {
+            tabView.setSelected(true);
+        }
+    }
+
+    private void addTabView(Tab tab, int position, boolean setSelected) {
+        final TabView tabView = createTabView(tab);
+        mTabStrip.addView(tabView, position, createLayoutParamsForTabs());
+        if (setSelected) {
+            tabView.setSelected(true);
+        }
+    }
+
+    private LinearLayout.LayoutParams createLayoutParamsForTabs() {
+        final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
+        updateTabViewLayoutParams(lp);
+        return lp;
+    }
+
+    private void updateTabViewLayoutParams(LinearLayout.LayoutParams lp) {
+        if (mMode == MODE_FIXED && mTabGravity == GRAVITY_FILL) {
+            lp.width = 0;
+            lp.weight = 1;
+        } else {
+            lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
+            lp.weight = 0;
+        }
+    }
+
+    private int dpToPx(int dps) {
+        return Math.round(getResources().getDisplayMetrics().density * dps);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // If we have a MeasureSpec which allows us to decide our height, try and use the default
+        // height
+        switch (MeasureSpec.getMode(heightMeasureSpec)) {
+            case MeasureSpec.AT_MOST:
+                heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                        Math.min(dpToPx(DEFAULT_HEIGHT), MeasureSpec.getSize(heightMeasureSpec)),
+                        MeasureSpec.EXACTLY);
+                break;
+            case MeasureSpec.UNSPECIFIED:
+                heightMeasureSpec = MeasureSpec.makeMeasureSpec(dpToPx(DEFAULT_HEIGHT),
+                        MeasureSpec.EXACTLY);
+                break;
+        }
+
+        // Now super measure itself using the (possibly) modified height spec
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        if (mMode == MODE_FIXED && getChildCount() == 1) {
+            // If we're in fixed mode then we need to make the tab strip is the same width as us
+            // so we don't scroll
+            final View child = getChildAt(0);
+            final int width = getMeasuredWidth();
+
+            if (child.getMeasuredWidth() > width) {
+                // If the child is wider than us, re-measure it with a widthSpec set to exact our
+                // measure width
+                int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop()
+                        + getPaddingBottom(), child.getLayoutParams().height);
+                int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
+                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+            }
+        }
+
+        // Now update the tab max width. We do it here as the default tab min width is
+        // layout width - 56dp
+        int maxTabWidth = mRequestedTabMaxWidth;
+        final int defaultTabMaxWidth = getMeasuredWidth() - dpToPx(TAB_MIN_WIDTH_MARGIN);
+        if (maxTabWidth == 0 || maxTabWidth > defaultTabMaxWidth) {
+            // If the request tab max width is 0, or larger than our default, use the default
+            maxTabWidth = defaultTabMaxWidth;
+        }
+        mTabMaxWidth = maxTabWidth;
+    }
+
+    private void removeTabViewAt(int position) {
+        mTabStrip.removeViewAt(position);
+        requestLayout();
+    }
+
+    private void animateToTab(int newPosition) {
+        clearAnimation();
+
+        if (newPosition == Tab.INVALID_POSITION) {
+            return;
+        }
+        
+        if (getWindowToken() == null || !ViewCompat.isLaidOut(this)) {
+            // If we don't have a window token, or we haven't been laid out yet just draw the new
+            // position now
+            setScrollPosition(newPosition, 0f, true);
+            return;
+        }
+
+        final int startScrollX = getScrollX();
+        final int targetScrollX = calculateScrollXForTab(newPosition, 0);
+        final int duration = ANIMATION_DURATION;
+
+        if (startScrollX != targetScrollX) {
+            final Animation animation = new Animation() {
+                @Override
+                protected void applyTransformation(float interpolatedTime, Transformation t) {
+                    final float value = AnimationUtils.lerp(startScrollX, targetScrollX,
+                            interpolatedTime);
+                    scrollTo((int) value, 0);
+                }
+            };
+            animation.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+            animation.setDuration(duration);
+            startAnimation(animation);
+        }
+
+        // Now animate the indicator
+        mTabStrip.animateIndicatorToPosition(newPosition, duration);
+    }
+
+    private void setSelectedTabView(int position) {
+        final int tabCount = mTabStrip.getChildCount();
+        for (int i = 0; i < tabCount; i++) {
+            final View child = mTabStrip.getChildAt(i);
+            child.setSelected(i == position);
+        }
+    }
+
+    private static boolean isAnimationRunning(Animation animation) {
+        return animation != null && animation.hasStarted() && !animation.hasEnded();
+    }
+
+    void selectTab(Tab tab) {
+        if (mSelectedTab == tab) {
+            if (mSelectedTab != null) {
+                if (mOnTabSelectedListener != null) {
+                    mOnTabSelectedListener.onTabReselected(mSelectedTab);
+                }
+                animateToTab(tab.getPosition());
+            }
+        } else {
+            final int newPosition = tab != null ? tab.getPosition() : Tab.INVALID_POSITION;
+            setSelectedTabView(newPosition);
+
+            if ((mSelectedTab == null || mSelectedTab.getPosition() == Tab.INVALID_POSITION)
+                    && newPosition != Tab.INVALID_POSITION) {
+                // If we don't currently have a tab, just draw the indicator
+                setScrollPosition(newPosition, 0f, true);
+            } else {
+                animateToTab(newPosition);
+            }
+
+            if (mSelectedTab != null && mOnTabSelectedListener != null) {
+                mOnTabSelectedListener.onTabUnselected(mSelectedTab);
+            }
+            mSelectedTab = tab;
+            if (mSelectedTab != null && mOnTabSelectedListener != null) {
+                mOnTabSelectedListener.onTabSelected(mSelectedTab);
+            }
+        }
+    }
+
+    private int calculateScrollXForTab(int position, float positionOffset) {
+        if (mMode == MODE_SCROLLABLE) {
+            final View selectedChild = mTabStrip.getChildAt(position);
+            final View nextChild = position + 1 < mTabStrip.getChildCount()
+                    ? mTabStrip.getChildAt(position + 1)
+                    : null;
+            final int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0;
+            final int nextWidth = nextChild != null ? nextChild.getWidth() : 0;
+
+            return (int) (selectedChild.getLeft()
+                    + ((selectedWidth + nextWidth) * positionOffset * 0.5f)
+                    + selectedChild.getWidth() * 0.5f
+                    - getWidth() * 0.5f);
+        }
+        return 0;
+    }
+
+    private void applyModeAndGravity() {
+        int paddingStart = 0;
+        if (mMode == MODE_SCROLLABLE) {
+            // If we're scrollable, or fixed at start, inset using padding
+            paddingStart = Math.max(0, mContentInsetStart - mTabPaddingStart);
+        }
+        ViewCompat.setPaddingRelative(mTabStrip, paddingStart, 0, 0, 0);
+
+        switch (mMode) {
+            case MODE_FIXED:
+                mTabStrip.setGravity(Gravity.CENTER_HORIZONTAL);
+                break;
+            case MODE_SCROLLABLE:
+                mTabStrip.setGravity(GravityCompat.START);
+                break;
+        }
+
+        updateTabViewsLayoutParams();
+    }
+
+    private void updateTabViewsLayoutParams() {
+        for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+            View child = mTabStrip.getChildAt(i);
+            updateTabViewLayoutParams((LinearLayout.LayoutParams) child.getLayoutParams());
+            child.requestLayout();
+        }
+    }
+
+    /**
+     * A tab in this layout. Instances can be created via {@link #newTab()}.
+     */
+    public static final class Tab {
+
+        /**
+         * An invalid position for a tab.
+         *
+         * @see #getPosition()
+         */
+        public static final int INVALID_POSITION = -1;
+
+        private Object mTag;
+        private Drawable mIcon;
+        private CharSequence mText;
+        private CharSequence mContentDesc;
+        private int mPosition = INVALID_POSITION;
+        private View mCustomView;
+
+        private final TabLayout mParent;
+
+        Tab(TabLayout parent) {
+            mParent = parent;
+        }
+
+        /**
+         * @return This Tab's tag object.
+         */
+        public Object getTag() {
+            return mTag;
+        }
+
+        /**
+         * Give this Tab an arbitrary object to hold for later use.
+         *
+         * @param tag Object to store
+         * @return The current instance for call chaining
+         */
+        public Tab setTag(Object tag) {
+            mTag = tag;
+            return this;
+        }
+
+        View getCustomView() {
+            return mCustomView;
+        }
+
+        /**
+         * Set a custom view to be used for this tab. This overrides values set by {@link
+         * #setText(CharSequence)} and {@link #setIcon(Drawable)}.
+         *
+         * @param view Custom view to be used as a tab.
+         * @return The current instance for call chaining
+         */
+        public Tab setCustomView(View view) {
+            mCustomView = view;
+            if (mPosition >= 0) {
+                mParent.updateTab(mPosition);
+            }
+            return this;
+        }
+
+        /**
+         * Set a custom view to be used for this tab. This overrides values set by {@link
+         * #setText(CharSequence)} and {@link #setIcon(Drawable)}.
+         *
+         * @param layoutResId A layout resource to inflate and use as a custom tab view
+         * @return The current instance for call chaining
+         */
+        public Tab setCustomView(int layoutResId) {
+            return setCustomView(
+                    LayoutInflater.from(mParent.getContext()).inflate(layoutResId, null));
+        }
+
+        /**
+         * Return the icon associated with this tab.
+         *
+         * @return The tab's icon
+         */
+        public Drawable getIcon() {
+            return mIcon;
+        }
+
+        /**
+         * Return the current position of this tab in the action bar.
+         *
+         * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
+         * the action bar.
+         */
+        public int getPosition() {
+            return mPosition;
+        }
+
+        void setPosition(int position) {
+            mPosition = position;
+        }
+
+        /**
+         * Return the text of this tab.
+         *
+         * @return The tab's text
+         */
+        public CharSequence getText() {
+            return mText;
+        }
+
+        /**
+         * Set the icon displayed on this tab.
+         *
+         * @param icon The drawable to use as an icon
+         * @return The current instance for call chaining
+         */
+        public Tab setIcon(Drawable icon) {
+            mIcon = icon;
+            if (mPosition >= 0) {
+                mParent.updateTab(mPosition);
+            }
+            return this;
+        }
+
+        /**
+         * Set the icon displayed on this tab.
+         *
+         * @param resId A resource ID referring to the icon that should be displayed
+         * @return The current instance for call chaining
+         */
+        public Tab setIcon(int resId) {
+            return setIcon(TintManager.getDrawable(mParent.getContext(), resId));
+        }
+
+        /**
+         * Set the text displayed on this tab. Text may be truncated if there is not room to display
+         * the entire string.
+         *
+         * @param text The text to display
+         * @return The current instance for call chaining
+         */
+        public Tab setText(CharSequence text) {
+            mText = text;
+            if (mPosition >= 0) {
+                mParent.updateTab(mPosition);
+            }
+            return this;
+        }
+
+        /**
+         * Set the text displayed on this tab. Text may be truncated if there is not room to display
+         * the entire string.
+         *
+         * @param resId A resource ID referring to the text that should be displayed
+         * @return The current instance for call chaining
+         */
+        public Tab setText(int resId) {
+            return setText(mParent.getResources().getText(resId));
+        }
+
+        /**
+         * Select this tab. Only valid if the tab has been added to the action bar.
+         */
+        public void select() {
+            mParent.selectTab(this);
+        }
+
+        /**
+         * Set a description of this tab's content for use in accessibility support. If no content
+         * description is provided the title will be used.
+         *
+         * @param resId A resource ID referring to the description text
+         * @return The current instance for call chaining
+         * @see #setContentDescription(CharSequence)
+         * @see #getContentDescription()
+         */
+        public Tab setContentDescription(int resId) {
+            return setContentDescription(mParent.getResources().getText(resId));
+        }
+
+        /**
+         * Set a description of this tab's content for use in accessibility support. If no content
+         * description is provided the title will be used.
+         *
+         * @param contentDesc Description of this tab's content
+         * @return The current instance for call chaining
+         * @see #setContentDescription(int)
+         * @see #getContentDescription()
+         */
+        public Tab setContentDescription(CharSequence contentDesc) {
+            mContentDesc = contentDesc;
+            if (mPosition >= 0) {
+                mParent.updateTab(mPosition);
+            }
+            return this;
+        }
+
+        /**
+         * Gets a brief description of this tab's content for use in accessibility support.
+         *
+         * @return Description of this tab's content
+         * @see #setContentDescription(CharSequence)
+         * @see #setContentDescription(int)
+         */
+        public CharSequence getContentDescription() {
+            return mContentDesc;
+        }
+    }
+
+    class TabView extends LinearLayout implements OnLongClickListener {
+        private final Tab mTab;
+        private TextView mTextView;
+        private ImageView mIconView;
+        private View mCustomView;
+
+        public TabView(Context context, Tab tab) {
+            super(context);
+            mTab = tab;
+            if (mTabBackgroundResId != 0) {
+                setBackgroundDrawable(TintManager.getDrawable(context, mTabBackgroundResId));
+            }
+            ViewCompat.setPaddingRelative(this, mTabPaddingStart, mTabPaddingTop,
+                    mTabPaddingEnd, mTabPaddingBottom);
+            setGravity(Gravity.CENTER);
+            update();
+        }
+
+        @Override
+        public void setSelected(boolean selected) {
+            final boolean changed = (isSelected() != selected);
+            super.setSelected(selected);
+            if (changed && selected) {
+                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+
+                if (mTextView != null) {
+                    mTextView.setSelected(selected);
+                }
+                if (mIconView != null) {
+                    mIconView.setSelected(selected);
+                }
+            }
+        }
+
+        @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+        @Override
+        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+            super.onInitializeAccessibilityEvent(event);
+            // This view masquerades as an action bar tab.
+            event.setClassName(ActionBar.Tab.class.getName());
+        }
+
+        @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+        @Override
+        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(info);
+            // This view masquerades as an action bar tab.
+            info.setClassName(ActionBar.Tab.class.getName());
+        }
+
+        @Override
+        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+            if (getMeasuredWidth() > mTabMaxWidth) {
+                // Re-measure if we went beyond our maximum size.
+                super.onMeasure(MeasureSpec.makeMeasureSpec(
+                        mTabMaxWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
+            } else if (mTabMinWidth > 0 && getMeasuredHeight() < mTabMinWidth) {
+                // Re-measure if we're below our minimum size.
+                super.onMeasure(MeasureSpec.makeMeasureSpec(
+                        mTabMinWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
+            }
+        }
+
+        final void update() {
+            final Tab tab = mTab;
+            final View custom = tab.getCustomView();
+            if (custom != null) {
+                final ViewParent customParent = custom.getParent();
+                if (customParent != this) {
+                    if (customParent != null) {
+                        ((ViewGroup) customParent).removeView(custom);
+                    }
+                    addView(custom);
+                }
+                mCustomView = custom;
+                if (mTextView != null) {
+                    mTextView.setVisibility(GONE);
+                }
+                if (mIconView != null) {
+                    mIconView.setVisibility(GONE);
+                    mIconView.setImageDrawable(null);
+                }
+            } else {
+                if (mCustomView != null) {
+                    removeView(mCustomView);
+                    mCustomView = null;
+                }
+
+                final Drawable icon = tab.getIcon();
+                final CharSequence text = tab.getText();
+
+                if (icon != null) {
+                    if (mIconView == null) {
+                        ImageView iconView = new ImageView(getContext());
+                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                                LayoutParams.WRAP_CONTENT);
+                        lp.gravity = Gravity.CENTER_VERTICAL;
+                        iconView.setLayoutParams(lp);
+                        addView(iconView, 0);
+                        mIconView = iconView;
+                    }
+                    mIconView.setImageDrawable(icon);
+                    mIconView.setVisibility(VISIBLE);
+                } else if (mIconView != null) {
+                    mIconView.setVisibility(GONE);
+                    mIconView.setImageDrawable(null);
+                }
+
+                final boolean hasText = !TextUtils.isEmpty(text);
+                if (hasText) {
+                    if (mTextView == null) {
+                        AppCompatTextView textView = new AppCompatTextView(getContext());
+                        textView.setTextAppearance(getContext(), mTabTextAppearance);
+                        textView.setMaxLines(MAX_TAB_TEXT_LINES);
+                        textView.setEllipsize(TextUtils.TruncateAt.END);
+                        textView.setGravity(Gravity.CENTER);
+                        if (mTabSelectedTextColorSet) {
+                            textView.setTextColor(createColorStateList(
+                                    textView.getCurrentTextColor(), mTabSelectedTextColor));
+                        }
+
+                        addView(textView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+                        mTextView = textView;
+                    }
+                    mTextView.setText(text);
+                    mTextView.setVisibility(VISIBLE);
+                } else if (mTextView != null) {
+                    mTextView.setVisibility(GONE);
+                    mTextView.setText(null);
+                }
+
+                if (mIconView != null) {
+                    mIconView.setContentDescription(tab.getContentDescription());
+                }
+
+                if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
+                    setOnLongClickListener(this);
+                } else {
+                    setOnLongClickListener(null);
+                    setLongClickable(false);
+                }
+            }
+        }
+
+        @Override
+        public boolean onLongClick(View v) {
+            final int[] screenPos = new int[2];
+            getLocationOnScreen(screenPos);
+
+            final Context context = getContext();
+            final int width = getWidth();
+            final int height = getHeight();
+            final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+
+            Toast cheatSheet = Toast.makeText(context, mTab.getContentDescription(),
+                    Toast.LENGTH_SHORT);
+            // Show under the tab
+            cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
+                    (screenPos[0] + width / 2) - screenWidth / 2, height);
+
+            cheatSheet.show();
+            return true;
+        }
+
+        private ColorStateList createColorStateList(int defaultColor, int selectedColor) {
+            final int[][] states = new int[2][];
+            final int[] colors = new int[2];
+            int i = 0;
+
+            states[i] = SELECTED_STATE_SET;
+            colors[i] = selectedColor;
+            i++;
+
+            // Default enabled state
+            states[i] = EMPTY_STATE_SET;
+            colors[i] = defaultColor;
+            i++;
+
+            return new ColorStateList(states, colors);
+        }
+
+        public Tab getTab() {
+            return mTab;
+        }
+    }
+
+    private class SlidingTabStrip extends LinearLayout {
+        private int mSelectedIndicatorHeight;
+        private final Paint mSelectedIndicatorPaint;
+
+        private int mSelectedPosition = -1;
+        private float mSelectionOffset;
+
+        private int mIndicatorLeft = -1;
+        private int mIndicatorRight = -1;
+
+        SlidingTabStrip(Context context) {
+            super(context);
+            setWillNotDraw(false);
+            mSelectedIndicatorPaint = new Paint();
+        }
+
+        void setSelectedIndicatorColor(int color) {
+            mSelectedIndicatorPaint.setColor(color);
+            ViewCompat.postInvalidateOnAnimation(this);
+        }
+
+        void setSelectedIndicatorHeight(int height) {
+            mSelectedIndicatorHeight = height;
+            ViewCompat.postInvalidateOnAnimation(this);
+        }
+
+        void setIndicatorPositionFromTabPosition(int position, float positionOffset) {
+            if (isAnimationRunning(getAnimation())) {
+                return;
+            }
+            mSelectedPosition = position;
+            mSelectionOffset = positionOffset;
+            updateIndicatorPosition();
+        }
+
+        @Override
+        protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+            if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) {
+                // HorizontalScrollView will first measure use with UNSPECIFIED, and then with
+                // EXACTLY. Ignore the first call since anything we do will be overwritten anyway
+                return;
+            }
+
+            if (mMode == MODE_FIXED && mTabGravity == GRAVITY_CENTER) {
+                final int count = getChildCount();
+
+                final int unspecifiedSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+
+                // First we'll find the largest tab
+                int largestTabWidth = 0;
+                for (int i = 0, z = count; i < z; i++) {
+                    final View child = getChildAt(i);
+                    child.measure(unspecifiedSpec, heightMeasureSpec);
+                    largestTabWidth = Math.max(largestTabWidth, child.getMeasuredWidth());
+                }
+
+                if (largestTabWidth <= 0) {
+                    // If we don't have a largest child yet, skip until the next measure pass
+                    return;
+                }
+
+                final int gutter = dpToPx(FIXED_WRAP_GUTTER_MIN);
+                if (largestTabWidth * count <= getMeasuredWidth() - gutter * 2) {
+                    // If the tabs fit within our width minus gutters, we will set all tabs to have
+                    // the same width
+                    for (int i = 0; i < count; i++) {
+                        final View child = getChildAt(i);
+                        final LinearLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                        lp.width = largestTabWidth;
+                        lp.weight = 0;
+                    }
+                } else {
+                    // If the tabs will wrap to be larger than the width minus gutters, we need
+                    // to switch to GRAVITY_FILL
+                    mTabGravity = GRAVITY_FILL;
+                    updateTabViewsLayoutParams();
+                }
+
+                // Now re-measure after our changes
+                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int l, int t, int r, int b) {
+            super.onLayout(changed, l, t, r, b);
+
+            if (!isAnimationRunning(getAnimation())) {
+                // If we've been layed out, and we're not currently in an animation, update the
+                // indicator position
+                updateIndicatorPosition();
+            }
+        }
+
+        private void updateIndicatorPosition() {
+            final View selectedTitle = getChildAt(mSelectedPosition);
+            int left, right;
+
+            if (selectedTitle != null && selectedTitle.getWidth() > 0) {
+                left = selectedTitle.getLeft();
+                right = selectedTitle.getRight();
+
+                if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) {
+                    // Draw the selection partway between the tabs
+                    View nextTitle = getChildAt(mSelectedPosition + 1);
+                    left = (int) (mSelectionOffset * nextTitle.getLeft() +
+                            (1.0f - mSelectionOffset) * left);
+                    right = (int) (mSelectionOffset * nextTitle.getRight() +
+                            (1.0f - mSelectionOffset) * right);
+                }
+            } else {
+                left = right = -1;
+            }
+
+            setIndicatorPosition(left, right);
+        }
+
+        private void setIndicatorPosition(int left, int right) {
+            if (left != mIndicatorLeft || right != mIndicatorRight) {
+                // If the indicator's left/right has changed, invalidate
+                mIndicatorLeft = left;
+                mIndicatorRight = right;
+                ViewCompat.postInvalidateOnAnimation(this);
+            }
+        }
+
+        void animateIndicatorToPosition(final int position, int duration) {
+            final boolean isRtl = ViewCompat.getLayoutDirection(this)
+                    == ViewCompat.LAYOUT_DIRECTION_RTL;
+
+            final View targetView = getChildAt(position);
+            final int targetLeft = targetView.getLeft();
+            final int targetRight = targetView.getRight();
+            final int startLeft;
+            final int startRight;
+
+            if (Math.abs(position - mSelectedPosition) <= 1) {
+                // If the views are adjacent, we'll animate from edge-to-edge
+                startLeft = mIndicatorLeft;
+                startRight = mIndicatorRight;
+            } else {
+                // Else, we'll just grow from the nearest edge
+                final int offset = dpToPx(MOTION_NON_ADJACENT_OFFSET);
+                if (position < mSelectedPosition) {
+                    // We're going end-to-start
+                    if (isRtl) {
+                        startLeft = startRight = targetLeft - offset;
+                    } else {
+                        startLeft = startRight = targetRight + offset;
+                    }
+                } else {
+                    // We're going start-to-end
+                    if (isRtl) {
+                        startLeft = startRight = targetRight + offset;
+                    } else {
+                        startLeft = startRight = targetLeft - offset;
+                    }
+                }
+            }
+
+            if (startLeft != targetLeft || startRight != targetRight) {
+                final Animation anim = new Animation() {
+                    @Override
+                    protected void applyTransformation(float interpolatedTime, Transformation t) {
+                        setIndicatorPosition(
+                                (int) AnimationUtils.lerp(startLeft, targetLeft, interpolatedTime),
+                                (int) AnimationUtils.lerp(startRight, targetRight, interpolatedTime));
+                    }
+                };
+                anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+                anim.setDuration(duration);
+                anim.setAnimationListener(new Animation.AnimationListener() {
+                    @Override
+                    public void onAnimationStart(Animation animation) {
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animation animation) {
+                        mSelectedPosition = position;
+                        mSelectionOffset = 0f;
+                    }
+
+                    @Override
+                    public void onAnimationRepeat(Animation animation) {
+                    }
+                });
+
+                startAnimation(anim);
+            }
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            // Thick colored underline below the current selection
+            if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
+                canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
+                        mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
+            }
+        }
+    }
+
+}
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
new file mode 100644
index 0000000..4992d7f
--- /dev/null
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -0,0 +1,439 @@
+/*
+ * 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.design.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Handler;
+import android.os.Message;
+import android.support.design.R;
+import android.support.v4.view.AccessibilityDelegateCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * Layout which wraps an {@link android.widget.EditText} to show a floating label when
+ * the hint is hidden due to the user inputting text.
+ */
+public class TextInputLayout extends LinearLayout {
+
+    private static final long ANIMATION_DURATION = 200;
+    private static final int MSG_UPDATE_LABEL = 0;
+
+    private EditText mEditText;
+    private CharSequence mHint;
+
+    private boolean mErrorEnabled;
+    private TextView mErrorView;
+    private int mErrorTextAppearance;
+
+    private ColorStateList mLabelTextColor;
+
+    private final CollapsingTextHelper mCollapsingTextHelper;
+    private final Handler mHandler;
+
+    public TextInputLayout(Context context) {
+        this(context, null);
+    }
+
+    public TextInputLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        setOrientation(VERTICAL);
+        setWillNotDraw(false);
+
+        mCollapsingTextHelper = new CollapsingTextHelper(this);
+        mHandler = new Handler(new Handler.Callback() {
+            @Override
+            public boolean handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_UPDATE_LABEL:
+                        updateLabelVisibility(true);
+                        return true;
+                }
+                return false;
+            }
+        });
+
+        mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+        mCollapsingTextHelper.setPositionInterpolator(new AccelerateInterpolator());
+        mCollapsingTextHelper.setCollapsedTextVerticalGravity(Gravity.TOP);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);
+        mHint = a.getText(R.styleable.TextInputLayout_android_hint);
+
+        final int hintAppearance = a.getResourceId(
+                R.styleable.TextInputLayout_hintTextAppearance, -1);
+        if (hintAppearance != -1) {
+            mCollapsingTextHelper.setCollapsedTextAppearance(hintAppearance);
+        }
+
+        mErrorTextAppearance = a.getResourceId(R.styleable.TextInputLayout_errorTextAppearance, 0);
+        final boolean errorEnabled = a.getBoolean(R.styleable.TextInputLayout_errorEnabled, false);
+
+        // We create a ColorStateList using the specified text color, combining it with our
+        // theme's textColorHint
+        mLabelTextColor = createLabelTextColorStateList(
+                mCollapsingTextHelper.getCollapsedTextColor());
+
+        mCollapsingTextHelper.setCollapsedTextColor(mLabelTextColor.getDefaultColor());
+        mCollapsingTextHelper.setExpandedTextColor(mLabelTextColor.getDefaultColor());
+
+        a.recycle();
+
+        if (errorEnabled) {
+            setErrorEnabled(true);
+        }
+
+        if (ViewCompat.getImportantForAccessibility(this)
+                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            // Make sure we're important for accessibility if we haven't been explicitly not
+            ViewCompat.setImportantForAccessibility(this,
+                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
+
+        ViewCompat.setAccessibilityDelegate(this, new TextInputAccessibilityDelegate());
+    }
+
+    @Override
+    public void addView(View child, int index, ViewGroup.LayoutParams params) {
+        if (child instanceof EditText) {
+            params = setEditText((EditText) child, params);
+            super.addView(child, 0, params);
+        } else {
+            // Carry on adding the View...
+            super.addView(child, index, params);
+        }
+    }
+
+    private LayoutParams setEditText(EditText editText, ViewGroup.LayoutParams lp) {
+        // If we already have an EditText, throw an exception
+        if (mEditText != null) {
+            throw new IllegalArgumentException("We already have an EditText, can only have one");
+        }
+        mEditText = editText;
+
+        // Use the EditText's text size for our expanded text
+        mCollapsingTextHelper.setExpandedTextSize(mEditText.getTextSize());
+
+        // Add a TextWatcher so that we know when the text input has changed
+        mEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void afterTextChanged(Editable s) {
+                mHandler.sendEmptyMessage(MSG_UPDATE_LABEL);
+            }
+
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+            }
+        });
+
+        // Add focus listener to the EditText so that we can notify the label that it is activated.
+        // Allows the use of a ColorStateList for the text color on the label
+        mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View view, boolean focused) {
+                mHandler.sendEmptyMessage(MSG_UPDATE_LABEL);
+            }
+        });
+
+        // If we do not have a valid hint, try and retrieve it from the EditText
+        if (TextUtils.isEmpty(mHint)) {
+            setHint(mEditText.getHint());
+            // Clear the EditText's hint as we will display it ourselves
+            mEditText.setHint(null);
+        }
+
+        if (mErrorView != null) {
+            // Add some start/end padding to the error so that it matches the EditText
+            ViewCompat.setPaddingRelative(mErrorView, ViewCompat.getPaddingStart(mEditText),
+                    0, ViewCompat.getPaddingEnd(mEditText), mEditText.getPaddingBottom());
+        }
+
+        // Update the label visibility with no animation
+        updateLabelVisibility(false);
+
+        // Create a new FrameLayout.LayoutParams so that we can add enough top margin
+        // to the EditText so make room for the label
+        LayoutParams newLp = new LayoutParams(lp);
+        Paint paint = new Paint();
+        paint.setTextSize(mCollapsingTextHelper.getExpandedTextSize());
+        newLp.topMargin = (int) -paint.ascent();
+
+        return newLp;
+    }
+
+    private void updateLabelVisibility(boolean animate) {
+        boolean hasText = !TextUtils.isEmpty(mEditText.getText());
+        boolean isFocused = mEditText.isFocused();
+
+        mCollapsingTextHelper.setCollapsedTextColor(mLabelTextColor.getColorForState(
+                isFocused ? FOCUSED_STATE_SET : EMPTY_STATE_SET,
+                mLabelTextColor.getDefaultColor()));
+
+        if (hasText || isFocused) {
+            // We should be showing the label so do so if it isn't already
+            collapseHint(animate);
+        } else {
+            // We should not be showing the label so hide it
+            expandHint(animate);
+        }
+    }
+
+    /**
+     * @return the {@link android.widget.EditText} text input
+     */
+    public EditText getEditText() {
+        return mEditText;
+    }
+
+    /**
+     * Set the hint to be displayed in the floating label
+     */
+    public void setHint(CharSequence hint) {
+        mHint = hint;
+        mCollapsingTextHelper.setText(hint);
+
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+    }
+
+    /**
+     * Whether the error functionality is enabled or not in this layout. Enabling this
+     * functionality before setting an error message via {@link #setError(CharSequence)}, will mean
+     * that this layout will not change size when an error is displayed.
+     *
+     * @attr R.attr.errorEnabled
+     */
+    public void setErrorEnabled(boolean enabled) {
+        if (mErrorEnabled != enabled) {
+            if (enabled) {
+                mErrorView = new TextView(getContext());
+                mErrorView.setTextAppearance(getContext(), mErrorTextAppearance);
+                mErrorView.setVisibility(INVISIBLE);
+                addView(mErrorView);
+
+                if (mEditText != null) {
+                    // Add some start/end padding to the error so that it matches the EditText
+                    ViewCompat.setPaddingRelative(mErrorView, ViewCompat.getPaddingStart(mEditText),
+                            0, ViewCompat.getPaddingEnd(mEditText), mEditText.getPaddingBottom());
+                }
+            } else {
+                removeView(mErrorView);
+                mErrorView = null;
+            }
+            mErrorEnabled = enabled;
+        }
+    }
+
+    /**
+     * Sets an error message that will be displayed below our {@link EditText}. If the
+     * {@code error} is {@code null}, the error message will be cleared.
+     * <p>
+     * If the error functionality has not been enabled via {@link #setErrorEnabled(boolean)}, then
+     * it will be automatically enabled if {@code error} is not empty.
+     *
+     * @param error Error message to display, or null to clear
+     */
+    public void setError(CharSequence error) {
+        if (!mErrorEnabled) {
+            if (TextUtils.isEmpty(error)) {
+                // If error isn't enabled, and the error is empty, just return
+                return;
+            }
+            // Else, we'll assume that they want to enable the error functionality
+            setErrorEnabled(true);
+        }
+
+        if (!TextUtils.isEmpty(error)) {
+            mErrorView.setText(error);
+            mErrorView.setVisibility(VISIBLE);
+            ViewCompat.setAlpha(mErrorView, 0f);
+            ViewCompat.animate(mErrorView)
+                    .alpha(1f)
+                    .setDuration(ANIMATION_DURATION)
+                    .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
+                    .setListener(null)
+                    .start();
+        } else {
+            if (mErrorView.getVisibility() == VISIBLE) {
+                ViewCompat.animate(mErrorView)
+                        .alpha(0f)
+                        .setDuration(ANIMATION_DURATION)
+                        .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
+                        .setListener(new ViewPropertyAnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationEnd(View view) {
+                                mErrorView.setText(null);
+                                mErrorView.setVisibility(INVISIBLE);
+                            }
+                        }).start();
+            }
+        }
+
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        mCollapsingTextHelper.draw(canvas);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        mCollapsingTextHelper.onLayout(changed, left, top, right, bottom);
+
+        if (mEditText != null) {
+            final int l = mEditText.getLeft() + mEditText.getPaddingLeft();
+            final int r = mEditText.getRight() - mEditText.getPaddingRight();
+
+            mCollapsingTextHelper.setExpandedBounds(l,
+                    mEditText.getTop() + mEditText.getPaddingTop(),
+                    r, mEditText.getBottom() - mEditText.getPaddingBottom());
+
+            // Set the collapsed bounds to be the the full height (minus padding) to match the
+            // EditText's editable area
+            mCollapsingTextHelper.setCollapsedBounds(l, getPaddingTop(),
+                    r, bottom - top - getPaddingBottom());
+        }
+    }
+
+    private void collapseHint(boolean animate) {
+        if (animate) {
+            animateToExpansionFraction(1f);
+        } else {
+            mCollapsingTextHelper.setExpansionFraction(1f);
+        }
+    }
+
+    private void expandHint(boolean animate) {
+        if (animate) {
+            animateToExpansionFraction(0f);
+        } else {
+            mCollapsingTextHelper.setExpansionFraction(0f);
+        }
+    }
+
+    private void animateToExpansionFraction(final float target) {
+        final float current = mCollapsingTextHelper.getExpansionFraction();
+
+        Animation anim = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                mCollapsingTextHelper.setExpansionFraction(
+                        AnimationUtils.lerp(current, target, interpolatedTime));
+            }
+        };
+        anim.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR);
+        anim.setDuration(ANIMATION_DURATION);
+        startAnimation(anim);
+    }
+
+    private ColorStateList createLabelTextColorStateList(int color) {
+        final int[][] states = new int[2][];
+        final int[] colors = new int[2];
+        int i = 0;
+
+        // Focused
+        states[i] = FOCUSED_STATE_SET;
+        colors[i] = color;
+        i++;
+
+        states[i] = EMPTY_STATE_SET;
+        colors[i] = getThemeAttrColor(android.R.attr.textColorHint);
+        i++;
+
+        return new ColorStateList(states, colors);
+    }
+
+    private int getThemeAttrColor(int attr) {
+        TypedValue tv = new TypedValue();
+        if (getContext().getTheme().resolveAttribute(attr, tv, true)) {
+            return tv.data;
+        } else {
+            return Color.MAGENTA;
+        }
+    }
+
+    private class TextInputAccessibilityDelegate extends AccessibilityDelegateCompat {
+        @Override
+        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+            super.onInitializeAccessibilityEvent(host, event);
+            event.setClassName(TextInputLayout.class.getSimpleName());
+        }
+
+        @Override
+        public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+            super.onPopulateAccessibilityEvent(host, event);
+
+            final CharSequence text = mCollapsingTextHelper.getText();
+            if (!TextUtils.isEmpty(text)) {
+                event.getText().add(text);
+            }
+        }
+
+        @Override
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+            super.onInitializeAccessibilityNodeInfo(host, info);
+            info.setClassName(TextInputLayout.class.getSimpleName());
+
+            final CharSequence text = mCollapsingTextHelper.getText();
+            if (!TextUtils.isEmpty(text)) {
+                info.setText(text);
+            }
+            if (mEditText != null) {
+                info.setLabelFor(mEditText);
+            }
+            final CharSequence error = mErrorView != null ? mErrorView.getText() : null;
+            if (!TextUtils.isEmpty(error)) {
+                info.setContentInvalid(true);
+                info.setError(error);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/media/Android.mk b/media/Android.mk
new file mode 100644
index 0000000..14ff0aa
--- /dev/null
+++ b/media/Android.mk
@@ -0,0 +1,16 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/protocols/Android.mk b/media/protocols/Android.mk
new file mode 100644
index 0000000..f4f0202
--- /dev/null
+++ b/media/protocols/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-media-protocols
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_JAVA_LIBRARIES := android-support-annotations
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/media/protocols/README.txt b/media/protocols/README.txt
new file mode 100644
index 0000000..12c24cd
--- /dev/null
+++ b/media/protocols/README.txt
@@ -0,0 +1,8 @@
+Media protocol declarations.
+
+This library contains declarations for protocols used by
+applications to communicate with services provided by media
+routes.  For example, the MediaPlayerProtocol defines a protocol
+by which an application may enqueue content to play on a
+remote media device.
+
diff --git a/media/protocols/build.gradle b/media/protocols/build.gradle
new file mode 100644
index 0000000..254770c
--- /dev/null
+++ b/media/protocols/build.gradle
@@ -0,0 +1,34 @@
+apply plugin: 'java'
+
+archivesBaseName = 'support-media-protocols'
+
+sourceSets {
+    main.java.srcDir 'src'
+}
+
+jar {
+    from sourceSets.main.output
+}
+
+// configuration for the javadoc to include all source sets.
+javadoc {
+    source    sourceSets.main.allJava
+}
+
+// custom tasks for creating source/javadoc jars
+task sourcesJar(type: Jar, dependsOn:classes) {
+    classifier = 'sources'
+    from sourceSets.main.allSource
+}
+
+task javadocJar(type: Jar, dependsOn:javadoc) {
+    classifier         'javadoc'
+    from               javadoc.destinationDir
+}
+
+// add javadoc/source jar tasks as artifacts
+artifacts {
+    archives jar
+    archives sourcesJar
+    archives javadocJar
+}
diff --git a/media/protocols/src/android/support/media/protocols/MediaPlayerProtocol.java b/media/protocols/src/android/support/media/protocols/MediaPlayerProtocol.java
new file mode 100644
index 0000000..3dc72dc
--- /dev/null
+++ b/media/protocols/src/android/support/media/protocols/MediaPlayerProtocol.java
@@ -0,0 +1,2179 @@
+/*
+ * Copyright (C) 2014 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.media.protocols;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcelable;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.view.accessibility.CaptioningManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Media route protocol for managing a queue of media to be played remotely
+ * by a media device.
+ */
+public class MediaPlayerProtocol extends MediaRouteProtocol {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = { RESUME_STATE_UNCHANGED, RESUME_STATE_PLAY,
+            RESUME_STATE_PAUSE })
+    public @interface ResumeState { }
+
+    /**
+     * A resume state indicating that the player state should be left unchanged.
+     */
+    public static final int RESUME_STATE_UNCHANGED = 0;
+
+    /**
+     * A resume state indicating that the player should be playing,
+     * regardless of its current state.
+     */
+    public static final int RESUME_STATE_PLAY = 1;
+
+    /**
+     * A resume state indicating that the player should be paused,
+     * regardless of its current state.
+     */
+    public static final int RESUME_STATE_PAUSE = 2;
+
+    /**
+     * Creates the protocol client object for an application to use to send
+     * messages to a media route.
+     * <p>
+     * This constructor is called automatically if you use
+     * {@link android.media.routing.MediaRouter.ConnectionInfo#getProtocolObject getProtocolObject}
+     * to obtain a protocol object from a media route connection.
+     * </p>
+     *
+     * @param binder The remote binder supplied by the media route service.  May be
+     * obtained using {@link android.media.routing.MediaRouter.ConnectionInfo#getProtocolBinder}
+     * on a route connection.
+     */
+    public MediaPlayerProtocol(@NonNull IBinder binder) {
+        super(binder);
+    }
+
+    /**
+     * Loads and optionally starts playback of a new media item.
+     * The media item starts playback at playPosition.
+     *
+     * @param mediaInfo An object describing the media item to load.
+     * @param autoplay Whether playback should start immediately.
+     * @param playPosition The initial playback position, in milliseconds from the
+     * beginning of the stream.
+     * @param extras Custom application-specific data to pass along with the request.
+     */
+    public void load(@NonNull MediaInfo mediaInfo, boolean autoplay,
+            long playPosition, @Nullable Bundle extras) {
+        if (mediaInfo == null) {
+            throw new IllegalArgumentException("mediaInfo must not be null");
+        }
+        Bundle args = new Bundle();
+        args.putBundle("mediaInfo", mediaInfo.toBundle());
+        args.putBoolean("autoplay", autoplay);
+        args.putLong("playPosition", playPosition);
+        args.putBundle("extras", extras);
+        sendRequest("load", args);
+    }
+
+    /**
+     * Begins or resumes playback of the current media item.
+     *
+     * @param extras Custom application-specific data to pass along with the request.
+     */
+    public void play(@Nullable Bundle extras) {
+        Bundle args = new Bundle();
+        args.putBundle("extras", extras);
+        sendRequest("play", args);
+    }
+
+    /**
+     * Pauses playback of the current media item.
+     *
+     * @param extras Custom application-specific data to pass along with the request.
+     */
+    public void pause(@Nullable Bundle extras) {
+        Bundle args = new Bundle();
+        args.putBundle("extras", extras);
+        sendRequest("pause", args);
+    }
+
+    /**
+     * Requests updated media status information from the receiver.
+     *
+     * @param extras Custom application-specific data to pass along with the request.
+     */
+    public void requestStatus(@Nullable Bundle extras) {
+        Bundle args = new Bundle();
+        args.putBundle("extras", extras);
+        sendRequest("requestStatus", args);
+    }
+
+    /**
+     * Seeks to a new position within the current media item.
+     *
+     * @param position The new position, in milliseconds from the beginning of the stream.
+     * @param resumeState The action to take after the seek operation has finished.
+     * @param extras Custom application-specific data to pass along with the request.
+     */
+    public void seek(long position, @ResumeState int resumeState, @Nullable Bundle extras) {
+        Bundle args = new Bundle();
+        args.putLong("position", position);
+        args.putInt("resumeState", resumeState);
+        args.putBundle("extras", extras);
+        sendRequest("seek", args);
+    }
+
+    /**
+     * Sets the active media tracks.
+     *
+     * @param trackIds The media track IDs.
+     * @param extras Custom application-specific data to pass along with the request.
+     */
+    public void setActiveMediaTracks(@NonNull long[] trackIds, @Nullable Bundle extras) {
+        if (trackIds == null) {
+            throw new IllegalArgumentException("trackIds must not be null");
+        }
+        Bundle args = new Bundle();
+        args.putLongArray("trackIds", trackIds);
+        args.putBundle("extras", extras);
+        sendRequest("setActiveMediaTracks", args);
+    }
+
+    /**
+     * Toggles the stream muting.
+     *
+     * @param muteState Whether the stream should be muted or unmuted.
+     * @param extras Custom application-specific data to pass along with the request.
+     */
+    public void setStreamMute(boolean muteState, @Nullable Bundle extras) {
+        Bundle args = new Bundle();
+        args.putBoolean("muteState", muteState);
+        args.putBundle("extras", extras);
+        sendRequest("setStreamMute", args);
+    }
+
+    /**
+     * Sets the stream volume.
+     * If volume is outside of the range [0.0, 1.0], then the value will be clipped.
+     *
+     * @param volume The new volume, in the range [0.0 - 1.0].
+     * @param extras Custom application-specific data to pass along with the request.
+     */
+    public void setStreamVolume(int volume, @Nullable Bundle extras) {
+        Bundle args = new Bundle();
+        args.putInt("volume", volume);
+        args.putBundle("extras", extras);
+        sendRequest("setStreamVolume", args);
+    }
+
+    /**
+     * Sets the text track style.
+     *
+     * @param trackStyle The track style.
+     * @param extras Custom application-specific data to pass along with the request.
+     */
+    public void setTextTrackStyle(@NonNull TextTrackStyle trackStyle, @Nullable Bundle extras) {
+        if (trackStyle == null) {
+            throw new IllegalArgumentException("trackStyle must not be null");
+        }
+        Bundle args = new Bundle();
+        args.putBundle("trackStyle", trackStyle.toBundle());
+        args.putBundle("extras", extras);
+        sendRequest("setTextTrackStyle", args);
+    }
+
+    /**
+     * Stops playback of the current media item.
+     *
+     * @param extras Custom application-specific data to pass along with the request.
+     */
+    public void stop(@Nullable Bundle extras) {
+        Bundle args = new Bundle();
+        args.putBundle("extras", extras);
+        sendRequest("stop", args);
+    }
+
+    /**
+     * Media player callbacks.
+     */
+    public static abstract class Callback extends MediaRouteProtocol.Callback {
+        /**
+         * Called when updated player status information is received.
+         *
+         * @param status The updated status, or null if none.
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onStatusUpdated(@Nullable MediaStatus status,
+                @Nullable Bundle extras) { }
+
+        @Override
+        public void onEvent(String event, Bundle args) {
+            switch (event) {
+                case "statusUpdated":
+                    onStatusUpdated(MediaStatus.fromBundle(args.getBundle("status")),
+                            args.getBundle("extras"));
+                    return;
+            }
+            super.onEvent(event, args);
+        }
+    }
+
+    /**
+     * Media player stubs.
+     */
+    public static abstract class Stub extends MediaRouteProtocol.Stub {
+        /**
+         * Creates an implementation of a media route protocol.
+         *
+         * @param handler The handler on which to receive requests, or null to use
+         * the current looper thread.
+         */
+        public Stub(@Nullable Handler handler) {
+            super(handler);
+        }
+
+        /**
+         * Loads and optionally starts playback of a new media item.
+         * The media item starts playback at playPosition.
+         *
+         * @param mediaInfo An object describing the media item to load.
+         * @param autoplay Whether playback should start immediately.
+         * @param playPosition The initial playback position, in milliseconds from the
+         * beginning of the stream.
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onLoad(@NonNull MediaInfo mediaInfo, boolean autoplay,
+                long playPosition, @Nullable Bundle extras) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Begins or resumes playback of the current media item.
+         *
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onPlay(@Nullable Bundle extras) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Pauses playback of the current media item.
+         *
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onPause(@Nullable Bundle extras) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Requests updated media status information from the receiver.
+         *
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onRequestStatus(@Nullable Bundle extras) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Seeks to a new position within the current media item.
+         *
+         * @param position The new position, in milliseconds from the beginning of the stream.
+         * @param resumeState The action to take after the seek operation has finished.
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onSeek(long position, @ResumeState int resumeState, @Nullable Bundle extras) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Sets the active media tracks.
+         *
+         * @param trackIds The media track IDs.
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onSetActiveMediaTracks(long[] trackIds, @Nullable Bundle extras) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Toggles the stream muting.
+         *
+         * @param muteState Whether the stream should be muted or unmuted.
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onSetStreamMute(boolean muteState, @Nullable Bundle extras) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Sets the stream volume.
+         * If volume is outside of the range [0.0, 1.0], then the value will be clipped.
+         *
+         * @param volume The new volume, in the range [0.0 - 1.0].
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onSetStreamVolume(int volume, @Nullable Bundle extras) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Sets the text track style.
+         *
+         * @param trackStyle The track style.
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onSetTextTrackStyle(@NonNull TextTrackStyle trackStyle,
+                @Nullable Bundle extras) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Stops playback of the current media item.
+         *
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void onStop(@Nullable Bundle extras) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Sends a status updated event.
+         *
+         * @param status The updated media status, or null if none.
+         * @param extras Custom application-specific data to pass along with the request.
+         */
+        public void sendStatusUpdatedEvent(@Nullable MediaStatus status,
+                @Nullable Bundle extras) {
+            Bundle args = new Bundle();
+            args.putBundle("status", status.toBundle());
+            args.putBundle("extras", extras);
+            sendEvent("statusUpdated", args);
+        }
+
+        @Override
+        public void onRequest(String request, Bundle args)
+                throws UnsupportedOperationException {
+            switch (request) {
+                case "load":
+                    onLoad(MediaInfo.fromBundle(args.getBundle("mediaInfo")),
+                            args.getBoolean("autoplay"), args.getLong("playPosition"),
+                            args.getBundle("extras"));
+                    return;
+                case "play":
+                    onPlay(args.getBundle("extras"));
+                    return;
+                case "pause":
+                    onPause(args.getBundle("extras"));
+                    return;
+                case "requestStatus":
+                    onRequestStatus(args.getBundle("extras"));
+                    return;
+                case "seek":
+                    onSeek(args.getLong("position"), args.getInt("resumeState"),
+                            args.getBundle("extras"));
+                    return;
+                case "setActiveMediaTracks":
+                    onSetActiveMediaTracks(args.getLongArray("trackIds"), args.getBundle("extras"));
+                    return;
+                case "setStreamMute":
+                    onSetStreamMute(args.getBoolean("muteState"), args.getBundle("extras"));
+                    return;
+                case "setStreamVolume":
+                    onSetStreamVolume(args.getInt("volume"), args.getBundle("extras"));
+                    return;
+                case "setTextTrackStyle":
+                    onSetTextTrackStyle(TextTrackStyle.fromBundle(args.getBundle("trackStyle")),
+                            args.getBundle("extras"));
+                    return;
+                case "stop":
+                    onStop(args.getBundle("extras"));
+                    return;
+            }
+            super.onRequest(request, args);
+        }
+    }
+
+    /**
+     * A class that aggregates information about a media item.
+     */
+    public static final class MediaInfo {
+        /** A stream type of "none". */
+        public static final int STREAM_TYPE_NONE = 0;
+
+        /** A buffered stream type. */
+        public static final int STREAM_TYPE_BUFFERED = 1;
+
+        /** A live stream type. */
+        public static final int STREAM_TYPE_LIVE = 2;
+
+        /** An invalid (unknown) stream type. */
+        public static final int STREAM_TYPE_INVALID = -1;
+
+        private static final int STREAM_TYPE_MAX = STREAM_TYPE_LIVE;
+
+        private static final String KEY_CONTENT_ID = "contentId";
+        private static final String KEY_CONTENT_TYPE = "contentType";
+        private static final String KEY_EXTRAS = "extras";
+        private static final String KEY_DURATION = "duration";
+        private static final String KEY_METADATA = "metadata";
+        private static final String KEY_STREAM_TYPE = "streamType";
+        private static final String KEY_TEXT_TRACK_STYLE = "textTrackStyle";
+        private static final String KEY_TRACKS = "tracks";
+
+        private final String mContentId;
+        private final int mStreamType;
+        private final String mContentType;
+        private MediaMetadata mMediaMetadata;
+        private long mStreamDuration;
+        private final ArrayList<MediaTrack> mMediaTracks = new ArrayList<MediaTrack>();
+        private TextTrackStyle mTextTrackStyle;
+        private Bundle mExtras;
+
+        /**
+         * Constructs a new MediaInfo with the given content ID.
+         *
+         * @throws IllegalArgumentException If the content ID or content type
+         * is {@code null} or empty, or if the stream type is invalid.
+         */
+        public MediaInfo(@NonNull String contentId, int streamType,
+                @NonNull String contentType) {
+            if (TextUtils.isEmpty(contentId)) {
+                throw new IllegalArgumentException("content ID cannot be null or empty");
+            }
+            if ((streamType < STREAM_TYPE_INVALID) || (streamType > STREAM_TYPE_MAX)) {
+                throw new IllegalArgumentException("invalid stream type");
+            }
+            if (TextUtils.isEmpty(contentType)) {
+                throw new IllegalArgumentException("content type cannot be null or empty");
+            }
+            mContentId = contentId;
+            mStreamType = streamType;
+            mContentType = contentType;
+        }
+
+        /**
+         * Returns the content ID.
+         */
+        public @NonNull String getContentId() {
+            return mContentId;
+        }
+
+        /**
+         * Returns the stream type.
+         */
+        public int getStreamType() {
+            return mStreamType;
+        }
+
+        /**
+         * Returns the content (MIME) type.
+         */
+        public @NonNull String getContentType() {
+            return mContentType;
+        }
+
+        /**
+         * Sets the media item metadata.
+         */
+        public void setMetadata(@Nullable MediaMetadata metadata) {
+            mMediaMetadata = metadata;
+        }
+
+        /**
+         * Returns the media item metadata.
+         */
+        public @Nullable MediaMetadata getMetadata() {
+            return mMediaMetadata;
+        }
+
+        /**
+         * Sets the stream duration, in milliseconds.
+         *
+         * @throws IllegalArgumentException If the duration is negative.
+         */
+        public void setStreamDuration(long streamDuration) {
+            if (streamDuration < 0) {
+                throw new IllegalArgumentException("Stream duration cannot be negative");
+            }
+            mStreamDuration = streamDuration;
+        }
+
+        /**
+         * Returns the stream duration, in milliseconds.
+         */
+        public long getStreamDuration() {
+            return mStreamDuration;
+        }
+
+        /**
+         * Sets the media tracks.
+         */
+        public void setMediaTracks(@NonNull List<MediaTrack> mediaTracks) {
+            mMediaTracks.clear();
+            mMediaTracks.addAll(mediaTracks);
+        }
+
+        /**
+         * Returns the list of media tracks, or {@code null} if none have been specified.
+         */
+        public @NonNull List<MediaTrack> getMediaTracks() {
+            return mMediaTracks;
+        }
+
+        /**
+         * Sets the text track style.
+         */
+        public void setTextTrackStyle(@Nullable TextTrackStyle textTrackStyle) {
+            mTextTrackStyle = textTrackStyle;
+        }
+
+        /**
+         * Returns the text track style, or {@code null} if none has been specified.
+         */
+        public @Nullable TextTrackStyle getTextTrackStyle() {
+            return mTextTrackStyle;
+        }
+
+        /**
+         * Sets the custom application-specific data.
+         */
+        public void setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+        }
+
+        /**
+         * Returns the extras, if any.
+         */
+        public @Nullable Bundle getExtras() {
+            return mExtras;
+        }
+
+        /**
+         * Creates a bundle representation of the object.
+         */
+        public @NonNull Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putString(KEY_CONTENT_ID, mContentId);
+            bundle.putInt(KEY_STREAM_TYPE, mStreamType);
+            bundle.putString(KEY_CONTENT_TYPE, mContentType);
+            if (mMediaMetadata != null) {
+                bundle.putBundle(KEY_METADATA, mMediaMetadata.toBundle());
+            }
+            bundle.putLong(KEY_DURATION, mStreamDuration);
+            if (mTextTrackStyle != null) {
+                bundle.putBundle(KEY_TEXT_TRACK_STYLE, mTextTrackStyle.toBundle());
+            }
+            if (mExtras != null) {
+                bundle.putBundle(KEY_EXTRAS, mExtras);
+            }
+            if (!mMediaTracks.isEmpty()) {
+                Parcelable[] trackBundles = new Parcelable[mMediaTracks.size()];
+                for (int i = 0; i < trackBundles.length; i++) {
+                    trackBundles[i] = mMediaTracks.get(i).toBundle();
+                }
+                bundle.putParcelableArray(KEY_TRACKS, trackBundles);
+            }
+            return bundle;
+        }
+
+        /**
+         * Constructs a new {@link MediaInfo} object from a bundle.
+         */
+        public static @Nullable MediaInfo fromBundle(@Nullable Bundle bundle) {
+            if (bundle == null) {
+                return null;
+            }
+
+            String contentId = bundle.getString(KEY_CONTENT_ID);
+            int streamType = bundle.getInt(KEY_STREAM_TYPE, STREAM_TYPE_INVALID);
+            String contentType = bundle.getString(KEY_CONTENT_TYPE);
+
+            MediaInfo info = new MediaInfo(contentId, streamType, contentType);
+            info.setMetadata(MediaMetadata.fromBundle(bundle.getBundle(KEY_METADATA)));
+            info.setStreamDuration(bundle.getLong(KEY_DURATION));
+            info.setTextTrackStyle(TextTrackStyle.fromBundle(
+                    bundle.getBundle(KEY_TEXT_TRACK_STYLE)));
+            info.setExtras(bundle.getBundle("extras"));
+
+            Parcelable[] trackBundles = bundle.getParcelableArray(KEY_TRACKS);
+            if (trackBundles != null) {
+                for (int i = 0; i < trackBundles.length; ++i) {
+                    info.mMediaTracks.add(MediaTrack.fromBundle((Bundle)trackBundles[i]));
+                }
+            }
+
+            return info;
+        }
+    }
+
+    /**
+     * Container class for media metadata. Metadata has a media type, an optional
+     * list of images, and a collection of metadata fields. Keys for common
+     * metadata fields are predefined as constants, but the application is free to
+     * define and use additional fields of its own.
+     * <p>
+     * The values of the predefined fields have predefined types. For example, a track number is
+     * an <code>int</code> and a creation date is a <code>Calendar</code>. Attempting to
+     * store a value of an incorrect type in a field will result in a
+     * {@link IllegalArgumentException}.
+     */
+    public static final class MediaMetadata {
+        /** A media type representing generic media content. */
+        public static final int MEDIA_TYPE_GENERIC = 0;
+        /** A media type representing a movie. */
+        public static final int MEDIA_TYPE_MOVIE = 1;
+        /** A media type representing an TV show. */
+        public static final int MEDIA_TYPE_TV_SHOW = 2;
+        /** A media type representing a music track. */
+        public static final int MEDIA_TYPE_MUSIC_TRACK = 3;
+        /** A media type representing a photo. */
+        public static final int MEDIA_TYPE_PHOTO = 4;
+        /** The smallest media type value that can be assigned for application-defined media types. */
+        public static final int MEDIA_TYPE_USER = 100;
+
+        // Field types.
+        private static final int TYPE_NONE = 0;
+        private static final int TYPE_STRING = 1;
+        private static final int TYPE_INT = 2;
+        private static final int TYPE_DOUBLE = 3;
+        private static final int TYPE_DATE = 4;
+
+        // Field type names. Used when constructing exceptions.
+        private static final String[] sTypeNames = { null, "String", "int", "double", "Calendar" };
+
+        private final int mMediaType;
+        private final Bundle mFields;
+        private final ArrayList<WebImage> mImages;
+
+        private static final String BUNDLE_KEY_MEDIA_TYPE = "mediaType";
+        private static final String BUNDLE_KEY_FIELDS = "fields";
+        private static final String BUNDLE_KEY_IMAGES = "images";
+
+        /**
+         * String key: Creation date.
+         * <p>
+         * The value is the date and/or time at which the media was created.
+         * For example, this could be the date and time at which a photograph was taken or a piece of
+         * music was recorded.
+         */
+        public static final String KEY_CREATION_DATE =
+                "android.support.media.protocols.metadata.CREATION_DATE";
+
+        /**
+         * String key: Release date.
+         * <p>
+         * The value is the date and/or time at which the media was released.
+         * For example, this could be the date that a movie or music album was released.
+         */
+        public static final String KEY_RELEASE_DATE =
+                "android.support.media.protocols.metadata.RELEASE_DATE";
+
+        /**
+         * String key: Broadcast date.
+         * <p>
+         * The value is the date and/or time at which the media was first broadcast.
+         * For example, this could be the date that a TV show episode was first aired.
+         */
+        public static final String KEY_BROADCAST_DATE =
+                "android.support.media.protocols.metadata.BROADCAST_DATE";
+
+        /**
+         * String key: Title.
+         * <p>
+         * The title of the media. For example, this could be the title of a song, movie, or TV show
+         * episode. This value is suitable for display purposes.
+         */
+        public static final String KEY_TITLE =
+                "android.support.media.protocols.metadata.TITLE";
+
+        /**
+         * String key: Subtitle.
+         * <p>
+         * The subtitle of the media. This value is suitable for display purposes.
+         */
+        public static final String KEY_SUBTITLE =
+                "android.support.media.protocols.metadata.SUBTITLE";
+
+        /**
+         * String key: Artist.
+         * <p>
+         * The name of the artist who created the media. For example, this could be the name of a
+         * musician, performer, or photographer. This value is suitable for display purposes.
+         */
+        public static final String KEY_ARTIST =
+                "android.support.media.protocols.metadata.ARTIST";
+
+        /**
+         * String key: Album artist.
+         * <p>
+         * The name of the artist who produced an album. For example, in compilation albums such as DJ
+         * mixes, the album artist is not necessarily the same as the artist(s) of the individual songs
+         * on the album. This value is suitable for display purposes.
+         */
+        public static final String KEY_ALBUM_ARTIST =
+                "android.support.media.protocols.metadata.ALBUM_ARTIST";
+
+        /**
+         * String key: Album title.
+         * <p>
+         * The title of the album that a music track belongs to. This value is suitable for display
+         * purposes.
+         */
+        public static final String KEY_ALBUM_TITLE =
+                "android.support.media.protocols.metadata.ALBUM_TITLE";
+
+        /**
+         * String key: Composer.
+         * <p>
+         * The name of the composer of a music track. This value is suitable for display purposes.
+         */
+        public static final String KEY_COMPOSER =
+                "android.support.media.protocols.metadata.COMPOSER";
+
+        /**
+         * Integer key: Disc number.
+         * <p>
+         * The disc number (counting from 1) that a music track belongs to in a multi-disc album.
+         */
+        public static final String KEY_DISC_NUMBER =
+                "android.support.media.protocols.metadata.DISC_NUMBER";
+
+        /**
+         * Integer key: Track number.
+         * <p>
+         * The track number of a music track on an album disc. Typically track numbers are counted
+         * starting from 1, however this value may be 0 if it is a "hidden track" at the beginning of
+         * an album.
+         */
+        public static final String KEY_TRACK_NUMBER =
+                "android.support.media.protocols.metadata.TRACK_NUMBER";
+
+        /**
+         * Integer key: Season number.
+         * <p>
+         * The season number that a TV show episode belongs to. Typically season numbers are counted
+         * starting from 1, however this value may be 0 if it is a "pilot" episode that predates the
+         * official start of a TV series.
+         */
+        public static final String KEY_SEASON_NUMBER =
+                "android.support.media.protocols.metadata.SEASON_NUMBER";
+
+        /**
+         * Integer key: Episode number.
+         * <p>
+         * The number of an episode in a given season of a TV show. Typically episode numbers are
+         * counted starting from 1, however this value may be 0 if it is a "pilot" episode that is not
+         * considered to be an official episode of the first season.
+         */
+        public static final String KEY_EPISODE_NUMBER =
+                "android.support.media.protocols.metadata.EPISODE_NUMBER";
+
+        /**
+         * String key: Series title.
+         * <p>
+         * The name of a series. For example, this could be the name of a TV show or series of related
+         * music albums. This value is suitable for display purposes.
+         */
+        public static final String KEY_SERIES_TITLE =
+                "android.support.media.protocols.metadata.SERIES_TITLE";
+
+        /**
+         * String key: Studio.
+         * <p>
+         * The name of a recording studio that produced a piece of media. For example, this could be
+         * the name of a movie studio or music label. This value is suitable for display purposes.
+         */
+        public static final String KEY_STUDIO =
+                "android.support.media.protocols.metadata.STUDIO";
+
+        /**
+         * Integer key: Width.
+         *
+         * The width of a piece of media, in pixels. This would typically be used for providing the
+         * dimensions of a photograph.
+         */
+        public static final String KEY_WIDTH =
+                "android.support.media.protocols.metadata.WIDTH";
+
+        /**
+         * Integer key: Height.
+         *
+         * The height of a piece of media, in pixels. This would typically be used for providing the
+         * dimensions of a photograph.
+         */
+        public static final String KEY_HEIGHT =
+                "android.support.media.protocols.metadata.HEIGHT";
+
+        /**
+         * String key: Location name.
+         * <p>
+         * The name of a location where a piece of media was created. For example, this could be the
+         * location of a photograph or the principal filming location of a movie. This value is
+         * suitable for display purposes.
+         */
+        public static final String KEY_LOCATION_NAME =
+                "android.support.media.protocols.metadata.LOCATION_NAME";
+
+        /**
+         * Double key: Location latitude.
+         * <p>
+         * The latitude component of the geographical location where a piece of media was created.
+         * For example, this could be the location of a photograph or the principal filming location of
+         * a movie.
+         */
+        public static final String KEY_LOCATION_LATITUDE =
+                "android.support.media.protocols.metadata.LOCATION_LATITUDE";
+
+        /**
+         * Double key: Location longitude.
+         * <p>
+         * The longitude component of the geographical location where a piece of media was created.
+         * For example, this could be the location of a photograph or the principal filming location of
+         * a movie.
+         */
+        public static final String KEY_LOCATION_LONGITUDE =
+                "android.support.media.protocols.metadata.LOCATION_LONGITUDE";
+
+        /**
+         * Constructs a new, empty, MediaMetadata with a media type of {@link #MEDIA_TYPE_GENERIC}.
+         */
+        public MediaMetadata() {
+            this(MEDIA_TYPE_GENERIC);
+        }
+
+        /**
+         * Constructs a new, empty, MediaMetadata with the given media type.
+         *
+         * @param mediaType The media type; one of the {@code MEDIA_TYPE_*} constants, or a value
+         * greater than or equal to {@link #MEDIA_TYPE_USER} for custom media types.
+         */
+        public MediaMetadata(int mediaType) {
+            this(mediaType, null);
+        }
+
+        private MediaMetadata(int mediaType, Bundle fields) {
+            mMediaType = mediaType;
+            mFields = fields != null ? fields : new Bundle();
+            mImages = new ArrayList<WebImage>();
+        }
+
+        /**
+         * Gets the media type.
+         */
+        public int getMediaType() {
+            return mMediaType;
+        }
+
+        /**
+         * Clears this object. The media type is left unchanged.
+         */
+        public void clear() {
+            mFields.clear();
+            mImages.clear();
+        }
+
+        /**
+         * Tests if the object contains a field with the given key.
+         */
+        public boolean containsKey(@NonNull String key) {
+            return mFields.containsKey(key);
+        }
+
+        /**
+         * Returns a set of keys for all fields that are present in the object.
+         */
+        public @NonNull Set<String> keySet() {
+            return mFields.keySet();
+        }
+
+        /**
+         * Stores a value in a String field.
+         *
+         * @param key The key for the field.
+         * @param value The new value for the field.
+         * @throws IllegalArgumentException If the key is {@code null} or empty or refers to a
+         * predefined field which is not a {@code String} field.
+         */
+        public void putString(@NonNull String key, String value) {
+            throwIfWrongType(key, TYPE_STRING);
+            mFields.putString(key, value);
+        }
+
+        /**
+         * Reads the value of a String field.
+         *
+         * @return The value of the field, or {@code null} if the field has not been set.
+         * @throws IllegalArgumentException If the key is {@code null} or empty or refers to a
+         * predefined field which is not a {@code String} field.
+         */
+        public @Nullable String getString(@NonNull String key) {
+            throwIfWrongType(key, TYPE_STRING);
+            return mFields.getString(key);
+        }
+
+        /**
+         * Stores a value in an int field.
+         *
+         * @param key The key for the field.
+         * @param value The new value for the field.
+         * @throws IllegalArgumentException If the key is {@code null} or empty or refers to a
+         * predefined field which is not an {@code int} field.
+         */
+        public void putInt(@NonNull String key, int value) {
+            throwIfWrongType(key, TYPE_INT);
+            mFields.putInt(key, value);
+        }
+
+        /**
+         * Reads the value of an {@code int} field.
+         *
+         * @return The value of the field, or {@code null} if the field has not been set.
+         * @throws IllegalArgumentException If the key is {@code null} or empty or refers to a
+         * predefined field which is not an {@code int} field.
+         */
+        public int getInt(@NonNull String key) {
+            throwIfWrongType(key, TYPE_INT);
+            return mFields.getInt(key);
+        }
+
+        /**
+         * Stores a value in a {@code double} field.
+         *
+         * @param key The key for the field.
+         * @param value The new value for the field.
+         * @throws IllegalArgumentException If the key is {@code null} or empty or refers to a
+         * predefined field which is not a {@code double} field.
+         */
+        public void putDouble(@NonNull String key, double value) {
+            throwIfWrongType(key, TYPE_DOUBLE);
+            mFields.putDouble(key, value);
+        }
+
+        /**
+         * Reads the value of a {@code double} field.
+         *
+         * @return The value of the field, or {@code null} if the field has not been set.
+         * @throws IllegalArgumentException If the key is {@code null} or empty or refers to a
+         * predefined field which is not a {@code double} field.
+         */
+        public double getDouble(@NonNull String key) {
+            throwIfWrongType(key, TYPE_DOUBLE);
+            return mFields.getDouble(key);
+        }
+
+        /**
+         * Stores a value in a date field.
+         *
+         * @param key The key for the field.
+         * @param value The new value for the field.
+         * @throws IllegalArgumentException If the key is {@code null} or empty or refers to a
+         * predefined field which is not a date field.
+         */
+        public void putDate(@NonNull String key, @Nullable Calendar value) {
+            throwIfWrongType(key, TYPE_DATE);
+            if (value != null) {
+                mFields.putLong(key, value.getTimeInMillis());
+            } else {
+                mFields.remove(key);
+            }
+        }
+
+        /**
+         * Reads the value of a date field.
+         *
+         * @param key The field name.
+         * @return The date, as a {@link Calendar}, or {@code null} if this field has not been set.
+         * @throws IllegalArgumentException If the key is {@code null} or empty or the specified field's
+         * predefined type is not a date.
+         */
+        public @Nullable Calendar getDate(String key) {
+            throwIfWrongType(key, TYPE_DATE);
+            if (mFields.containsKey(key)) {
+                Calendar date = Calendar.getInstance();
+                date.setTimeInMillis(mFields.getLong(key));
+                return date;
+            }
+            return null;
+        }
+
+        /**
+         * Returns the list of images. If there are no images, returns an empty list.
+         */
+        public List<WebImage> getImages() {
+            return mImages;
+        }
+
+        /**
+         * Checks if the metadata includes any images.
+         */
+        public boolean hasImages() {
+            return (mImages != null) && !mImages.isEmpty();
+        }
+
+        /**
+         * Clears the list of images.
+         */
+        public void clearImages() {
+            mImages.clear();
+        }
+
+        /**
+         * Adds an image to the list of images.
+         */
+        public void addImage(WebImage image) {
+            mImages.add(image);
+        }
+
+        /*
+         * Verifies that a key is not empty, and if the key is a predefined key, verifies that it has
+         * the specified type.
+         */
+        private void throwIfWrongType(String key, int type) {
+            if (TextUtils.isEmpty(key)) {
+                throw new IllegalArgumentException("null and empty keys are not allowed");
+            }
+            int actualType = getFieldType(key);
+            if ((actualType != type) && (actualType != TYPE_NONE))
+                throw new IllegalArgumentException("Value for " + key + " must be a "
+                        + sTypeNames[type]);
+        }
+
+        /**
+         * Creates a bundle representation of the object.
+         */
+        public @NonNull Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putInt(BUNDLE_KEY_MEDIA_TYPE, mMediaType);
+            bundle.putBundle(BUNDLE_KEY_FIELDS, mFields);
+
+            if (mImages.isEmpty()) {
+                Parcelable[] imageBundles = new Parcelable[mImages.size()];
+                for (int i = 0; i < imageBundles.length; i++) {
+                    imageBundles[i] = mImages.get(i).toBundle();
+                }
+                bundle.putParcelableArray(BUNDLE_KEY_IMAGES, imageBundles);
+            }
+
+            return bundle;
+        }
+
+        /**
+         * Constructs a new {@link MediaMetadata} object from a bundle.
+         */
+        public static MediaMetadata fromBundle(Bundle bundle) {
+            if (bundle == null) {
+                return null;
+            }
+
+            int mediaType = bundle.getInt(BUNDLE_KEY_MEDIA_TYPE);
+            Bundle fields = bundle.getBundle(BUNDLE_KEY_FIELDS);
+            MediaMetadata metadata = new MediaMetadata(mediaType, fields);
+
+            Parcelable[] imageBundles = bundle.getParcelableArray(BUNDLE_KEY_IMAGES);
+            if (imageBundles != null) {
+                for (Parcelable imageBundle : imageBundles) {
+                    metadata.addImage(WebImage.fromBundle((Bundle)imageBundle));
+                }
+            }
+
+            return metadata;
+        }
+
+        private static int getFieldType(String key) {
+            switch (key) {
+                case KEY_CREATION_DATE: return TYPE_DATE;
+                case KEY_RELEASE_DATE: return TYPE_DATE;
+                case KEY_BROADCAST_DATE: return TYPE_DATE;
+                case KEY_TITLE: return TYPE_STRING;
+                case KEY_SUBTITLE: return TYPE_STRING;
+                case KEY_ARTIST: return TYPE_STRING;
+                case KEY_ALBUM_ARTIST: return TYPE_STRING;
+                case KEY_ALBUM_TITLE: return TYPE_STRING;
+                case KEY_COMPOSER: return TYPE_STRING;
+                case KEY_DISC_NUMBER: return TYPE_INT;
+                case KEY_TRACK_NUMBER: return TYPE_INT;
+                case KEY_SEASON_NUMBER: return TYPE_INT;
+                case KEY_EPISODE_NUMBER: return TYPE_INT;
+                case KEY_SERIES_TITLE: return TYPE_STRING;
+                case KEY_STUDIO: return TYPE_STRING;
+                case KEY_WIDTH: return TYPE_INT;
+                case KEY_HEIGHT: return TYPE_INT;
+                case KEY_LOCATION_NAME: return TYPE_STRING;
+                case KEY_LOCATION_LATITUDE: return TYPE_DOUBLE;
+                case KEY_LOCATION_LONGITUDE: return TYPE_DOUBLE;
+                default: return TYPE_NONE;
+            }
+        }
+    }
+
+    /**
+     * A class that holds status information about some media.
+     */
+    public static final class MediaStatus {
+        private static final String KEY_ACTIVE_TRACK_IDS = "activeTrackIds";
+        private static final String KEY_CURRENT_TIME = "currentTime";
+        private static final String KEY_EXTRAS = "extras";
+        private static final String KEY_IDLE_REASON = "idleReason";
+        private static final String KEY_MEDIA = "media";
+        private static final String KEY_MEDIA_SESSION_ID = "mediaSessionId";
+        private static final String KEY_MUTED = "muted";
+        private static final String KEY_PLAYBACK_RATE = "playbackRate";
+        private static final String KEY_PLAYER_STATE = "playerState";
+        private static final String KEY_SUPPORTED_MEDIA_COMMANDS = "supportedMediaCommands";
+        private static final String KEY_VOLUME = "volume";
+
+        /** A flag (bitmask) indicating that a media item can be paused. */
+        public static final long COMMAND_PAUSE = 1 << 0;
+
+        /** A flag (bitmask) indicating that a media item supports seeking. */
+        public static final long COMMAND_SEEK = 1 << 1;
+
+        /** A flag (bitmask) indicating that a media item's audio volume can be changed. */
+        public static final long COMMAND_SET_VOLUME = 1 << 2;
+
+        /** A flag (bitmask) indicating that a media item's audio can be muted. */
+        public static final long COMMAND_TOGGLE_MUTE = 1 << 3;
+
+        /** A flag (bitmask) indicating that a media item supports skipping forward. */
+        public static final long COMMAND_SKIP_FORWARD = 1 << 4;
+
+        /** A flag (bitmask) indicating that a media item supports skipping backward. */
+        public static final long COMMAND_SKIP_BACKWARD = 1 << 5;
+
+        /** Constant indicating unknown player state. */
+        public static final int PLAYER_STATE_UNKNOWN = 0;
+
+        /** Constant indicating that the media player is idle. */
+        public static final int PLAYER_STATE_IDLE = 1;
+
+        /** Constant indicating that the media player is playing. */
+        public static final int PLAYER_STATE_PLAYING = 2;
+
+        /** Constant indicating that the media player is paused. */
+        public static final int PLAYER_STATE_PAUSED = 3;
+
+        /** Constant indicating that the media player is buffering. */
+        public static final int PLAYER_STATE_BUFFERING = 4;
+
+        /** Constant indicating that the player currently has no idle reason. */
+        public static final int IDLE_REASON_NONE = 0;
+
+        /** Constant indicating that the player is idle because playback has finished. */
+        public static final int IDLE_REASON_FINISHED = 1;
+
+        /**
+         * Constant indicating that the player is idle because playback has been canceled in
+         * response to a STOP command.
+         */
+        public static final int IDLE_REASON_CANCELED = 2;
+
+        /**
+         * Constant indicating that the player is idle because playback has been interrupted by
+         * a LOAD command.
+         */
+        public static final int IDLE_REASON_INTERRUPTED = 3;
+
+        /** Constant indicating that the player is idle because a playback error has occurred. */
+        public static final int IDLE_REASON_ERROR = 4;
+
+        private final long mMediaSessionId;
+        private final MediaInfo mMediaInfo;
+        private double mPlaybackRate;
+        private int mPlayerState;
+        private int mIdleReason;
+        private long mStreamPosition;
+        private long mSupportedMediaCommands;
+        private double mVolume;
+        private boolean mMuteState;
+        private long mActiveTrackIds[];
+        private Bundle mExtras;
+
+        /**
+         * Constructs a new {@link MediaStatus} object with the given properties.
+         */
+        public MediaStatus(long mediaSessionId, @NonNull MediaInfo mediaInfo) {
+            if (mediaInfo == null) {
+                throw new IllegalArgumentException("mediaInfo must not be null");
+            }
+
+            mMediaSessionId = mediaSessionId;
+            mMediaInfo = mediaInfo;
+            mPlayerState = PLAYER_STATE_UNKNOWN;
+            mIdleReason = IDLE_REASON_NONE;
+        }
+
+        /**
+         * Returns the media session ID for this item.
+         */
+        public long getMediaSessionId() {
+            return mMediaSessionId;
+        }
+
+        /**
+         * Returns the {@link MediaInfo} for this item.
+         */
+        public @NonNull MediaInfo getMediaInfo() {
+            return mMediaInfo;
+        }
+
+        /**
+         * Gets the current media player state.
+         */
+        public int getPlayerState() {
+            return mPlayerState;
+        }
+
+        /**
+         * Sets the current media player state.
+         */
+        public void setPlayerState(int playerState) {
+            mPlayerState = playerState;
+        }
+
+        /**
+         * Gets the player state idle reason. This value is only meaningful if the player state is
+         * in fact {@link #PLAYER_STATE_IDLE}.
+         */
+        public int getIdleReason() {
+            return mIdleReason;
+        }
+
+        /**
+         * Sets the player state idle reason. This value is only meaningful if the player state is
+         * in fact {@link #PLAYER_STATE_IDLE}.
+         */
+        public void setIdleReason(int idleReason) {
+            mIdleReason = idleReason;
+        }
+
+        /**
+         * Gets the current stream playback rate. This will be negative if the stream is seeking
+         * backwards, 0 if the stream is paused, 1 if the stream is playing normally, and some other
+         * positive value if the stream is seeking forwards.
+         */
+        public double getPlaybackRate() {
+            return mPlaybackRate;
+        }
+
+        /**
+         * Sets the current stream playback rate. This will be negative if the stream is seeking
+         * backwards, 0 if the stream is paused, 1 if the stream is playing normally, and some other
+         * positive value if the stream is seeking forwards.
+         */
+        public void setPlaybackRate(double playbackRate) {
+            mPlaybackRate = playbackRate;
+        }
+
+        /**
+         * Returns the current stream position, in milliseconds.
+         */
+        public long getStreamPosition() {
+            return mStreamPosition;
+        }
+
+        /**
+         * Sets the current stream position, in milliseconds.
+         */
+        public void setStreamPosition(long streamPosition) {
+            mStreamPosition = streamPosition;
+        }
+
+        /**
+         * Tests if the stream supports a given control command.
+         *
+         * @param mediaCommand The media command.
+         * @return {@code true} if the command is supported, {@code false} otherwise.
+         */
+        public boolean isMediaCommandSupported(long mediaCommand) {
+            return (mSupportedMediaCommands & mediaCommand) != 0;
+        }
+
+        /**
+         * Sets whether the stream supports a given control command.
+         */
+        public void setSupportedMediaCommands(long supportedMediaCommands) {
+            mSupportedMediaCommands = supportedMediaCommands;
+        }
+
+        /**
+         * Returns the stream's volume.
+         */
+        public double getStreamVolume() {
+            return mVolume;
+        }
+
+        /**
+         * Sets the stream's volume.
+         */
+        public void setStreamVolume(double volume) {
+            mVolume = volume;
+        }
+
+        /**
+         * Returns the stream's mute state.
+         */
+        public boolean isMute() {
+            return mMuteState;
+        }
+
+        /**
+         * Sets the stream's mute state.
+         */
+        public void setMute(boolean muteState) {
+            mMuteState = muteState;
+        }
+
+        /**
+         * Returns the list of active track IDs, if any, otherwise {@code null}.
+         */
+        public @Nullable long[] getActiveTrackIds() {
+            return mActiveTrackIds;
+        }
+
+        /**
+         * Sets the list of active track IDs, if any, otherwise {@code null}.
+         */
+        public void setActiveTrackIds(@Nullable long[] trackIds) {
+            mActiveTrackIds = trackIds;
+        }
+
+        /**
+         * Returns any extras that are is associated with the media item.
+         */
+        public @Nullable Bundle getExtras() {
+            return mExtras;
+        }
+
+        /**
+         * Sets any extras that are associated with the media item.
+         */
+        public void setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+        }
+
+        /**
+         * Creates a bundle representation of the object.
+         */
+        public @NonNull Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putLong(KEY_MEDIA_SESSION_ID, mMediaSessionId);
+            bundle.putBundle(KEY_MEDIA, mMediaInfo.toBundle());
+            bundle.putLongArray(KEY_ACTIVE_TRACK_IDS, mActiveTrackIds);
+            bundle.putLong(KEY_CURRENT_TIME, mStreamPosition);
+            bundle.putInt(KEY_IDLE_REASON, mIdleReason);
+            bundle.putBoolean(KEY_MUTED, mMuteState);
+            bundle.putDouble(KEY_PLAYBACK_RATE, mPlaybackRate);
+            bundle.putInt(KEY_PLAYER_STATE, mPlayerState);
+            bundle.putLong(KEY_SUPPORTED_MEDIA_COMMANDS, mSupportedMediaCommands);
+            bundle.putDouble(KEY_VOLUME, mVolume);
+            return bundle;
+        }
+
+        /**
+         * Constructs a new {@link MediaStatus} object from a bundle.
+         */
+        public static MediaStatus fromBundle(Bundle bundle) {
+            if (bundle == null) {
+                return null;
+            }
+
+            long mediaSessionId = bundle.getLong(KEY_MEDIA_SESSION_ID);
+            MediaInfo mediaInfo = MediaInfo.fromBundle(bundle.getBundle(KEY_MEDIA));
+            MediaStatus status = new MediaStatus(mediaSessionId, mediaInfo);
+
+            status.setActiveTrackIds(bundle.getLongArray(KEY_ACTIVE_TRACK_IDS));
+            status.setStreamPosition(bundle.getLong(KEY_CURRENT_TIME));
+            status.setIdleReason(bundle.getInt(KEY_IDLE_REASON));
+            status.setMute(bundle.getBoolean(KEY_MUTED));
+            status.setPlaybackRate(bundle.getDouble(KEY_PLAYBACK_RATE));
+            status.setPlayerState(bundle.getInt(KEY_PLAYER_STATE));
+            status.setSupportedMediaCommands(bundle.getLong(KEY_SUPPORTED_MEDIA_COMMANDS));
+            status.setStreamVolume(bundle.getDouble(KEY_VOLUME));
+            status.setExtras(bundle.getBundle(KEY_EXTRAS));
+            return status;
+        }
+    }
+
+    /**
+     * A class that represents a media track, such as a language track or closed caption text track
+     * in a video.
+     */
+    public static final class MediaTrack {
+        private static final String KEY_TRACK_ID = "trackId";
+        private static final String KEY_TYPE = "type";
+        private static final String KEY_TRACK_CONTENT_ID = "trackContentId";
+        private static final String KEY_TRACK_CONTENT_TYPE = "trackContentType";
+        private static final String KEY_NAME = "name";
+        private static final String KEY_LANGUAGE = "language";
+        private static final String KEY_SUBTYPE = "subtype";
+        private static final String KEY_EXTRAS = "extras";
+
+        /** A media track type indicating an unknown track type. */
+        public static final int TYPE_UNKNOWN = 0;
+        /** A media track type indicating a text track. */
+        public static final int TYPE_TEXT = 1;
+        /** A media track type indicating an audio track. */
+        public static final int TYPE_AUDIO = 2;
+        /** A media track type indicating a video track. */
+        public static final int TYPE_VIDEO = 3;
+
+        /** A media track subtype indicating an unknown subtype. */
+        public static final int SUBTYPE_UNKNOWN = -1;
+        /** A media track subtype indicating no subtype. */
+        public static final int SUBTYPE_NONE = 0;
+        /** A media track subtype indicating subtitles. */
+        public static final int SUBTYPE_SUBTITLES = 1;
+        /** A media track subtype indicating closed captions. */
+        public static final int SUBTYPE_CAPTIONS = 2;
+        /** A media track subtype indicating descriptions. */
+        public static final int SUBTYPE_DESCRIPTIONS = 3;
+        /** A media track subtype indicating chapters. */
+        public static final int SUBTYPE_CHAPTERS = 4;
+        /** A media track subtype indicating metadata. */
+        public static final int SUBTYPE_METADATA = 5;
+
+        private long mId;
+        private int mType;
+        private String mContentId;
+        private String mContentType;
+        private String mName;
+        private String mLanguage;
+        private int mSubtype;
+        private Bundle mExtras;
+
+        /**
+         * Constructs a new track with the given track ID and type.
+         *
+         * @throws IllegalArgumentException If the track type is invalid.
+         */
+        public MediaTrack(long id, int type) {
+            clear();
+            mId = id;
+            if ((type <= TYPE_UNKNOWN) || (type > TYPE_VIDEO)) {
+                throw new IllegalArgumentException("invalid type " + type);
+            }
+            mType = type;
+        }
+
+        /**
+         * Returns the unique ID of the media track.
+         */
+        public long getId() {
+            return mId;
+        }
+
+        /**
+         * Returns the type of the track; one of the {@code TYPE_} constants defined above.
+         */
+        public int getType() {
+            return mType;
+        }
+
+        /**
+         * Returns the content ID of the media track.
+         */
+        public String getContentId() {
+            return mContentId;
+        }
+
+        /**
+         * Sets the content ID for the media track.
+         */
+        public void setContentId(String contentId) {
+            mContentId = contentId;
+        }
+
+        /**
+         * Returns the content type (MIME type) of the media track, or {@code null} if none was
+         * specified.
+         */
+        public String getContentType() {
+            return mContentType;
+        }
+
+        /**
+         * Sets the content type (MIME type) of the media track.
+         */
+        public void setContentType(String contentType) {
+            mContentType = contentType;
+        }
+
+        /**
+         * Returns the name of the media track, or {@code null} if none was specified.
+         */
+        public String getName() {
+            return mName;
+        }
+
+        /**
+         * Sets the track name.
+         */
+        public void setName(String name) {
+            mName = name;
+        }
+
+        /**
+         * Returns the language of this media track, or {@code null} if none was specified.
+         */
+        public String getLanguage() {
+            return mLanguage;
+        }
+
+        /**
+         * Sets the track language.
+         */
+        public void setLanguage(String language) {
+            mLanguage = language;
+        }
+
+        /**
+         * Returns the subtype of this media track; one of the {@code SUBTYPE_}
+         * constants defined above.
+         */
+        public int getSubtype() {
+            return mSubtype;
+        }
+
+        /**
+         * Sets the track subtype.
+         *
+         * @throws IllegalArgumentException If the subtype is invalid.
+         */
+        public void setSubtype(int subtype) {
+            if ((subtype <= SUBTYPE_UNKNOWN) || (subtype > SUBTYPE_METADATA)) {
+                throw new IllegalArgumentException("invalid subtype " + subtype);
+            }
+            if ((subtype != SUBTYPE_NONE) && (mType != TYPE_TEXT)) {
+                throw new IllegalArgumentException("subtypes are only valid for text tracks");
+            }
+
+            mSubtype = subtype;
+        }
+
+        /**
+         * Returns the extras object for this media track, or {@code null} if none was
+         * specified.
+         */
+        public Bundle getExtras() {
+            return mExtras;
+        }
+
+        /**
+         * Sets the track's extras object.
+         */
+        public void setExtras(Bundle extras) {
+            mExtras = extras;
+        }
+
+        private void clear() {
+            mId = 0;
+            mType = TYPE_UNKNOWN;
+            mContentId = null;
+            mName = null;
+            mLanguage = null;
+            mSubtype = SUBTYPE_UNKNOWN;
+            mExtras = null;
+        }
+
+        /**
+         * Creates a bundle representation of the object.
+         */
+        public @NonNull Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putLong(KEY_TRACK_ID, mId);
+            bundle.putInt(KEY_TYPE, mType);
+            bundle.putString(KEY_TRACK_CONTENT_ID, mContentId);
+            bundle.putString(KEY_TRACK_CONTENT_TYPE, mContentType);
+            bundle.putString(KEY_NAME, mName);
+            bundle.putString(KEY_LANGUAGE, mLanguage);
+            bundle.putInt(KEY_SUBTYPE, mSubtype);
+            bundle.putBundle(KEY_EXTRAS, mExtras);
+            return bundle;
+        }
+
+        /**
+         * Constructs a new {@link MediaTrack} object from a bundle.
+         */
+        public static MediaTrack fromBundle(Bundle bundle) {
+            if (bundle == null) {
+                return null;
+            }
+
+            long trackId = bundle.getLong(KEY_TRACK_ID);
+            int type = bundle.getInt(KEY_TYPE);
+            MediaTrack track = new MediaTrack(trackId, type);
+
+            track.setContentId(bundle.getString(KEY_TRACK_CONTENT_ID));
+            track.setContentType(bundle.getString(KEY_TRACK_CONTENT_TYPE));
+            track.setName(bundle.getString(KEY_NAME));
+            track.setLanguage(bundle.getString(KEY_LANGUAGE));
+            track.setSubtype(bundle.getInt(KEY_SUBTYPE));
+            track.setExtras(bundle.getBundle(KEY_EXTRAS));
+            return track;
+        }
+    }
+
+    /**
+     * A class that specifies how a text track's text will be displayed on-screen. The text is
+     * displayed inside a rectangular "window". The appearance of both the text and the window are
+     * configurable.
+     * <p>
+     * With the exception of the font scale, which has a predefined default value, any attribute that
+     * is not explicitly set will remain "unspecified", and the player will select an appropriate
+     * value.
+     */
+    public static final class TextTrackStyle {
+        /** The default font scale. */
+        public static final float DEFAULT_FONT_SCALE = 1.0f;
+
+        /** A color value that indicates an unspecified (unset) color. */
+        public static final int COLOR_UNSPECIFIED = 0;
+
+        /** An edge type indicating an unspecified edge type. */
+        public static final int EDGE_TYPE_UNSPECIFIED = -1;
+        /** An edge type indicating no edge. */
+        public static final int EDGE_TYPE_NONE = 0;
+        /** An edge type indicating an outline edge. */
+        public static final int EDGE_TYPE_OUTLINE = 1;
+        /** An edge type indicating a drop shadow edge. */
+        public static final int EDGE_TYPE_DROP_SHADOW = 2;
+        /** An edge type indicating a raised edge. */
+        public static final int EDGE_TYPE_RAISED = 3;
+        /** An edge type indicating a depressed edge. */
+        public static final int EDGE_TYPE_DEPRESSED = 4;
+
+        /** A window type indicating an unspecified window type. */
+        public static final int WINDOW_TYPE_UNSPECIFIED = -1;
+        /** A window type indicating no window type. */
+        public static final int WINDOW_TYPE_NONE = 0;
+        /** A window type indicating a normal window. */
+        public static final int WINDOW_TYPE_NORMAL = 1;
+        /** A window type indicating a window with rounded corners. */
+        public static final int WINDOW_TYPE_ROUNDED = 2;
+
+        /** A font family indicating an unspecified font family. */
+        public static final int FONT_FAMILY_UNSPECIFIED = -1;
+        /** A font family indicating Sans Serif. */
+        public static final int FONT_FAMILY_SANS_SERIF = 0;
+        /** A font family indicating Monospaced Sans Serif. */
+        public static final int FONT_FAMILY_MONOSPACED_SANS_SERIF = 1;
+        /** A font family indicating Serif. */
+        public static final int FONT_FAMILY_SERIF = 2;
+        /** A font family indicating Monospaced Serif. */
+        public static final int FONT_FAMILY_MONOSPACED_SERIF = 3;
+        /** A font family indicating Casual. */
+        public static final int FONT_FAMILY_CASUAL = 4;
+        /** A font family indicating Cursive. */
+        public static final int FONT_FAMILY_CURSIVE = 5;
+        /** A font family indicating Small Capitals. */
+        public static final int FONT_FAMILY_SMALL_CAPITALS = 6;
+
+        /** A font style indicating an unspecified style. */
+        public static final int FONT_STYLE_UNSPECIFIED = -1;
+        /** A font style indicating a normal style. */
+        public static final int FONT_STYLE_NORMAL = 0;
+        /** A font style indicating a bold style. */
+        public static final int FONT_STYLE_BOLD = 1;
+        /** A font style indicating an italic style. */
+        public static final int FONT_STYLE_ITALIC = 2;
+        /** A font style indicating a bold and italic style. */
+        public static final int FONT_STYLE_BOLD_ITALIC = 3;
+
+        private static final String KEY_FONT_SCALE = "fontScale";
+        private static final String KEY_FOREGROUND_COLOR = "foregroundColor";
+        private static final String KEY_BACKGROUND_COLOR = "backgroundColor";
+        private static final String KEY_EDGE_TYPE = "edgeType";
+        private static final String KEY_EDGE_COLOR = "edgeColor";
+        private static final String KEY_WINDOW_TYPE = "windowType";
+        private static final String KEY_WINDOW_COLOR = "windowColor";
+        private static final String KEY_WINDOW_CORNER_RADIUS = "windowRoundedCornerRadius";
+        private static final String KEY_FONT_FAMILY = "fontFamily";
+        private static final String KEY_FONT_GENERIC_FAMILY = "fontGenericFamily";
+        private static final String KEY_FONT_STYLE = "fontStyle";
+        private static final String KEY_EXTRAS = "extras";
+
+        private float mFontScale;
+        private int mForegroundColor;
+        private int mBackgroundColor;
+        private int mEdgeType;
+        private int mEdgeColor;
+        private int mWindowType;
+        private int mWindowColor;
+        private int mWindowCornerRadius;
+        private String mFontFamily;
+        private int mFontGenericFamily;
+        private int mFontStyle;
+        private Bundle mExtras;
+
+        /**
+         * Constructs a new TextTrackStyle.
+         */
+        public TextTrackStyle() {
+            clear();
+        }
+
+        /**
+         * Sets the font scale factor. The default is {@link #DEFAULT_FONT_SCALE}.
+         */
+        public void setFontScale(float fontScale) {
+            mFontScale = fontScale;
+        }
+
+        /**
+         * Gets the font scale factor.
+         */
+        public float getFontScale() {
+            return mFontScale;
+        }
+
+        /**
+         * Sets the text's foreground color.
+         *
+         * @param foregroundColor The color, as an ARGB value.
+         */
+        public void setForegroundColor(int foregroundColor) {
+            mForegroundColor = foregroundColor;
+        }
+
+        /**
+         * Gets the text's foreground color.
+         */
+        public int getForegroundColor() {
+            return mForegroundColor;
+        }
+
+        /**
+         * Sets the text's background color.
+         *
+         * @param backgroundColor The color, as an ARGB value.
+         */
+        public void setBackgroundColor(int backgroundColor) {
+            mBackgroundColor = backgroundColor;
+        }
+
+        /**
+         * Gets the text's background color.
+         */
+        public int getBackgroundColor() {
+            return mBackgroundColor;
+        }
+
+        /**
+         * Sets the caption window's edge type.
+         *
+         * @param edgeType The edge type; one of the {@code EDGE_TYPE_} constants defined above.
+         */
+        public void setEdgeType(int edgeType) {
+            if ((edgeType < EDGE_TYPE_NONE) || (edgeType > EDGE_TYPE_DEPRESSED)) {
+                throw new IllegalArgumentException("invalid edgeType");
+            }
+            mEdgeType = edgeType;
+        }
+
+        /**
+         * Gets the caption window's edge type.
+         */
+        public int getEdgeType() {
+            return mEdgeType;
+        }
+
+        /**
+         * Sets the window's edge color.
+         *
+         * @param edgeColor The color, as an ARGB value.
+         */
+        public void setEdgeColor(int edgeColor) {
+            mEdgeColor = edgeColor;
+        }
+
+        /**
+         * Gets the window's edge color.
+         */
+        public int getEdgeColor() {
+            return mEdgeColor;
+        }
+
+        /**
+         * Sets the window type.
+         *
+         * @param windowType The window type; one of the {@code WINDOW_TYPE_} constants defined above.
+         */
+        public void setWindowType(int windowType) {
+            if ((windowType < WINDOW_TYPE_NONE) || (windowType > WINDOW_TYPE_ROUNDED)) {
+                throw new IllegalArgumentException("invalid windowType");
+            }
+            mWindowType = windowType;
+        }
+
+        /**
+         * Gets the caption window type.
+         */
+        public int getWindowType() {
+            return mWindowType;
+        }
+
+        /**
+         * Sets the window's color.
+         *
+         * @param windowColor The color, as an ARGB value.
+         */
+        public void setWindowColor(int windowColor) {
+            mWindowColor = windowColor;
+        }
+
+        /**
+         * Gets the window's color.
+         */
+        public int getWindowColor() {
+            return mWindowColor;
+        }
+
+        /**
+         * If the window type is {@link #WINDOW_TYPE_ROUNDED}, sets the radius for the window's
+         * corners.
+         *
+         * @param windowCornerRadius The radius, in pixels. Must be a positive value.
+         */
+        public void setWindowCornerRadius(int windowCornerRadius) {
+            if (windowCornerRadius < 0) {
+                throw new IllegalArgumentException("invalid windowCornerRadius");
+            }
+            mWindowCornerRadius = windowCornerRadius;
+        }
+
+        /**
+         * Gets the window corner radius.
+         */
+        public int getWindowCornerRadius() {
+            return mWindowCornerRadius;
+        }
+
+        /**
+         * Sets the text's font family.
+         *
+         * @param fontFamily The text font family.
+         */
+        public void setFontFamily(String fontFamily) {
+            mFontFamily = fontFamily;
+        }
+
+        /**
+         * Gets the text's font family.
+         */
+        public String getFontFamily() {
+            return mFontFamily;
+        }
+
+        /**
+         * Sets the text's generic font family. This will be used if the font family specified with
+         * {@link #setFontFamily} (if any) is unavailable.
+         *
+         * @param fontGenericFamily The generic family; one of the {@code FONT_FAMILY_} constants
+         * defined above.
+         */
+        public void setFontGenericFamily(int fontGenericFamily) {
+            if ((fontGenericFamily < FONT_FAMILY_SANS_SERIF)
+                    || (fontGenericFamily > FONT_FAMILY_SMALL_CAPITALS)) {
+                throw new IllegalArgumentException("invalid fontGenericFamily");
+            }
+            mFontGenericFamily = fontGenericFamily;
+        }
+
+        /**
+         * Gets the text's generic font family.
+         */
+        public int getFontGenericFamily() {
+            return mFontGenericFamily;
+        }
+
+        /**
+         * Sets the text font style.
+         *
+         * @param fontStyle The font style; one of the {@code FONT_STYLE_} constants defined above.
+         */
+        public void setFontStyle(int fontStyle) {
+            if ((fontStyle < FONT_STYLE_NORMAL) || (fontStyle > FONT_STYLE_BOLD_ITALIC)) {
+                throw new IllegalArgumentException("invalid fontStyle");
+            }
+            mFontStyle = fontStyle;
+        }
+
+        /**
+         * Gets the text font style.
+         */
+        public int getFontStyle() {
+            return mFontStyle;
+        }
+
+        /**
+         * Sets the extras object.
+         */
+        public void setExtras(Bundle extras) {
+            mExtras = extras;
+        }
+
+        /**
+         * Gets the extras object.
+         */
+        public Bundle getExtras() {
+            return mExtras;
+        }
+
+        private void clear() {
+            mFontScale = DEFAULT_FONT_SCALE;
+            mForegroundColor = COLOR_UNSPECIFIED;
+            mBackgroundColor = COLOR_UNSPECIFIED;
+            mEdgeType = EDGE_TYPE_UNSPECIFIED;
+            mEdgeColor = COLOR_UNSPECIFIED;
+            mWindowType = WINDOW_TYPE_UNSPECIFIED;
+            mWindowColor = COLOR_UNSPECIFIED;
+            mWindowCornerRadius = 0;
+            mFontFamily = null;
+            mFontGenericFamily = FONT_FAMILY_UNSPECIFIED;
+            mFontStyle = FONT_STYLE_UNSPECIFIED;
+            mExtras = null;
+        }
+
+        /**
+         * Constructs a new TextTrackStyle based on the systems' current closed caption style settings.
+         * On platform levels below 19, this returns an object with "unspecified" values for all
+         * fields.
+         *
+         * @param context The calling context.
+         * @return The new TextTrackStyle.
+         */
+        public static TextTrackStyle fromSystemSettings(Context context) {
+            TextTrackStyle style = new TextTrackStyle();
+            if (Build.VERSION.SDK_INT >= 19) {
+                Impl19.loadSystemSettings(style, context);
+            }
+            return style;
+        }
+
+        /**
+         * Creates a bundle representation of the object.
+         */
+        public @NonNull Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putFloat(KEY_FONT_SCALE, mFontScale);
+            bundle.putInt(KEY_FOREGROUND_COLOR, mForegroundColor);
+            bundle.putInt(KEY_BACKGROUND_COLOR, mBackgroundColor);
+            bundle.putInt(KEY_EDGE_TYPE, mEdgeType);
+            bundle.putInt(KEY_EDGE_COLOR, mEdgeColor);
+            bundle.putInt(KEY_WINDOW_TYPE, mWindowType);
+            bundle.putInt(KEY_WINDOW_COLOR, mWindowColor);
+            bundle.putInt(KEY_WINDOW_CORNER_RADIUS, mWindowCornerRadius);
+            bundle.putString(KEY_FONT_FAMILY, mFontFamily);
+            bundle.putInt(KEY_FONT_GENERIC_FAMILY, mFontGenericFamily);
+            bundle.putInt(KEY_FONT_STYLE, mFontStyle);
+            bundle.putBundle(KEY_EXTRAS, mExtras);
+            return bundle;
+        }
+
+        /**
+         * Constructs a new {@link MediaTrack} object from a bundle.
+         */
+        public static TextTrackStyle fromBundle(Bundle bundle) {
+            if (bundle == null) {
+                return null;
+            }
+
+            TextTrackStyle style = new TextTrackStyle();
+            style.setFontScale(bundle.getFloat(KEY_FONT_SCALE));
+            style.setForegroundColor(bundle.getInt(KEY_FOREGROUND_COLOR));
+            style.setBackgroundColor(bundle.getInt(KEY_BACKGROUND_COLOR));
+            style.setEdgeType(bundle.getInt(KEY_EDGE_TYPE));
+            style.setEdgeColor(bundle.getInt(KEY_EDGE_COLOR));
+            style.setWindowType(bundle.getInt(KEY_WINDOW_TYPE));
+            style.setWindowColor(bundle.getInt(KEY_WINDOW_COLOR));
+            style.setWindowCornerRadius(bundle.getInt(KEY_WINDOW_CORNER_RADIUS));
+            style.setFontFamily(bundle.getString(KEY_FONT_FAMILY));
+            style.setFontGenericFamily(bundle.getInt(KEY_FONT_GENERIC_FAMILY));
+            style.setFontStyle(bundle.getInt(KEY_FONT_STYLE));
+            style.setExtras(bundle.getBundle(KEY_EXTRAS));
+            return style;
+        }
+
+        // Compatibility for new platform features introduced in KitKat.
+        private static final class Impl19 {
+            public static void loadSystemSettings(TextTrackStyle style, Context context) {
+                CaptioningManager captioningManager =
+                        (CaptioningManager)context.getSystemService(Context.CAPTIONING_SERVICE);
+                style.setFontScale(captioningManager.getFontScale());
+
+                CaptioningManager.CaptionStyle userStyle = captioningManager.getUserStyle();
+                style.setBackgroundColor(userStyle.backgroundColor);
+                style.setForegroundColor(userStyle.foregroundColor);
+
+                switch (userStyle.edgeType) {
+                    case CaptioningManager.CaptionStyle.EDGE_TYPE_OUTLINE:
+                        style.setEdgeType(EDGE_TYPE_OUTLINE);
+                        break;
+
+                    case CaptioningManager.CaptionStyle.EDGE_TYPE_DROP_SHADOW:
+                        style.setEdgeType(EDGE_TYPE_DROP_SHADOW);
+                        break;
+
+                    case CaptioningManager.CaptionStyle.EDGE_TYPE_NONE:  // Fall through
+                    default:
+                        style.setEdgeType(EDGE_TYPE_NONE);
+                }
+
+                style.setEdgeColor(userStyle.edgeColor);
+
+                Typeface typeface = userStyle.getTypeface();
+                if (typeface != null) {
+                    if (Typeface.MONOSPACE.equals(typeface)) {
+                        style.setFontGenericFamily(FONT_FAMILY_MONOSPACED_SANS_SERIF);
+                    } else if (Typeface.SANS_SERIF.equals(typeface)) {
+                        style.setFontGenericFamily(FONT_FAMILY_SANS_SERIF);
+                    } else if (Typeface.SERIF.equals(typeface)) {
+                        style.setFontGenericFamily(FONT_FAMILY_SERIF);
+                    } else {
+                        // Otherwise, assume sans-serif.
+                        style.setFontGenericFamily(FONT_FAMILY_SANS_SERIF);
+                    }
+
+                    boolean bold = typeface.isBold();
+                    boolean italic = typeface.isItalic();
+
+                    if (bold && italic) {
+                        style.setFontStyle(FONT_STYLE_BOLD_ITALIC);
+                    } else if (bold) {
+                        style.setFontStyle(FONT_STYLE_BOLD);
+                    } else if (italic) {
+                        style.setFontStyle(FONT_STYLE_ITALIC);
+                    } else {
+                        style.setFontStyle(FONT_STYLE_NORMAL);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * A class that represents an image that is located on a web server.
+     */
+    public static final class WebImage {
+        private final Uri mUrl;
+        private final int mWidth;
+        private final int mHeight;
+
+        private static final String KEY_URL = "url";
+        private static final String KEY_HEIGHT = "height";
+        private static final String KEY_WIDTH = "width";
+
+        /**
+         * Constructs a new {@link WebImage} with the given URL.
+         *
+         * @param url The URL of the image.
+         * @throws IllegalArgumentException If the URL is null or empty.
+         */
+        public WebImage(@NonNull Uri url) throws IllegalArgumentException {
+            this(url, 0, 0);
+        }
+
+        /**
+         * Constructs a new {@link WebImage} with the given URL and dimensions.
+         *
+         * @param url The URL of the image.
+         * @param width The width of the image, in pixels.
+         * @param height The height of the image, in pixels.
+         * @throws IllegalArgumentException If the URL is null or empty,
+         * or the dimensions are invalid.
+         */
+        public WebImage(@NonNull Uri url, int width, int height) throws IllegalArgumentException {
+            if (url == null) {
+                throw new IllegalArgumentException("url cannot be null");
+            }
+
+            if ((width < 0) || (height < 0)) {
+                throw new IllegalArgumentException("width and height must not be negative");
+            }
+
+            mUrl = url;
+            mWidth = width;
+            mHeight = height;
+        }
+
+        /**
+         * Gets the image URL.
+         */
+        public @NonNull Uri getUrl() {
+            return mUrl;
+        }
+
+        /**
+         * Gets the image width, in pixels.
+         */
+        public int getWidth() {
+            return mWidth;
+        }
+
+        /**
+         * Gets the image height, in pixels.
+         */
+        public int getHeight() {
+            return mHeight;
+        }
+
+        /**
+         * Returns a string representation of this object.
+         */
+        @Override
+        public @NonNull String toString() {
+            return String.format("Image %dx%d %s", mWidth, mHeight, mUrl.toString());
+        }
+
+        /**
+         * Creates a bundle representation of this object.
+         */
+        public @NonNull Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putString(KEY_URL, mUrl.toString());
+            bundle.putInt(KEY_WIDTH, mWidth);
+            bundle.putInt(KEY_HEIGHT, mHeight);
+            return bundle;
+        }
+
+        /**
+         * Creates a {@link WebImage} from a bundle.
+         */
+        public static @Nullable WebImage fromBundle(@Nullable Bundle bundle) {
+            if (bundle == null) {
+                return null;
+            }
+            return new WebImage(Uri.parse(bundle.getString(KEY_URL)),
+                    bundle.getInt(KEY_WIDTH), bundle.getInt(KEY_HEIGHT));
+        }
+    }
+}
diff --git a/media/protocols/src/android/support/media/protocols/MediaRouteProtocol.java b/media/protocols/src/android/support/media/protocols/MediaRouteProtocol.java
new file mode 100644
index 0000000..cb49732
--- /dev/null
+++ b/media/protocols/src/android/support/media/protocols/MediaRouteProtocol.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2014 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.media.protocols;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.lang.ref.WeakReference;
+
+/**
+ * Base class for media route protocols.
+ * <p>
+ * A media route protocol expresses an interface contract between an application and
+ * a media route that it would like to communicate with and control.  By using
+ * a protocol to send messages to a media route, an application can
+ * ask the media route to perform functions such as creating a playlist of music
+ * to be played on behalf of the application.
+ * </p><p>
+ * Subclasses should extend this class to offer specialized protocols.
+ * </p><p>
+ * Instances of this class are thread-safe but event will only be received
+ * on the handler that was specified when the callback was registered.
+ * </p>
+ *
+ * <h3>Overview</h3>
+ * <p>
+ * A media route protocol is essentially just a binder-based messaging interface.
+ * Messages sent from the application to the media route service are called "requests"
+ * whereas messages sent from the media route service back to the application are
+ * called "events" or "errors" depending on their purpose.
+ * </p><p>
+ * All communication through a protocol is asynchronous and is dispatched to a
+ * a {@link android.os.Looper} of the application or the media route service's choice
+ * (separate for each end).  Arguments are transferred through key/value pairs in
+ * {@link Bundle bundles}.
+ * </p><p>
+ * The overall interface is somewhat simpler than directly using AIDL and Binder which
+ * requires additional care to extend and maintain binary compatibility and to
+ * perform thread synchronization on either end of the communication channel.
+ * Media route protocols also support bidirectional asynchronous communication
+ * requests, events, and errors between the application and the media route service.
+ * </p>
+ *
+ * <h3>Using Protocols</h3>
+ * <p>
+ * To use a protocol, an application must do the following.
+ * </p><ul>
+ * <li>Create a {@link android.media.routing.MediaRouter media router}.
+ * <li>Add a {@link android.media.routing.MediaRouteSelector media route selector}
+ * that specifies the protocol as required or optional.
+ * <li>Show a media route button in the application's action bar to enable the
+ * user to choose a destination to connect to.
+ * <li>Once the connection has been established, obtain the protocol's
+ * binder from the {@link android.media.routing.MediaRouter.ConnectionInfo route connection}
+ * information and {@link MediaRouteProtocol#MediaRouteProtocol(IBinder) create} the protocol
+ * object.  There is also a convenience method called
+ * {@link android.media.routing.MediaRouter.ConnectionInfo#getProtocolObject getProtocolObject}
+ * to do this all in one step.
+ * <li>Set a {@link Callback} on the protocol object to receive events.
+ * <li>At this point, the application can begin sending requests to the media route
+ * and receiving events in response via the protocol object.
+ * </ul>
+ *
+ * <h3>Providing Protocols</h3>
+ * <p>
+ * The provide a protocol, a media route service must do the following.
+ * </p><ul>
+ * <li>Upon receiving a
+ * {@link android.media.routing.MediaRouter.DiscoveryRequest discovery request}
+ * from an application that contains a
+ * {@link android.media.routing.MediaRouteSelector media route selector}
+ * which asks to find routes that support known protocols during discovery, the media
+ * route service should indicate that it supports this protocol by adding it to the list
+ * of supported protocols in the
+ * {@link android.media.routing.MediaRouter.RouteInfo route information} for those
+ * routes that support them.
+ * <li>Upon receiving a
+ * {@link android.media.routing.MediaRouter.ConnectionRequest connection request}
+ * from an application that requests to connect to a route for which the application
+ * previously requested support of known protocols, the media route service should
+ * {@link MediaRouteProtocol.Stub#MediaRouteProtocol.Stub(Handler) create} a subclass of the stub
+ * object that implements the protocol then add it to the list of protocol binders
+ * in the {@link android.media.routing.MediaRouter.ConnectionInfo route connection}
+ * object it returns to the application.
+ * <li>Once the route is connected, the media route service should handle incoming
+ * protocol requests from the client and respond accordingly.
+ * <li>Once the route is disconnected, the media route service should cease to
+ * handle incoming protocol requests from the client and should clean up its state
+ * accordingly.
+ * </ul>
+ *
+ * <h3>Creating Custom Protocols</h3>
+ * <p>
+ * Although the framework provides standard media route protocols to encourage
+ * interoperability, it may be useful to create and publish custom protocols to
+ * access extended functionality only supported by certain routes.
+ * </p><p>
+ * To create a custom protocol, create a subclass of the {@link MediaRouteProtocol}
+ * class to declare the new request methods and marshal their arguments.  Also create
+ * a subclass of the {@link MediaRouteProtocol.Callback} class to decode any new kinds
+ * of events and subclass the {@link MediaRouteProtocol.Stub} class to decode
+ * incoming requests.
+ * </p><p>
+ * It may help to refer to the source code of the <code>android.support.media.protocol.jar</code>
+ * library for details.
+ * </p><p>
+ * Here is a simple example:
+ * </p><pre>
+ * public abstract class CustomProtocol extends MediaRouteProtocol {
+ *     public CustomProtocol(IBinder binder) {
+ *         super(binder);
+ *     }
+ *
+ *     // declare custom request
+ *     public void tuneRadio(int station) {
+ *         Bundle args = new Bundle();
+ *         args.putInt("station", station);
+ *         sendRequest("tuneRadio", args);
+ *     }
+ *
+ *     public static abstract class Callback extends MediaRouteProtocol.Callback {
+ *         // declare custom event
+ *         public void onStationTuned(int station, boolean hifi) { }
+ *
+ *         &#064;Override
+ *         public void onEvent(String event, Bundle args) {
+ *             switch (event) {
+ *                 case "radioTuned":
+ *                     onRadioTuned(args.getInt("station"), args.getBoolean("hifi"));
+ *                     return;
+ *             }
+ *             super.onEvent(event, args);
+ *         }
+ *     }
+ *
+ *     public static abstract class Stub extends MediaRouteProtocol.Stub {
+ *         // declare custom request stub
+ *         public abstract void onTuneRadio(int station);
+ *
+ *         &#064;Override
+ *         public void onRequest(String request, Bundle args) {
+ *             switch (request) {
+ *                 case "tuneRadio":
+ *                     onTuneRadio(args.getInt("station"));
+ *                     return;
+ *             }
+ *             super.onRequest(request, args);
+ *         }
+ *     }
+ * }
+ * </pre>
+ */
+public abstract class MediaRouteProtocol {
+    private static final String TAG = "MediaRouteProtocol";
+
+    private static final int REQUEST_MSG_SUBSCRIBE = 1;
+    private static final int REQUEST_MSG_COMMAND = 2;
+
+    private static final int REPLY_MSG_ERROR = 1;
+    private static final int REPLY_MSG_EVENT = 2;
+
+    private final Object mLock = new Object();
+    private final Messenger mRequestMessenger;
+    private Messenger mReplyMessenger;
+    private volatile Callback mCallback;
+    private Looper mCallbackLooper;
+    private Handler mCallbackHandler;
+
+    /**
+     * Error code: Some other unknown error occurred.
+     */
+    public static final String ERROR_UNKNOWN =
+            "android.support.errors.UNKNOWN";
+
+    /**
+     * Error code: The media route has been disconnected.
+     */
+    public static final String ERROR_DISCONNECTED =
+            "android.support.errors.DISCONNECTED";
+
+    /**
+     * Error code: The application issued an unsupported request.
+     */
+    public static final String ERROR_UNSUPPORTED_OPERATION =
+            "android.support.errors.UNSUPPORTED_OPERATION";
+
+    /**
+     * Creates the protocol client object for an application to use to send
+     * messages to a media route.
+     * <p>
+     * This constructor is called automatically if you use
+     * {@link android.media.routing.MediaRouter.ConnectionInfo#getProtocolObject getProtocolObject}
+     * to obtain a protocol object from a media route connection.
+     * </p>
+     *
+     * @param binder The remote binder supplied by the media route service.  May be
+     * obtained using {@link android.media.routing.MediaRouter.ConnectionInfo#getProtocolBinder}
+     * on a route connection.
+     */
+    public MediaRouteProtocol(@NonNull IBinder binder) {
+        if (binder == null) {
+            throw new IllegalArgumentException("binder must not be null");
+        }
+
+        mRequestMessenger = new Messenger(binder);
+    }
+
+    /**
+     * Sets the callback interface and handler on which to receive events and errors.
+     *
+     * @param callback The callback interface, or null if none.
+     * @param handler The handler on which to receive events and errors, or null to use
+     * the current looper thread.
+     */
+    public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
+        synchronized (mLock) {
+            Looper looper = callback != null ?
+                    (handler != null ? handler.getLooper() : Looper.myLooper()) : null;
+            if (mCallback != callback || mCallbackLooper != looper) {
+                mCallback = callback;
+                if (mCallback != null) {
+                    mCallbackLooper = looper;
+                    mCallbackHandler = handler != null ? handler : new Handler();
+                    mReplyMessenger = new Messenger(new ReplyHandler(this, looper));
+                } else {
+                    mCallbackLooper = null;
+                    mCallbackHandler = null;
+                    mReplyMessenger = null;
+                }
+
+                Message msg = Message.obtain();
+                msg.what = REQUEST_MSG_SUBSCRIBE;
+                msg.replyTo = mReplyMessenger;
+                sendSafelyLocked(msg);
+            }
+        }
+    }
+
+    /**
+     * Sends an asynchronous request to the media route service.
+     * <p>
+     * If an error occurs, it will be reported to the callback's {@link Callback#onError}
+     * method.
+     * </p>
+     *
+     * @param request The request name.
+     * @param args The request arguments, or null if none.
+     */
+    public void sendRequest(@NonNull String request, @Nullable Bundle args) {
+        if (TextUtils.isEmpty(request)) {
+            throw new IllegalArgumentException("request must not be null or empty");
+        }
+
+        synchronized (mLock) {
+            Message msg = Message.obtain();
+            msg.what = REQUEST_MSG_COMMAND;
+            msg.obj = request;
+            msg.setData(args);
+            sendSafelyLocked(msg);
+        }
+    }
+
+    private void sendSafelyLocked(Message msg) {
+        if (mRequestMessenger != null) {
+            try {
+                mRequestMessenger.send(msg);
+            } catch (RemoteException ex) {
+                postErrorLocked(ERROR_DISCONNECTED, null);
+            }
+        } else {
+            postErrorLocked(ERROR_DISCONNECTED, null);
+        }
+    }
+
+    private void postErrorLocked(final String error, final Bundle args) {
+        final Callback callback = mCallback;
+        if (callback != null) {
+            mCallbackHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (callback != null) {
+                        callback.onError(error, args);
+                    }
+                }
+            });
+        }
+    }
+
+    private void handleReply(Message msg) {
+        Callback callback = mCallback; // note: racy
+        if (callback != null) {
+            // ignore unrecognized messages in case of future protocol extension
+            if (msg.what == REPLY_MSG_ERROR && msg.obj instanceof String) {
+                mCallback.onError((String)msg.obj, msg.peekData());
+            } else if (msg.what == REPLY_MSG_EVENT && msg.obj instanceof String) {
+                mCallback.onEvent((String)msg.obj, msg.peekData());
+            }
+        }
+    }
+
+    /*
+     * Only use this handler to handle replies coming back from the media route service
+     * because the service can send any message it wants to it.
+     * Validate arguments carefully.
+     */
+    private static final class ReplyHandler extends Handler {
+        // break hard reference cycles through binder
+        private final WeakReference<MediaRouteProtocol> mProtocolRef;
+
+        public ReplyHandler(MediaRouteProtocol protocol, Looper looper) {
+            super(looper);
+            mProtocolRef = new WeakReference<MediaRouteProtocol>(protocol);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            MediaRouteProtocol protocol = mProtocolRef.get();
+            if (protocol != null) {
+                protocol.handleReply(msg);
+            }
+        }
+    }
+
+    /**
+     * Base class for application callbacks from the media route service.
+     * <p>
+     * Subclasses should extend this class to offer events for specialized protocols.
+     * </p>
+     */
+    public static abstract class Callback {
+        /**
+         * Called when an event is received from the media route service.
+         *
+         * @param event The event name.
+         * @param args The event arguments, or null if none.
+         */
+        public void onEvent(@NonNull String event, @Nullable Bundle args) { }
+
+        /**
+         * Called when an error occurs in the media route service.
+         *
+         * @param error The error name.
+         * @param args The error arguments, or null if none.
+         */
+        public void onError(@NonNull String error, @Nullable Bundle args) { }
+    }
+
+    /**
+     * Base class for a media route protocol stub implemented by a media route service.
+     * <p>
+     * Subclasses should extend this class to offer implementation for specialized
+     * protocols.
+     * </p><p>
+     * Instances of this class are thread-safe but requests will only be received
+     * on the handler that was specified in the constructor.
+     * </p>
+     */
+    public static abstract class Stub implements IInterface, Closeable {
+        private final Object mLock = new Object();
+
+        private final Messenger mRequestMessenger;
+        private Messenger mReplyMessenger;
+        private volatile boolean mClosed;
+
+        /**
+         * Creates an implementation of a media route protocol.
+         *
+         * @param handler The handler on which to receive requests, or null to use
+         * the current looper thread.
+         */
+        public Stub(@Nullable Handler handler) {
+            mRequestMessenger = new Messenger(new RequestHandler(this,
+                    handler != null ? handler.getLooper() : Looper.myLooper()));
+        }
+
+        /**
+         * Gets the underlying binder object for the stub.
+         */
+        @Override
+        public @NonNull IBinder asBinder() {
+            return mRequestMessenger.getBinder();
+        }
+
+        /**
+         * Closes the stub and prevents it from receiving any additional
+         * messages from the application.
+         */
+        @Override
+        public void close() {
+            synchronized (mLock) {
+                mClosed = true;
+                mReplyMessenger = null;
+            }
+        }
+
+        /**
+         * Called when the application sends a request to the media route service
+         * through this protocol.
+         * <p>
+         * The default implementation throws {@link UnsupportedOperationException}
+         * which is reported back to the application's error callback as
+         * {@link #ERROR_UNSUPPORTED_OPERATION}.
+         * </p>
+         *
+         * @param request The request name.
+         * @param args The request arguments, or null if none.
+         */
+        public void onRequest(@NonNull String request, @Nullable Bundle args)
+                throws UnsupportedOperationException {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Called when the application attaches a callback to receive events and errors.
+         */
+        public void onClientAttached() {
+        }
+
+        /**
+         * Called when the application removes its callback and can no longer receive
+         * events and errors.
+         */
+        public void onClientDetached() {
+        }
+
+        /**
+         * Sends an error to the application.
+         *
+         * @param error The error name.
+         * @param args The error arguments, or null if none.
+         * @return True if the message was sent, or false if the client is not
+         * attached, cannot be reached, or if the stub has been closed.
+         */
+        public boolean sendError(@NonNull String error, @Nullable Bundle args) {
+            if (TextUtils.isEmpty(error)) {
+                throw new IllegalArgumentException("error must not be null or empty");
+            }
+
+            synchronized (mLock) {
+                Message msg = Message.obtain();
+                msg.what = REPLY_MSG_ERROR;
+                msg.obj = error;
+                msg.setData(args);
+                return replySafelyLocked(msg);
+            }
+        }
+
+        /**
+         * Sends an event to the application.
+         *
+         * @param event The event name.
+         * @param args The event arguments, or null if none.
+         * @return True if the message was sent, or false if the client is not
+         * attached, cannot be reached, or if the stub has been closed.
+         */
+        public boolean sendEvent(@NonNull String event, @Nullable Bundle args) {
+            if (TextUtils.isEmpty(event)) {
+                throw new IllegalArgumentException("event must not be null or empty");
+            }
+
+            synchronized (mLock) {
+                Message msg = Message.obtain();
+                msg.what = REPLY_MSG_EVENT;
+                msg.obj = event;
+                msg.setData(args);
+                return replySafelyLocked(msg);
+            }
+        }
+
+        private boolean replySafelyLocked(Message msg) {
+            if (mClosed) {
+                Log.w(TAG, "Could not send reply message because the stub has been closed: "
+                        + msg + ", in: " + getClass().getName());
+                return false;
+            }
+            if (mReplyMessenger == null) {
+                Log.w(TAG, "Could not send reply message because the client has not yet "
+                        + "attached a callback: " + msg + ", in: " + getClass().getName());
+                return false;
+            }
+
+            try {
+                mReplyMessenger.send(msg);
+                return true;
+            } catch (RemoteException ex) {
+                Log.w(TAG, "Could not send reply message because the client died: "
+                        + msg + ", in: " + getClass().getName());
+                return false;
+            }
+        }
+
+        private void handleRequest(Message msg) {
+            if (mClosed) { // note: racy
+                Log.w(TAG, "Dropping request because the media route service has "
+                        + "closed its end of the protocol: " + msg + ", in: " + getClass());
+                return;
+            }
+
+            // ignore unrecognized messages in case of future protocol extension
+            if (msg.what == REQUEST_MSG_COMMAND && msg.obj instanceof String) {
+                String command = (String)msg.obj;
+                try {
+                    onRequest(command, msg.peekData());
+                } catch (UnsupportedOperationException ex) {
+                    Log.w(TAG, "Client sent unsupported command request: "
+                            + msg + ", in: " + getClass());
+                    sendError(ERROR_UNSUPPORTED_OPERATION, null);
+                } catch (RuntimeException ex) {
+                    Log.e(TAG, "Stub threw runtime exception while processing command "
+                            + "request: " + msg + ", in: " + getClass());
+                    sendError(ERROR_UNKNOWN, null);
+                }
+            } else if (msg.what == REQUEST_MSG_SUBSCRIBE) {
+                synchronized (mLock) {
+                    if (mClosed) {
+                        return; // fix possible race if close() is called on another thread
+                    }
+                    mReplyMessenger = msg.replyTo;
+                }
+                if (msg.replyTo != null) {
+                    onClientAttached();
+                } else {
+                    onClientDetached();
+                }
+            }
+        }
+
+        /*
+         * Use this handler only to handle requests coming from the application
+         * because the application can send any message it wants to it.
+         */
+        private static final class RequestHandler extends Handler {
+            // break hard reference cycles through binder
+            private final WeakReference<Stub> mStubRef;
+
+            public RequestHandler(Stub stub, Looper looper) {
+                super(looper);
+                mStubRef = new WeakReference<Stub>(stub);
+            }
+
+            @Override
+            public void handleMessage(Message msg) {
+                Stub stub = mStubRef.get();
+                if (stub != null) {
+                    stub.handleRequest(msg);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 856c8d9..7e907c2 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -27,3 +27,6 @@
 
 include ':support-leanback-v17'
 project(':support-leanback-v17').projectDir = new File(rootDir, 'v17/leanback')
+
+include ':support-design'
+project(':support-design').projectDir = new File(rootDir, 'design')
diff --git a/tests/java/android/support/v4/text/IcuCompatTest.java b/tests/java/android/support/v4/text/IcuCompatTest.java
new file mode 100644
index 0000000..6f7f459
--- /dev/null
+++ b/tests/java/android/support/v4/text/IcuCompatTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.text;
+
+import junit.framework.TestCase;
+
+import java.util.Locale;
+
+public class IcuCompatTest extends TestCase {
+    public void testMaximizeAndGetScript() {
+        assertEquals("Latn", ICUCompat.maximizeAndGetScript(new Locale("en", "US")));
+        assertEquals("Visp", ICUCompat.maximizeAndGetScript(Locale.forLanguageTag("en-Visp-US")));
+    }
+}
diff --git a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
index 1339318..89be82e 100644
--- a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
+++ b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
@@ -184,7 +184,7 @@
         }
         for (int i=0; i<mFragments.size(); i++) {
             Fragment f = mFragments.get(i);
-            if (f != null) {
+            if (f != null && f.isAdded()) {
                 if (state == null) {
                     state = new Bundle();
                 }
diff --git a/v17/leanback/generatev4.py b/v17/leanback/generatev4.py
index 58a727a..605e9a1 100755
--- a/v17/leanback/generatev4.py
+++ b/v17/leanback/generatev4.py
@@ -20,7 +20,7 @@
 print "Generate v4 fragment related code for leanback"
 
 cls = ['Background', 'Base', 'BaseRow', 'Browse', 'Details', 'Error', 'Headers',
-      'PlaybackOverlay', 'Rows', 'Search', 'VerticalGrid']
+      'PlaybackOverlay', 'Rows', 'Search', 'VerticalGrid', 'Branded']
 
 for w in cls:
     print "copy {}Fragment to {}SupportFragment".format(w, w)
diff --git a/v4/api21/android/support/v4/view/ViewGroupCompatApi21.java b/v17/leanback/jbmr2/android/support/v17/leanback/os/TraceHelperJbmr2.java
similarity index 61%
copy from v4/api21/android/support/v4/view/ViewGroupCompatApi21.java
copy to v17/leanback/jbmr2/android/support/v17/leanback/os/TraceHelperJbmr2.java
index 5ebd187..70b8ce9 100644
--- a/v4/api21/android/support/v4/view/ViewGroupCompatApi21.java
+++ b/v17/leanback/jbmr2/android/support/v17/leanback/os/TraceHelperJbmr2.java
@@ -11,20 +11,19 @@
  * 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.
+ * limitations under the License
  */
+package android.support.v17.leanback.os;
 
-package android.support.v4.view;
+import android.os.Trace;
 
-import android.view.ViewGroup;
+class TraceHelperJbmr2 {
 
-class ViewGroupCompatApi21 {
-
-    public static void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-        group.setTransitionGroup(isTransitionGroup);
+    public static void beginSection(String section) {
+        Trace.beginSection(section);
     }
 
-    public static boolean isTransitionGroup(ViewGroup group) {
-        return group.isTransitionGroup();
+    public static void endSection() {
+        Trace.endSection();
     }
 }
diff --git a/v17/leanback/res/drawable-hdpi/lb_action_bg_focused.9.png b/v17/leanback/res/drawable-hdpi/lb_action_bg_focused.9.png
index 112b541..3058076 100644
--- a/v17/leanback/res/drawable-hdpi/lb_action_bg_focused.9.png
+++ b/v17/leanback/res/drawable-hdpi/lb_action_bg_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_card_shadow_focused.9.png b/v17/leanback/res/drawable-hdpi/lb_card_shadow_focused.9.png
index 7c59b7f..653419e 100644
--- a/v17/leanback/res/drawable-hdpi/lb_card_shadow_focused.9.png
+++ b/v17/leanback/res/drawable-hdpi/lb_card_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_card_shadow_normal.9.png b/v17/leanback/res/drawable-hdpi/lb_card_shadow_normal.9.png
index 4abb20a..9780ed2 100644
--- a/v17/leanback/res/drawable-hdpi/lb_card_shadow_normal.9.png
+++ b/v17/leanback/res/drawable-hdpi/lb_card_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_ic_actions_right_arrow.png b/v17/leanback/res/drawable-hdpi/lb_ic_actions_right_arrow.png
index e737a66..b4c0abe 100644
--- a/v17/leanback/res/drawable-hdpi/lb_ic_actions_right_arrow.png
+++ b/v17/leanback/res/drawable-hdpi/lb_ic_actions_right_arrow.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_ic_in_app_search.png b/v17/leanback/res/drawable-hdpi/lb_ic_in_app_search.png
index 542dd87..283b4d8 100644
--- a/v17/leanback/res/drawable-hdpi/lb_ic_in_app_search.png
+++ b/v17/leanback/res/drawable-hdpi/lb_ic_in_app_search.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_ic_sad_cloud.png b/v17/leanback/res/drawable-hdpi/lb_ic_sad_cloud.png
index 222b514..f45f1fd 100644
--- a/v17/leanback/res/drawable-hdpi/lb_ic_sad_cloud.png
+++ b/v17/leanback/res/drawable-hdpi/lb_ic_sad_cloud.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_ic_search_mic.png b/v17/leanback/res/drawable-hdpi/lb_ic_search_mic.png
index 5db5420..25617f5 100644
--- a/v17/leanback/res/drawable-hdpi/lb_ic_search_mic.png
+++ b/v17/leanback/res/drawable-hdpi/lb_ic_search_mic.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_ic_search_mic_out.png b/v17/leanback/res/drawable-hdpi/lb_ic_search_mic_out.png
index 78e9b1e..2eaecbd 100644
--- a/v17/leanback/res/drawable-hdpi/lb_ic_search_mic_out.png
+++ b/v17/leanback/res/drawable-hdpi/lb_ic_search_mic_out.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_in_app_search_bg.9.png b/v17/leanback/res/drawable-hdpi/lb_in_app_search_bg.9.png
index d5c5dc1..e27282e 100644
--- a/v17/leanback/res/drawable-hdpi/lb_in_app_search_bg.9.png
+++ b/v17/leanback/res/drawable-hdpi/lb_in_app_search_bg.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_focused.9.png b/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_focused.9.png
index 562ae9d..e296282 100644
--- a/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_focused.9.png
+++ b/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_normal.9.png b/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_normal.9.png
index db44754..df53d0d 100644
--- a/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_normal.9.png
+++ b/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_action_bg_focused.9.png b/v17/leanback/res/drawable-mdpi/lb_action_bg_focused.9.png
index 1d2b041..823c69c 100644
--- a/v17/leanback/res/drawable-mdpi/lb_action_bg_focused.9.png
+++ b/v17/leanback/res/drawable-mdpi/lb_action_bg_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_card_shadow_focused.9.png b/v17/leanback/res/drawable-mdpi/lb_card_shadow_focused.9.png
index 39b220c..2112154 100644
--- a/v17/leanback/res/drawable-mdpi/lb_card_shadow_focused.9.png
+++ b/v17/leanback/res/drawable-mdpi/lb_card_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_card_shadow_normal.9.png b/v17/leanback/res/drawable-mdpi/lb_card_shadow_normal.9.png
index b9c3400..8252ee4 100644
--- a/v17/leanback/res/drawable-mdpi/lb_card_shadow_normal.9.png
+++ b/v17/leanback/res/drawable-mdpi/lb_card_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_ic_actions_right_arrow.png b/v17/leanback/res/drawable-mdpi/lb_ic_actions_right_arrow.png
index aa6b44f..8c2c3b9 100644
--- a/v17/leanback/res/drawable-mdpi/lb_ic_actions_right_arrow.png
+++ b/v17/leanback/res/drawable-mdpi/lb_ic_actions_right_arrow.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_ic_in_app_search.png b/v17/leanback/res/drawable-mdpi/lb_ic_in_app_search.png
index ffdc8ec..9fd5012 100644
--- a/v17/leanback/res/drawable-mdpi/lb_ic_in_app_search.png
+++ b/v17/leanback/res/drawable-mdpi/lb_ic_in_app_search.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_ic_sad_cloud.png b/v17/leanback/res/drawable-mdpi/lb_ic_sad_cloud.png
index d2ec22a..b0bed22 100644
--- a/v17/leanback/res/drawable-mdpi/lb_ic_sad_cloud.png
+++ b/v17/leanback/res/drawable-mdpi/lb_ic_sad_cloud.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_ic_search_mic.png b/v17/leanback/res/drawable-mdpi/lb_ic_search_mic.png
index 7cc6d61..75eb962 100644
--- a/v17/leanback/res/drawable-mdpi/lb_ic_search_mic.png
+++ b/v17/leanback/res/drawable-mdpi/lb_ic_search_mic.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_ic_search_mic_out.png b/v17/leanback/res/drawable-mdpi/lb_ic_search_mic_out.png
index 9e36b2e..1682a46 100644
--- a/v17/leanback/res/drawable-mdpi/lb_ic_search_mic_out.png
+++ b/v17/leanback/res/drawable-mdpi/lb_ic_search_mic_out.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_in_app_search_bg.9.png b/v17/leanback/res/drawable-mdpi/lb_in_app_search_bg.9.png
index be062cc..fdf205e 100644
--- a/v17/leanback/res/drawable-mdpi/lb_in_app_search_bg.9.png
+++ b/v17/leanback/res/drawable-mdpi/lb_in_app_search_bg.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_focused.9.png b/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_focused.9.png
index b63a57f..8ef4ae9 100644
--- a/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_focused.9.png
+++ b/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_normal.9.png b/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_normal.9.png
index d91acee..36ea129 100644
--- a/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_normal.9.png
+++ b/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_action_bg_focused.9.png b/v17/leanback/res/drawable-xhdpi/lb_action_bg_focused.9.png
index 5e7c2be..cafe2ab 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_action_bg_focused.9.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_action_bg_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_card_shadow_focused.9.png b/v17/leanback/res/drawable-xhdpi/lb_card_shadow_focused.9.png
index 599928b..635fa5c 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_card_shadow_focused.9.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_card_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_card_shadow_normal.9.png b/v17/leanback/res/drawable-xhdpi/lb_card_shadow_normal.9.png
index e5413a8..060d56f 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_card_shadow_normal.9.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_card_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_actions_right_arrow.png b/v17/leanback/res/drawable-xhdpi/lb_ic_actions_right_arrow.png
index 9796171..d0ca2e2 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_actions_right_arrow.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_actions_right_arrow.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_card_info_text_fade.png b/v17/leanback/res/drawable-xhdpi/lb_ic_card_info_text_fade.png
index 1364a48..a09fdee 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_card_info_text_fade.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_card_info_text_fade.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_cc.png b/v17/leanback/res/drawable-xhdpi/lb_ic_cc.png
index 518c063..d4616cf 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_cc.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_cc.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_fast_forward.png b/v17/leanback/res/drawable-xhdpi/lb_ic_fast_forward.png
index c5afb69..0dfefcc 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_fast_forward.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_fast_forward.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_fast_rewind.png b/v17/leanback/res/drawable-xhdpi/lb_ic_fast_rewind.png
index d9774ef..09e8a3b 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_fast_rewind.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_fast_rewind.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_hq.png b/v17/leanback/res/drawable-xhdpi/lb_ic_hq.png
index f797d38..5aefe6d 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_hq.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_hq.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_in_app_search.png b/v17/leanback/res/drawable-xhdpi/lb_ic_in_app_search.png
index 1dd0d0f..8ef325b 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_in_app_search.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_in_app_search.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_loop.png b/v17/leanback/res/drawable-xhdpi/lb_ic_loop.png
index 988f572..791386e 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_loop.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_loop.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_loop_one.png b/v17/leanback/res/drawable-xhdpi/lb_ic_loop_one.png
index 4ae9faa..e10f5d3 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_loop_one.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_loop_one.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_more.png b/v17/leanback/res/drawable-xhdpi/lb_ic_more.png
index ef62a69..662e03c 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_more.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_more.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_pause.png b/v17/leanback/res/drawable-xhdpi/lb_ic_pause.png
index 01e07f7..e55f78d 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_pause.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_pause.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_play.png b/v17/leanback/res/drawable-xhdpi/lb_ic_play.png
index 1556076..fbd792b 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_play.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_play.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_playback_loop.png b/v17/leanback/res/drawable-xhdpi/lb_ic_playback_loop.png
index f444ca4..e5c9acc 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_playback_loop.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_playback_loop.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_replay.png b/v17/leanback/res/drawable-xhdpi/lb_ic_replay.png
index a76d8fe..c5a0294 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_replay.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_replay.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_sad_cloud.png b/v17/leanback/res/drawable-xhdpi/lb_ic_sad_cloud.png
index b048503..93a74f6 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_sad_cloud.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_sad_cloud.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic.png b/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic.png
index a99d693..0785c8b 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic_out.png b/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic_out.png
index f8b3cca..7bbfa23 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic_out.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic_out.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_shuffle.png b/v17/leanback/res/drawable-xhdpi/lb_ic_shuffle.png
index 32cf2dc..5aa850b 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_shuffle.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_shuffle.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_skip_next.png b/v17/leanback/res/drawable-xhdpi/lb_ic_skip_next.png
index 2173375..7349a07 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_skip_next.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_skip_next.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_skip_previous.png b/v17/leanback/res/drawable-xhdpi/lb_ic_skip_previous.png
index 2c6035a..a27e543 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_skip_previous.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_skip_previous.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_stop.png b/v17/leanback/res/drawable-xhdpi/lb_ic_stop.png
index b96f297..586c4bd 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_stop.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_stop.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down.png b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down.png
index 85dd902..6e9d472 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down_outline.png b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down_outline.png
index 2208fdf..6000fa3 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down_outline.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_down_outline.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up.png b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up.png
index b24b146..54b9ad4 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up_outline.png b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up_outline.png
index d6a5240..7a9706e 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up_outline.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_thumb_up_outline.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_in_app_search_bg.9.png b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_bg.9.png
index 8cc5438..894ab76 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_in_app_search_bg.9.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_bg.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_focused.9.png b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_focused.9.png
index f913c0f..125d6d1 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_focused.9.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_normal.9.png b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_normal.9.png
index 791ffd7..5204234 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_normal.9.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_text_dot_one.png b/v17/leanback/res/drawable-xhdpi/lb_text_dot_one.png
index bdc82af..af12c1d 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_text_dot_one.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_text_dot_one.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_text_dot_one_small.png b/v17/leanback/res/drawable-xhdpi/lb_text_dot_one_small.png
index 80ed1c2..219bfca 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_text_dot_one_small.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_text_dot_one_small.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_text_dot_two.png b/v17/leanback/res/drawable-xhdpi/lb_text_dot_two.png
index deec9bb..7861019 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_text_dot_two.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_text_dot_two.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_text_dot_two_small.png b/v17/leanback/res/drawable-xhdpi/lb_text_dot_two_small.png
index b22cb9c..65f522c 100644
--- a/v17/leanback/res/drawable-xhdpi/lb_text_dot_two_small.png
+++ b/v17/leanback/res/drawable-xhdpi/lb_text_dot_two_small.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_action_bg_focused.9.png b/v17/leanback/res/drawable-xxhdpi/lb_action_bg_focused.9.png
index d2227b9..1bef6f2 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_action_bg_focused.9.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_action_bg_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_card_shadow_focused.9.png b/v17/leanback/res/drawable-xxhdpi/lb_card_shadow_focused.9.png
index 125bf12..3f1affa 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_card_shadow_focused.9.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_card_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_card_shadow_normal.9.png b/v17/leanback/res/drawable-xxhdpi/lb_card_shadow_normal.9.png
index 887d24f..921688a 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_card_shadow_normal.9.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_card_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_ic_actions_right_arrow.png b/v17/leanback/res/drawable-xxhdpi/lb_ic_actions_right_arrow.png
index 6539869..42b7c77 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_ic_actions_right_arrow.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_ic_actions_right_arrow.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_ic_in_app_search.png b/v17/leanback/res/drawable-xxhdpi/lb_ic_in_app_search.png
index f47827f..b45deb6 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_ic_in_app_search.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_ic_in_app_search.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_ic_sad_cloud.png b/v17/leanback/res/drawable-xxhdpi/lb_ic_sad_cloud.png
index 825c693..08fd07c 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_ic_sad_cloud.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_ic_sad_cloud.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic.png b/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic.png
index c8a0790..a36a912 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic_out.png b/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic_out.png
index cb97131..8c251e1 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic_out.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic_out.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_bg.9.png b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_bg.9.png
index b9e372e..fef8b07 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_bg.9.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_bg.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_focused.9.png b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_focused.9.png
index 65f3a9e..ceb6a40 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_focused.9.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_normal.9.png b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_normal.9.png
index 9bc3f6f..18d2fcb 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_normal.9.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/layout/lb_browse_title.xml b/v17/leanback/res/layout/lb_browse_title.xml
index e14350a..d3c944b 100644
--- a/v17/leanback/res/layout/lb_browse_title.xml
+++ b/v17/leanback/res/layout/lb_browse_title.xml
@@ -18,9 +18,5 @@
     android:id="@+id/browse_title_group"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:paddingTop="?attr/browsePaddingTop"
-    android:paddingEnd="?attr/browsePaddingEnd"
-    android:paddingStart="?attr/browsePaddingStart"
-    android:paddingBottom="?attr/browsePaddingTop"
     style="?attr/browseTitleViewStyle" />
 
diff --git a/v17/leanback/res/layout/lb_details_fragment.xml b/v17/leanback/res/layout/lb_details_fragment.xml
index 92cf4b4..578e269 100644
--- a/v17/leanback/res/layout/lb_details_fragment.xml
+++ b/v17/leanback/res/layout/lb_details_fragment.xml
@@ -19,8 +19,21 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent" >
 
-    <FrameLayout
-        android:id="@+id/fragment_dock"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent" />
+    <android.support.v17.leanback.widget.BrowseFrameLayout
+        android:id="@+id/details_frame"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:descendantFocusability="afterDescendants"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+
+        <FrameLayout
+            android:id="@+id/details_rows_dock"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent" />
+
+        <include layout="@layout/lb_browse_title" />
+
+    </android.support.v17.leanback.widget.BrowseFrameLayout>
+
 </FrameLayout>
diff --git a/v17/leanback/res/layout/lb_list_row.xml b/v17/leanback/res/layout/lb_list_row.xml
index 80d7bef..8d673f1 100644
--- a/v17/leanback/res/layout/lb_list_row.xml
+++ b/v17/leanback/res/layout/lb_list_row.xml
@@ -17,7 +17,10 @@
 
 <android.support.v17.leanback.widget.HorizontalGridView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:lb="http://schemas.android.com/apk/res-auto"
     android:id="@+id/row_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:clipToPadding="false"
+    lb:rowHeight="wrap_content"
     style="?attr/rowHorizontalGridStyle" />
diff --git a/v17/leanback/res/layout/lb_playback_controls_row.xml b/v17/leanback/res/layout/lb_playback_controls_row.xml
index 395dfd1..a58840d 100644
--- a/v17/leanback/res/layout/lb_playback_controls_row.xml
+++ b/v17/leanback/res/layout/lb_playback_controls_row.xml
@@ -16,9 +16,9 @@
 -->
 
 <!-- Note: clipChildren/clipToPadding false are needed to apply shadows to child
-     views with no padding of their own. Also to allow for negative marge on description. -->
+     views with no padding of their own. Also to allow for negative margin on description. -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.v17.leanback.widget.PlaybackControlsRowView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
@@ -30,7 +30,7 @@
     <LinearLayout
         android:id="@+id/controls_card"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/lb_playback_controls_card_height"
+        android:layout_height="wrap_content"
         android:layout_marginBottom="@dimen/lb_playback_controls_margin_bottom"
         android:clipChildren="false"
         android:clipToPadding="false"
@@ -39,13 +39,14 @@
         <ImageView
             android:id="@+id/image"
             android:layout_width="wrap_content"
-            android:layout_height="match_parent"
+            android:layout_height="@dimen/lb_playback_controls_card_height"
             android:adjustViewBounds="true"
             android:scaleType="fitStart" />
 
         <LinearLayout
+            android:id="@+id/controls_card_right_panel"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
+            android:layout_height="wrap_content"
             android:clipChildren="false"
             android:clipToPadding="false"
             android:orientation="vertical" >
@@ -53,21 +54,18 @@
             <FrameLayout
                 android:id="@+id/description_dock"
                 android:layout_width="wrap_content"
-                android:layout_height="0dp"
+                android:layout_height="wrap_content"
                 android:layout_marginEnd="@dimen/lb_playback_description_margin_end"
                 android:layout_marginStart="@dimen/lb_playback_description_margin_start"
                 android:paddingTop="@dimen/lb_playback_description_margin_top"
                 android:clipToPadding="false"
                 android:clipChildren="false"
-                android:layout_weight="1"
                 android:gravity="top" />
 
-            <!-- Space here prevents room only for title and subtitle above -->
-
             <Space
                 android:id="@+id/spacer"
                 android:layout_width="match_parent"
-                android:layout_height="12dp" />
+                android:layout_height="24dp" />
 
             <FrameLayout
                 android:id="@+id/controls_dock"
@@ -90,4 +88,4 @@
         android:layout_width="match_parent"
         android:layout_height="@dimen/lb_playback_controls_margin_bottom" />
 
-</LinearLayout>
\ No newline at end of file
+</android.support.v17.leanback.widget.PlaybackControlsRowView>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_row_container.xml b/v17/leanback/res/layout/lb_row_container.xml
index 69fb0e1..31b0f58 100644
--- a/v17/leanback/res/layout/lb_row_container.xml
+++ b/v17/leanback/res/layout/lb_row_container.xml
@@ -20,7 +20,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="horizontal"
-    android:paddingStart="?attr/browsePaddingStart"
+    style="?attr/rowHeaderDockStyle"
     android:clipToPadding="false">
 </android.support.v17.leanback.widget.NonOverlappingLinearLayout>
 </merge>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_rows_fragment.xml b/v17/leanback/res/layout/lb_rows_fragment.xml
index c188b3c..0a5112d 100644
--- a/v17/leanback/res/layout/lb_rows_fragment.xml
+++ b/v17/leanback/res/layout/lb_rows_fragment.xml
@@ -24,6 +24,7 @@
         android:id="@+id/container_list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+        android:clipToPadding="false"
         style="?attr/rowsVerticalGridStyle" />
 
 </android.support.v17.leanback.widget.ScaleFrameLayout>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_shadow.xml b/v17/leanback/res/layout/lb_shadow.xml
index b0aa0b1..af4e4aa 100644
--- a/v17/leanback/res/layout/lb_shadow.xml
+++ b/v17/leanback/res/layout/lb_shadow.xml
@@ -15,13 +15,12 @@
      limitations under the License.
 -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <View
+    <android.support.v17.leanback.widget.NonOverlappingView
         android:id="@+id/lb_shadow_normal"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:background="@drawable/lb_card_shadow_normal" />
-    <View
+    <android.support.v17.leanback.widget.NonOverlappingView
         android:id="@+id/lb_shadow_focused"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/v17/leanback/res/layout/lb_title_view.xml b/v17/leanback/res/layout/lb_title_view.xml
index e82621e..e1c79ef 100644
--- a/v17/leanback/res/layout/lb_title_view.xml
+++ b/v17/leanback/res/layout/lb_title_view.xml
@@ -17,7 +17,7 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <ImageView
-        android:id="@+id/browse_badge"
+        android:id="@+id/title_badge"
         android:layout_width="wrap_content"
         android:maxWidth="@dimen/lb_browse_title_icon_max_width"
         android:adjustViewBounds="true"
@@ -28,14 +28,14 @@
         style="?attr/browseTitleIconStyle"/>
 
     <TextView
-        android:id="@+id/browse_title"
+        android:id="@+id/title_text"
         android:layout_width="@dimen/lb_browse_title_text_width"
         android:layout_height="@dimen/lb_browse_title_height"
         android:layout_gravity="center_vertical|end"
         style="?attr/browseTitleTextStyle"/>
 
     <android.support.v17.leanback.widget.SearchOrbView
-        android:id="@+id/browse_orb"
+        android:id="@+id/title_orb"
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         android:transitionGroup="true"
diff --git a/v17/leanback/res/layout/lb_vertical_grid.xml b/v17/leanback/res/layout/lb_vertical_grid.xml
index 7154e48..504c4c9 100644
--- a/v17/leanback/res/layout/lb_vertical_grid.xml
+++ b/v17/leanback/res/layout/lb_vertical_grid.xml
@@ -17,8 +17,11 @@
 
 <android.support.v17.leanback.widget.VerticalGridView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:lb="http://schemas.android.com/apk/res-auto"
     android:id="@+id/browse_grid"
     android:layout_width="wrap_content"
     android:layout_height="match_parent"
     android:layout_gravity="center"
+    android:clipToPadding="false"
+    lb:columnWidth="wrap_content"
     style="?attr/itemsVerticalGridStyle" />
diff --git a/v17/leanback/res/layout/lb_vertical_grid_fragment.xml b/v17/leanback/res/layout/lb_vertical_grid_fragment.xml
index b287986..118b0a9 100644
--- a/v17/leanback/res/layout/lb_vertical_grid_fragment.xml
+++ b/v17/leanback/res/layout/lb_vertical_grid_fragment.xml
@@ -20,19 +20,19 @@
     android:layout_height="match_parent" >
 
     <android.support.v17.leanback.widget.BrowseFrameLayout
-        android:id="@+id/browse_frame"
+        android:id="@+id/grid_frame"
         android:focusable="true"
         android:focusableInTouchMode="true"
         android:descendantFocusability="afterDescendants"
         android:layout_width="match_parent"
         android:layout_height="match_parent" >
 
-        <include layout="@layout/lb_browse_title" />
-
         <FrameLayout
             android:id="@+id/browse_grid_dock"
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
 
+        <include layout="@layout/lb_browse_title" />
+
     </android.support.v17.leanback.widget.BrowseFrameLayout>
 </FrameLayout>
diff --git a/v17/leanback/res/transition-v21/lb_browse_return_transition.xml b/v17/leanback/res/transition-v21/lb_browse_return_transition.xml
index ee88fff..84ae993 100644
--- a/v17/leanback/res/transition-v21/lb_browse_return_transition.xml
+++ b/v17/leanback/res/transition-v21/lb_browse_return_transition.xml
@@ -21,7 +21,7 @@
       android:slideEdge="left">
       <targets>
           <target android:targetId="@id/browse_headers_root" />
-          <target android:targetId="@id/browse_orb" />
+          <target android:targetId="@id/title_orb" />
       </targets>
   </slide>
   <slide
@@ -30,7 +30,7 @@
       android:slideEdge="right">
       <targets>
           <target android:excludeId="@+id/browse_headers_root" />
-          <target android:excludeId="@+id/browse_orb" />
+          <target android:excludeId="@+id/title_orb" />
       </targets>
   </slide>
   <fade
diff --git a/v17/leanback/res/transition-v21/lb_details_enter_transition.xml b/v17/leanback/res/transition-v21/lb_details_enter_transition.xml
index 240d4bc..633acc2 100644
--- a/v17/leanback/res/transition-v21/lb_details_enter_transition.xml
+++ b/v17/leanback/res/transition-v21/lb_details_enter_transition.xml
@@ -20,5 +20,10 @@
       android:duration="500"
       android:interpolator="@android:interpolator/linear_out_slow_in"
       android:slideEdge="bottom">
-  </transition>
+        <targets>
+            <target android:excludeId="@id/title_badge" />
+            <target android:excludeId="@id/title_text" />
+            <target android:excludeId="@id/title_orb" />
+        </targets>
+    </transition>
 </transitionSet>
\ No newline at end of file
diff --git a/v17/leanback/res/transition-v21/lb_details_return_transition.xml b/v17/leanback/res/transition-v21/lb_details_return_transition.xml
index f555533..f31ebcb 100644
--- a/v17/leanback/res/transition-v21/lb_details_return_transition.xml
+++ b/v17/leanback/res/transition-v21/lb_details_return_transition.xml
@@ -19,6 +19,11 @@
       android:interpolator="@android:interpolator/fast_out_linear_in"
       android:duration="350"
       android:slideEdge="bottom">
+      <targets>
+          <target android:excludeId="@id/title_badge" />
+          <target android:excludeId="@id/title_text" />
+          <target android:excludeId="@id/title_orb" />
+      </targets>
   </transition>
   <fade
       android:interpolator="@android:interpolator/fast_out_linear_in"
diff --git a/v17/leanback/res/transition-v22/lb_browse_return_transition.xml b/v17/leanback/res/transition-v22/lb_browse_return_transition.xml
index 62a273e..e8dbee0 100644
--- a/v17/leanback/res/transition-v22/lb_browse_return_transition.xml
+++ b/v17/leanback/res/transition-v22/lb_browse_return_transition.xml
@@ -21,7 +21,7 @@
       android:slideEdge="start">
       <targets>
           <target android:targetId="@id/browse_headers_root" />
-          <target android:targetId="@id/browse_orb" />
+          <target android:targetId="@id/title_orb" />
       </targets>
   </slide>
   <slide
@@ -30,7 +30,7 @@
       android:slideEdge="end">
       <targets>
           <target android:excludeId="@+id/browse_headers_root" />
-          <target android:excludeId="@+id/browse_orb" />
+          <target android:excludeId="@+id/title_orb" />
       </targets>
   </slide>
   <fade
diff --git a/v17/leanback/res/values-gl-rES/strings.xml b/v17/leanback/res/values-gl-rES/strings.xml
index a3884d8..717b994 100644
--- a/v17/leanback/res/values-gl-rES/strings.xml
+++ b/v17/leanback/res/values-gl-rES/strings.xml
@@ -33,9 +33,9 @@
     <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Saltar seguinte"</string>
     <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Saltar anterior"</string>
     <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Máis accións"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Deseleccionar polgar cara arriba"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Anular \"Gústame\""</string>
     <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Seleccionar polgar cara arriba"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Deseleccionar polgar cara abaixo"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Anular \"Non me gusta\""</string>
     <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Seleccionar polgar cara abaixo"</string>
     <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Non repetir"</string>
     <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Repetir todo"</string>
diff --git a/v17/leanback/res/values-km-rKH/strings.xml b/v17/leanback/res/values-km-rKH/strings.xml
index ea7d0f4..7874af2 100644
--- a/v17/leanback/res/values-km-rKH/strings.xml
+++ b/v17/leanback/res/values-km-rKH/strings.xml
@@ -37,7 +37,7 @@
     <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"ជ្រើស​មេ​ដៃ​ឡើង​លើ"</string>
     <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"មិន​ជ្រើស​​មេដៃ​ចុះ​ក្រោម"</string>
     <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"ជ្រើស​​មេ​ដៃ​ចុះក្រោម"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"មិន​ធ្វើ​ឡើង​វិញ"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"មិន​ធ្វើ​ឡើង​វិញ​"</string>
     <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"ធ្វើ​ម្ដង​ទៀត​ទាំងអស់"</string>
     <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"ធ្វើ​​ឡើងវិញ​ម្ដង"</string>
     <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"បើក​ការ​​ច្របល់"</string>
diff --git a/v17/leanback/res/values-my-rMM/strings.xml b/v17/leanback/res/values-my-rMM/strings.xml
index 77a2271..2efaf7f 100644
--- a/v17/leanback/res/values-my-rMM/strings.xml
+++ b/v17/leanback/res/values-my-rMM/strings.xml
@@ -37,13 +37,13 @@
     <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"နှစ်ခြိုက်သော သင်္က​ေတအား ရွေးရန်"</string>
     <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"မနှစ်ခြိုက်သော သင်္က​ေတအား မရွေးရန်"</string>
     <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"မနှစ်ခြိုက်သော သင်္က​ေတအား ရွေးရန်"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ထပ်တလဲလဲမဖွင့်ရန်"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"အားလုံး ထပ်တလဲလဲဖွင့်ရန်"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"တစ်ခုအား ထပ်တလဲလဲဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"ထပ်တလဲလဲမဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"အားလုံး ထပ်တလဲလဲဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"တစ်ခုအား ထပ်တလဲလဲဖွင့်ရန်"</string>
     <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"ရောသမမွှေခြင်း ပြုရန်"</string>
     <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"ရောသမမေွှခြင်း မပြုရန်"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"အရည်အသွေးကောင်းအား ဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"အရည်အသွေးကောင်းအား ဖွင့်ရန်"</string>
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"အရည်အသွေးကောင်းအား ပိတ်ထားရန်"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"စာတမ်းထိုး ဖွင့်ရန်"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"စာတမ်းထိုး ဖွင့်ရန်"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"စာတမ်းထိုးအား ပိတ်ထားရန်"</string>
 </resources>
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index 7038fff..923ba3e 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -223,6 +223,8 @@
         <attr name="rowHorizontalGridStyle" format="reference" />
         <!-- header style inside a row -->
         <attr name="rowHeaderStyle" format="reference" />
+        <!-- style for the layout that hosting Header inside a row -->
+        <attr name="rowHeaderDockStyle" format="reference" />
 
         <!-- hover card title style -->
         <attr name="rowHoverCardTitleStyle" format="reference" />
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index 3fef5ee..8c4c198 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -58,6 +58,7 @@
     <dimen name="lb_browse_expanded_selected_row_top_padding">16dp</dimen>
     <dimen name="lb_browse_expanded_row_no_hovercard_bottom_padding">28dp</dimen>
 
+    <item name="lb_focus_zoom_factor_xsmall" type="fraction">106%</item>
     <item name="lb_focus_zoom_factor_small" type="fraction">110%</item>
     <item name="lb_focus_zoom_factor_medium" type="fraction">114%</item>
     <item name="lb_focus_zoom_factor_large" type="fraction">118%</item>
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index 03be3ac..71c5122 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -98,6 +98,10 @@
     </style>
 
     <style name="Widget.Leanback.TitleView" >
+        <item name="android:paddingTop">?attr/browsePaddingTop</item>
+        <item name="android:paddingBottom">?attr/browsePaddingTop</item>
+        <item name="android:paddingStart">?attr/browsePaddingStart</item>
+        <item name="android:paddingEnd">?attr/browsePaddingEnd</item>
     </style>
 
     <style name="Widget.Leanback.Title" />
@@ -127,7 +131,6 @@
 
     <style name="Widget.Leanback.Headers.VerticalGridView" >
         <item name="android:paddingStart">?attr/browsePaddingStart</item>
-        <item name="android:clipToPadding">false</item>
         <item name="focusOutFront">true</item>
         <item name="focusOutEnd">true</item>
         <item name="verticalMargin">@dimen/lb_browse_headers_vertical_margin</item>
@@ -137,6 +140,7 @@
 
     <style name="Widget.Leanback.Header" >
         <item name="android:minHeight">@dimen/lb_browse_header_height</item>
+        <item name="android:minWidth">1dp</item>
         <item name="android:textAppearance">@style/TextAppearance.Leanback.Header</item>
         <item name="android:singleLine">true</item>
         <item name="android:ellipsize">none</item>
@@ -144,7 +148,6 @@
 
     <style name="Widget.Leanback.Rows.VerticalGridView" >
         <item name="android:paddingBottom">?attr/browsePaddingBottom</item>
-        <item name="android:clipToPadding">false</item>
         <item name="focusOutFront">true</item>
         <item name="focusOutEnd">true</item>
         <item name="android:focusable">true</item>
@@ -152,7 +155,6 @@
     </style>
 
     <style name="Widget.Leanback.Row.HorizontalGridView">
-        <item name="android:clipToPadding">false</item>
         <item name="android:focusable">true</item>
         <item name="android:focusableInTouchMode">true</item>
         <item name="android:paddingStart">?attr/browsePaddingStart</item>
@@ -162,11 +164,9 @@
         <item name="horizontalMargin">@dimen/lb_browse_item_horizontal_margin</item>
         <item name="verticalMargin">@dimen/lb_browse_item_vertical_margin</item>
         <item name="focusOutFront">true</item>
-        <item name="rowHeight">wrap_content</item>
     </style>
 
     <style name="Widget.Leanback.GridItems.VerticalGridView">
-        <item name="android:clipToPadding">false</item>
         <item name="android:focusable">true</item>
         <item name="android:focusableInTouchMode">true</item>
         <item name="android:paddingStart">?attr/browsePaddingStart</item>
@@ -176,7 +176,6 @@
         <item name="android:gravity">center_horizontal</item>
         <item name="horizontalMargin">@dimen/lb_browse_item_horizontal_margin</item>
         <item name="verticalMargin">@dimen/lb_browse_item_vertical_margin</item>
-        <item name="columnWidth">wrap_content</item>
         <item name="focusOutFront">true</item>
     </style>
 
@@ -184,6 +183,10 @@
         <item name="android:textAppearance">@style/TextAppearance.Leanback.Row.Header</item>
     </style>
 
+    <style name="Widget.Leanback.Row.HeaderDock">
+        <item name="android:paddingStart">?attr/browsePaddingStart</item>
+    </style>
+
     <style name="TextAppearance.Leanback.Row.HoverCardTitle" parent="TextAppearance.Leanback">
         <item name="android:textSize">@dimen/lb_browse_row_hovercard_title_font_size</item>
     </style>
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index 0503e17..457798b 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -53,6 +53,7 @@
         <item name="rowHeaderStyle">@style/Widget.Leanback.Row.Header</item>
         <item name="rowHoverCardTitleStyle">@style/Widget.Leanback.Row.HoverCardTitle</item>
         <item name="rowHoverCardDescriptionStyle">@style/Widget.Leanback.Row.HoverCardDescription</item>
+        <item name="rowHeaderDockStyle">@style/Widget.Leanback.Row.HeaderDock</item>
 
         <item name="searchOrbViewStyle">@style/Widget.Leanback.SearchOrbViewStyle</item>
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
index 7346753..87b55b7 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
@@ -15,6 +15,7 @@
 
 import java.lang.ref.WeakReference;
 
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.animation.Animator;
 import android.animation.ValueAnimator;
@@ -32,6 +33,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.os.Handler;
+import android.support.v4.view.animation.FastOutLinearInInterpolator;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -39,7 +41,7 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
+import android.view.animation.AnimationUtils;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.content.ContextCompat;
 
@@ -108,6 +110,12 @@
     private Drawable mBackgroundDrawable;
     private int mBackgroundColor;
     private boolean mAttached;
+    private long mLastSetTime;
+
+    private final Interpolator mAccelerateInterpolator;
+    private final Interpolator mDecelerateInterpolator;
+    private final ValueAnimator mAnimator;
+    private final ValueAnimator mDimAnimator;
 
     private static class BitmapDrawable extends Drawable {
 
@@ -179,23 +187,11 @@
     private static class DrawableWrapper {
         protected int mAlpha;
         protected Drawable mDrawable;
-        protected ValueAnimator mAnimator;
-        protected boolean mAnimationPending;
-
-        private final Interpolator mInterpolator = new LinearInterpolator();
-        private final ValueAnimator.AnimatorUpdateListener mAnimationUpdateListener =
-                new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                setAlpha((Integer) animation.getAnimatedValue());
-            }
-        };
 
         public DrawableWrapper(Drawable drawable) {
             mDrawable = drawable;
             setAlpha(FULL_ALPHA);
         }
-
         public Drawable getDrawable() {
             return mDrawable;
         }
@@ -209,39 +205,6 @@
         public void setColor(int color) {
             ((ColorDrawable) mDrawable).setColor(color);
         }
-        public void fadeIn(int durationMs, int delayMs) {
-            fade(durationMs, delayMs, FULL_ALPHA);
-        }
-        public void fadeOut(int durationMs) {
-            fade(durationMs, 0, 0);
-        }
-        public void fade(int durationMs, int delayMs, int alpha) {
-            if (mAnimator != null && mAnimator.isStarted()) {
-                mAnimator.cancel();
-            }
-            mAnimator = ValueAnimator.ofInt(getAlpha(), alpha);
-            mAnimator.addUpdateListener(mAnimationUpdateListener);
-            mAnimator.setInterpolator(mInterpolator);
-            mAnimator.setDuration(durationMs);
-            mAnimator.setStartDelay(delayMs);
-            mAnimationPending = true;
-        }
-        public boolean isAnimationPending() {
-            return mAnimationPending;
-        }
-        public boolean isAnimationStarted() {
-            return mAnimator != null && mAnimator.isStarted();
-        }
-        public void startAnimation() {
-            startAnimation(null);
-        }
-        public void startAnimation(Animator.AnimatorListener listener) {
-            if (listener != null) {
-                mAnimator.addListener(listener);
-            }
-            mAnimator.start();
-            mAnimationPending = false;
-        }
     }
 
     private LayerDrawable mLayerDrawable;
@@ -251,9 +214,51 @@
     private DrawableWrapper mColorWrapper;
     private DrawableWrapper mDimWrapper;
 
-    private Drawable mThemeDrawable;
+    private Drawable mDimDrawable;
     private ChangeBackgroundRunnable mChangeRunnable;
 
+    private final Animator.AnimatorListener mAnimationListener = new Animator.AnimatorListener() {
+        @Override
+        public void onAnimationStart(Animator animation) {
+        }
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+        }
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (mChangeRunnable != null) {
+                long delayMs = getRunnableDelay();
+                if (DEBUG) Log.v(TAG, "animation ended, found change runnable delayMs " + delayMs);
+                mHandler.postDelayed(mChangeRunnable, delayMs);
+            }
+            mImageOutWrapper = null;
+        }
+        @Override
+        public void onAnimationCancel(Animator animation) {
+        }
+    };
+
+    private final ValueAnimator.AnimatorUpdateListener mAnimationUpdateListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            int fadeInAlpha = (Integer) animation.getAnimatedValue();
+            if (mImageInWrapper != null) {
+                mImageInWrapper.setAlpha(fadeInAlpha);
+            }
+        }
+    };
+
+    private final ValueAnimator.AnimatorUpdateListener mDimUpdateListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            if (mDimWrapper != null) {
+                mDimWrapper.setAlpha((Integer) animation.getAnimatedValue());
+            }
+        }
+    };
+
     /**
      * Shared memory continuity service.
      */
@@ -375,6 +380,20 @@
         mWidthPx = mContext.getResources().getDisplayMetrics().widthPixels;
         mHandler = new Handler();
 
+        Interpolator defaultInterpolator = new FastOutLinearInInterpolator();
+        mAccelerateInterpolator = AnimationUtils.loadInterpolator(mContext,
+                android.R.anim.accelerate_interpolator);
+        mDecelerateInterpolator = AnimationUtils.loadInterpolator(mContext,
+                android.R.anim.decelerate_interpolator);
+
+        mAnimator = ValueAnimator.ofInt(0, FULL_ALPHA);
+        mAnimator.addListener(mAnimationListener);
+        mAnimator.addUpdateListener(mAnimationUpdateListener);
+        mAnimator.setInterpolator(defaultInterpolator);
+
+        mDimAnimator = new ValueAnimator();
+        mDimAnimator.addUpdateListener(mDimUpdateListener);
+
         TypedArray ta = activity.getTheme().obtainStyledAttributes(new int[] {
                 android.R.attr.windowBackground });
         mThemeDrawableResourceId = ta.getResourceId(0, -1);
@@ -467,17 +486,30 @@
 
         mLayerDrawable.setDrawableByLayerId(R.id.background_imageout, createEmptyDrawable());
 
-        mDimWrapper = new DrawableWrapper(
-                mLayerDrawable.findDrawableByLayerId(R.id.background_dim));
+        mLayerDrawable.setDrawableByLayerId(R.id.background_theme, getThemeDrawable());
 
         mLayerWrapper = new DrawableWrapper(mLayerDrawable);
 
         mColorWrapper = new DrawableWrapper(
                 mLayerDrawable.findDrawableByLayerId(R.id.background_color));
+
+        updateDimWrapper();
+    }
+
+    private void updateDimWrapper() {
+        if (mDimDrawable == null) {
+            mDimDrawable = getDefaultDimLayer();
+        }
+        mDimWrapper = new DrawableWrapper(mDimDrawable.getConstantState().newDrawable(
+                mContext.getResources()).mutate());
+        if (mLayerDrawable != null) {
+            mLayerDrawable.setDrawableByLayerId(R.id.background_dim, mDimWrapper.getDrawable());
+        }
     }
 
     /**
-     * Make the background visible on the given Window.
+     * Make the background visible on the given Window.  The background manager must be attached
+     * when the background is set.
      */
     public void attach(Window window) {
         if (USE_SEPARATE_WINDOW) {
@@ -516,6 +548,13 @@
     }
 
     /**
+     * Returns true if the background manager is currently attached; false otherwise.
+     */
+    public boolean isAttached() {
+        return mAttached;
+    }
+
+    /**
      * Release references to Drawables and put the BackgroundManager into the
      * detached state. Called when the associated Activity is destroyed.
      * @hide
@@ -553,6 +592,7 @@
         if (mLayerDrawable != null) {
             mLayerDrawable.setDrawableByLayerId(R.id.background_imagein, createEmptyDrawable());
             mLayerDrawable.setDrawableByLayerId(R.id.background_imageout, createEmptyDrawable());
+            mLayerDrawable.setDrawableByLayerId(R.id.background_theme, createEmptyDrawable());
             mLayerDrawable = null;
         }
         mLayerWrapper = null;
@@ -560,9 +600,8 @@
         mImageOutWrapper = null;
         mColorWrapper = null;
         mDimWrapper = null;
-        mThemeDrawable = null;
         if (mChangeRunnable != null) {
-            mChangeRunnable.cancel();
+            mHandler.removeCallbacks(mChangeRunnable);
             mChangeRunnable = null;
         }
         releaseBackgroundBitmap();
@@ -572,6 +611,28 @@
         mBackgroundDrawable = null;
     }
 
+    /**
+     * Sets the drawable used as a dim layer.
+     */
+    public void setDimLayer(Drawable drawable) {
+        mDimDrawable = drawable;
+        updateDimWrapper();
+    }
+
+    /**
+     * Returns the drawable used as a dim layer.
+     */
+    public Drawable getDimLayer() {
+        return mDimDrawable;
+    }
+
+    /**
+     * Returns the default drawable used as a dim layer.
+     */
+    public Drawable getDefaultDimLayer() {
+        return ContextCompat.getDrawable(mContext, R.color.lb_background_protection);
+    }
+
     private void updateImmediate() {
         lazyInit();
 
@@ -581,9 +642,6 @@
         }
         showWallpaper(mBackgroundColor == Color.TRANSPARENT);
 
-        mThemeDrawable = getThemeDrawable();
-        mLayerDrawable.setDrawableByLayerId(R.id.background_theme, mThemeDrawable);
-
         if (mBackgroundDrawable == null) {
             mLayerDrawable.setDrawableByLayerId(R.id.background_imagein, createEmptyDrawable());
         } else {
@@ -600,7 +658,7 @@
      * Set the background to the given color. The timing for when this becomes
      * visible in the app is undefined and may take place after a small delay.
      */
-    public void setColor(int color) {
+    public void setColor(@ColorInt int color) {
         if (DEBUG) Log.v(TAG, "setColor " + Integer.toHexString(color));
 
         mBackgroundColor = color;
@@ -626,23 +684,30 @@
         if (!mAttached) {
             throw new IllegalStateException("Must attach before setting background drawable");
         }
+        long delayMs = getRunnableDelay();
+        mLastSetTime = System.currentTimeMillis();
 
         if (mChangeRunnable != null) {
             if (sameDrawable(drawable, mChangeRunnable.mDrawable)) {
-                if (DEBUG) Log.v(TAG, "setting same drawable");
+                if (DEBUG) Log.v(TAG, "new drawable same as pending");
                 return;
             }
-            mChangeRunnable.cancel();
+            mHandler.removeCallbacks(mChangeRunnable);
         }
         mChangeRunnable = new ChangeBackgroundRunnable(drawable);
 
-        if (mImageInWrapper != null && mImageInWrapper.isAnimationStarted()) {
+        if (mAnimator.isStarted()) {
             if (DEBUG) Log.v(TAG, "animation in progress");
         } else {
-            mHandler.postDelayed(mChangeRunnable, CHANGE_BG_DELAY_MS);
+            if (DEBUG) Log.v(TAG, "posting runnable delayMs " + delayMs);
+            mHandler.postDelayed(mChangeRunnable, delayMs);
         }
     }
 
+    private long getRunnableDelay() {
+        return Math.max(0, mLastSetTime + CHANGE_BG_DELAY_MS - System.currentTimeMillis());
+    }
+
     /**
      * Set the given bitmap into the background. When using setBitmap to set the
      * background, the provided bitmap will be scaled and cropped to correctly
@@ -703,13 +768,10 @@
 
         if (DEBUG) Log.v(TAG, "applyBackgroundChanges drawable " + mBackgroundDrawable);
 
-        int dimAlpha = 0;
+        int dimAlpha = -1;
 
-        if (mImageOutWrapper != null && mImageOutWrapper.isAnimationPending()) {
-            if (DEBUG) Log.v(TAG, "mImageOutWrapper animation starting");
-            mImageOutWrapper.startAnimation();
-            mImageOutWrapper = null;
-            dimAlpha = DIM_ALPHA_ON_SOLID;
+        if (mImageOutWrapper != null) {
+            dimAlpha = mBackgroundColor == Color.TRANSPARENT ? 0 : DIM_ALPHA_ON_SOLID;
         }
 
         if (mImageInWrapper == null && mBackgroundDrawable != null) {
@@ -718,40 +780,27 @@
             mLayerDrawable.setDrawableByLayerId(R.id.background_imagein, mBackgroundDrawable);
             if (DEBUG) Log.v(TAG, "mImageInWrapper animation starting");
             mImageInWrapper.setAlpha(0);
-            mImageInWrapper.fadeIn(FADE_DURATION, 0);
-            mImageInWrapper.startAnimation(mImageInListener);
             dimAlpha = FULL_ALPHA;
         }
 
-        if (mDimWrapper != null && dimAlpha != 0) {
+        mAnimator.setDuration(FADE_DURATION);
+        mAnimator.start();
+
+        if (mDimWrapper != null && dimAlpha >= 0) {
             if (DEBUG) Log.v(TAG, "dimwrapper animation starting to " + dimAlpha);
-            mDimWrapper.fade(FADE_DURATION, 0, dimAlpha);
-            mDimWrapper.startAnimation();
+            mDimAnimator.cancel();
+            mDimAnimator.setIntValues(mDimWrapper.getAlpha(), dimAlpha);
+            mDimAnimator.setDuration(FADE_DURATION);
+            mDimAnimator.setInterpolator(
+                    dimAlpha == FULL_ALPHA ? mDecelerateInterpolator : mAccelerateInterpolator);
+            mDimAnimator.start();
         }
     }
 
-    private final Animator.AnimatorListener mImageInListener = new Animator.AnimatorListener() {
-        @Override
-        public void onAnimationStart(Animator animation) {
-        }
-        @Override
-        public void onAnimationRepeat(Animator animation) {
-        }
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (mChangeRunnable != null) {
-                if (DEBUG) Log.v(TAG, "animation ended, found change runnable");
-                mChangeRunnable.run();
-            }
-        }
-        @Override
-        public void onAnimationCancel(Animator animation) {
-        }
-    };
-
     /**
      * Returns the current background color.
      */
+    @ColorInt
     public final int getColor() {
         return mBackgroundColor;
     }
@@ -783,44 +832,37 @@
      */
     class ChangeBackgroundRunnable implements Runnable {
         private Drawable mDrawable;
-        private boolean mCancel;
 
         ChangeBackgroundRunnable(Drawable drawable) {
             mDrawable = drawable;
         }
 
-        public void cancel() {
-            mCancel = true;
-        }
-
         @Override
         public void run() {
-            if (!mCancel) {
-                runTask();
-            }
+            runTask();
+            mChangeRunnable = null;
         }
 
         private void runTask() {
             lazyInit();
 
             if (sameDrawable(mDrawable, mBackgroundDrawable)) {
-                if (DEBUG) Log.v(TAG, "same bitmap detected");
+                if (DEBUG) Log.v(TAG, "new drawable same as current");
                 return;
             }
 
             releaseBackgroundBitmap();
 
             if (mImageInWrapper != null) {
+                if (DEBUG) Log.v(TAG, "moving image in to image out");
                 mImageOutWrapper = new DrawableWrapper(mImageInWrapper.getDrawable());
-                mImageOutWrapper.setAlpha(mImageInWrapper.getAlpha());
-                mImageOutWrapper.fadeOut(FADE_DURATION);
+                mImageOutWrapper.setAlpha(FULL_ALPHA);
 
                 // Order is important! Setting a drawable "removes" the
                 // previous one from the view
                 mLayerDrawable.setDrawableByLayerId(R.id.background_imagein, createEmptyDrawable());
                 mLayerDrawable.setDrawableByLayerId(R.id.background_imageout,
                         mImageOutWrapper.getDrawable());
-                mImageInWrapper.setAlpha(0);
                 mImageInWrapper = null;
             }
 
@@ -828,8 +870,6 @@
             mService.setDrawable(mBackgroundDrawable);
 
             applyBackgroundChanges();
-
-            mChangeRunnable = null;
         }
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
index 07e6123..d3b5182 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
@@ -13,7 +13,6 @@
  */
 package android.support.v17.leanback.app;
 
-import android.app.Fragment;
 import android.os.Bundle;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.transition.TransitionHelper;
@@ -24,7 +23,7 @@
 /**
  * @hide
  */
-class BaseFragment extends Fragment {
+class BaseFragment extends BrandedFragment {
 
     private boolean mEntranceTransitionEnabled = false;
     private boolean mStartEntranceTransitionPending = false;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
index 88439ef..337ea64 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
@@ -15,7 +15,6 @@
  */
 package android.support.v17.leanback.app;
 
-import android.support.v4.app.Fragment;
 import android.os.Bundle;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.transition.TransitionHelper;
@@ -26,7 +25,7 @@
 /**
  * @hide
  */
-class BaseSupportFragment extends Fragment {
+class BaseSupportFragment extends BrandedSupportFragment {
 
     private boolean mEntranceTransitionEnabled = false;
     private boolean mStartEntranceTransitionPending = false;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrandedFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrandedFragment.java
new file mode 100644
index 0000000..f454144
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrandedFragment.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2014 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.app;
+
+import android.app.Fragment;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.widget.SearchOrbView;
+import android.support.v17.leanback.widget.TitleHelper;
+import android.support.v17.leanback.widget.TitleView;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Fragment support for managing branding on a
+ * {@link android.support.v17.leanback.widget.TitleView}.
+ * @hide
+ */
+class BrandedFragment extends Fragment {
+
+    // BUNDLE attribute for title is showing
+    private static final String TITLE_SHOW = "titleShow";
+
+    private boolean mShowingTitle = true;
+    private String mTitle;
+    private Drawable mBadgeDrawable;
+    private TitleView mTitleView;
+    private SearchOrbView.Colors mSearchAffordanceColors;
+    private boolean mSearchAffordanceColorSet;
+    private View.OnClickListener mExternalOnSearchClickedListener;
+    private TitleHelper mTitleHelper;
+
+    /**
+     * Sets the {@link TitleView}.
+     */
+    void setTitleView(TitleView titleView) {
+        mTitleView = titleView;
+        if (mTitleView == null) {
+            mTitleHelper = null;
+        } else {
+            mTitleView.setTitle(mTitle);
+            mTitleView.setBadgeDrawable(mBadgeDrawable);
+            if (mSearchAffordanceColorSet) {
+                mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
+            }
+            if (mExternalOnSearchClickedListener != null) {
+                mTitleView.setOnSearchClickedListener(mExternalOnSearchClickedListener);
+            }
+            if (getView() instanceof ViewGroup) {
+                mTitleHelper = new TitleHelper((ViewGroup) getView(), mTitleView);
+            }
+        }
+    }
+
+    /**
+     * Returns the {@link TitleView}.
+     */
+    TitleView getTitleView() {
+        return mTitleView;
+    }
+
+    /**
+     * Returns the {@link TitleHelper}.
+     */
+    TitleHelper getTitleHelper() {
+        return mTitleHelper;
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(TITLE_SHOW, mShowingTitle);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        if (savedInstanceState != null) {
+            mShowingTitle = savedInstanceState.getBoolean(TITLE_SHOW);
+        }
+        if (mTitleView != null && view instanceof ViewGroup) {
+            mTitleHelper = new TitleHelper((ViewGroup) view, mTitleView);
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mTitleHelper = null;
+    }
+
+    /**
+     * Shows or hides the {@link android.support.v17.leanback.widget.TitleView}.
+     */
+    void showTitle(boolean show) {
+        // TODO: handle interruptions?
+        if (show == mShowingTitle) {
+            return;
+        }
+        mShowingTitle = show;
+        if (mTitleHelper != null) {
+            mTitleHelper.showTitle(show);
+        }
+    }
+
+    /**
+     * Sets the drawable displayed in the browse fragment title.
+     *
+     * @param drawable The Drawable to display in the browse fragment title.
+     */
+    public void setBadgeDrawable(Drawable drawable) {
+        if (mBadgeDrawable != drawable) {
+            mBadgeDrawable = drawable;
+            if (mTitleView != null) {
+                mTitleView.setBadgeDrawable(drawable);
+            }
+        }
+    }
+
+    /**
+     * Returns the badge drawable used in the fragment title.
+     */
+    public Drawable getBadgeDrawable() {
+        return mBadgeDrawable;
+    }
+
+    /**
+     * Sets a title for the browse fragment.
+     *
+     * @param title The title of the browse fragment.
+     */
+    public void setTitle(String title) {
+        mTitle = title;
+        if (mTitleView != null) {
+            mTitleView.setTitle(title);
+        }
+    }
+
+    /**
+     * Returns the title for the browse fragment.
+     */
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Sets a click listener for the search affordance.
+     *
+     * <p>The presence of a listener will change the visibility of the search
+     * affordance in the fragment title. When set to non-null, the title will
+     * contain an element that a user may click to begin a search.
+     *
+     * <p>The listener's {@link View.OnClickListener#onClick onClick} method
+     * will be invoked when the user clicks on the search element.
+     *
+     * @param listener The listener to call when the search element is clicked.
+     */
+    public void setOnSearchClickedListener(View.OnClickListener listener) {
+        mExternalOnSearchClickedListener = listener;
+        if (mTitleView != null) {
+            mTitleView.setOnSearchClickedListener(listener);
+        }
+    }
+
+    /**
+     * Sets the {@link android.support.v17.leanback.widget.SearchOrbView.Colors} used to draw the search affordance.
+     */
+    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
+        mSearchAffordanceColors = colors;
+        mSearchAffordanceColorSet = true;
+        if (mTitleView != null) {
+            mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
+        }
+    }
+
+    /**
+     * Returns the {@link android.support.v17.leanback.widget.SearchOrbView.Colors}
+     * used to draw the search affordance.
+     */
+    public SearchOrbView.Colors getSearchAffordanceColors() {
+        if (mSearchAffordanceColorSet) {
+            return mSearchAffordanceColors;
+        }
+        if (mTitleView == null) {
+            throw new IllegalStateException("Fragment views not yet created");
+        }
+        return mTitleView.getSearchAffordanceColors();
+    }
+
+    /**
+     * Sets the color used to draw the search affordance.
+     * A default brighter color will be set by the framework.
+     *
+     * @param color The color to use for the search affordance.
+     */
+    public void setSearchAffordanceColor(int color) {
+        setSearchAffordanceColors(new SearchOrbView.Colors(color));
+    }
+
+    /**
+     * Returns the color used to draw the search affordance.
+     */
+    public int getSearchAffordanceColor() {
+        return getSearchAffordanceColors().color;
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mTitleView != null) {
+            mTitleView.setVisibility(mShowingTitle ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        if (mTitleView != null) {
+            mTitleView.enableAnimation(false);
+        }
+        super.onPause();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mTitleView != null) {
+            mTitleView.enableAnimation(true);
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrandedSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrandedSupportFragment.java
new file mode 100644
index 0000000..18fae4f
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrandedSupportFragment.java
@@ -0,0 +1,244 @@
+/* This file is auto-generated from BrandedFragment.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2014 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.app;
+
+import android.support.v4.app.Fragment;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.widget.SearchOrbView;
+import android.support.v17.leanback.widget.TitleHelper;
+import android.support.v17.leanback.widget.TitleView;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Fragment support for managing branding on a
+ * {@link android.support.v17.leanback.widget.TitleView}.
+ * @hide
+ */
+class BrandedSupportFragment extends Fragment {
+
+    // BUNDLE attribute for title is showing
+    private static final String TITLE_SHOW = "titleShow";
+
+    private boolean mShowingTitle = true;
+    private String mTitle;
+    private Drawable mBadgeDrawable;
+    private TitleView mTitleView;
+    private SearchOrbView.Colors mSearchAffordanceColors;
+    private boolean mSearchAffordanceColorSet;
+    private View.OnClickListener mExternalOnSearchClickedListener;
+    private TitleHelper mTitleHelper;
+
+    /**
+     * Sets the {@link TitleView}.
+     */
+    void setTitleView(TitleView titleView) {
+        mTitleView = titleView;
+        if (mTitleView == null) {
+            mTitleHelper = null;
+        } else {
+            mTitleView.setTitle(mTitle);
+            mTitleView.setBadgeDrawable(mBadgeDrawable);
+            if (mSearchAffordanceColorSet) {
+                mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
+            }
+            if (mExternalOnSearchClickedListener != null) {
+                mTitleView.setOnSearchClickedListener(mExternalOnSearchClickedListener);
+            }
+            if (getView() instanceof ViewGroup) {
+                mTitleHelper = new TitleHelper((ViewGroup) getView(), mTitleView);
+            }
+        }
+    }
+
+    /**
+     * Returns the {@link TitleView}.
+     */
+    TitleView getTitleView() {
+        return mTitleView;
+    }
+
+    /**
+     * Returns the {@link TitleHelper}.
+     */
+    TitleHelper getTitleHelper() {
+        return mTitleHelper;
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(TITLE_SHOW, mShowingTitle);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        if (savedInstanceState != null) {
+            mShowingTitle = savedInstanceState.getBoolean(TITLE_SHOW);
+        }
+        if (mTitleView != null && view instanceof ViewGroup) {
+            mTitleHelper = new TitleHelper((ViewGroup) view, mTitleView);
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mTitleHelper = null;
+    }
+
+    /**
+     * Shows or hides the {@link android.support.v17.leanback.widget.TitleView}.
+     */
+    void showTitle(boolean show) {
+        // TODO: handle interruptions?
+        if (show == mShowingTitle) {
+            return;
+        }
+        mShowingTitle = show;
+        if (mTitleHelper != null) {
+            mTitleHelper.showTitle(show);
+        }
+    }
+
+    /**
+     * Sets the drawable displayed in the browse fragment title.
+     *
+     * @param drawable The Drawable to display in the browse fragment title.
+     */
+    public void setBadgeDrawable(Drawable drawable) {
+        if (mBadgeDrawable != drawable) {
+            mBadgeDrawable = drawable;
+            if (mTitleView != null) {
+                mTitleView.setBadgeDrawable(drawable);
+            }
+        }
+    }
+
+    /**
+     * Returns the badge drawable used in the fragment title.
+     */
+    public Drawable getBadgeDrawable() {
+        return mBadgeDrawable;
+    }
+
+    /**
+     * Sets a title for the browse fragment.
+     *
+     * @param title The title of the browse fragment.
+     */
+    public void setTitle(String title) {
+        mTitle = title;
+        if (mTitleView != null) {
+            mTitleView.setTitle(title);
+        }
+    }
+
+    /**
+     * Returns the title for the browse fragment.
+     */
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Sets a click listener for the search affordance.
+     *
+     * <p>The presence of a listener will change the visibility of the search
+     * affordance in the fragment title. When set to non-null, the title will
+     * contain an element that a user may click to begin a search.
+     *
+     * <p>The listener's {@link View.OnClickListener#onClick onClick} method
+     * will be invoked when the user clicks on the search element.
+     *
+     * @param listener The listener to call when the search element is clicked.
+     */
+    public void setOnSearchClickedListener(View.OnClickListener listener) {
+        mExternalOnSearchClickedListener = listener;
+        if (mTitleView != null) {
+            mTitleView.setOnSearchClickedListener(listener);
+        }
+    }
+
+    /**
+     * Sets the {@link android.support.v17.leanback.widget.SearchOrbView.Colors} used to draw the search affordance.
+     */
+    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
+        mSearchAffordanceColors = colors;
+        mSearchAffordanceColorSet = true;
+        if (mTitleView != null) {
+            mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
+        }
+    }
+
+    /**
+     * Returns the {@link android.support.v17.leanback.widget.SearchOrbView.Colors}
+     * used to draw the search affordance.
+     */
+    public SearchOrbView.Colors getSearchAffordanceColors() {
+        if (mSearchAffordanceColorSet) {
+            return mSearchAffordanceColors;
+        }
+        if (mTitleView == null) {
+            throw new IllegalStateException("Fragment views not yet created");
+        }
+        return mTitleView.getSearchAffordanceColors();
+    }
+
+    /**
+     * Sets the color used to draw the search affordance.
+     * A default brighter color will be set by the framework.
+     *
+     * @param color The color to use for the search affordance.
+     */
+    public void setSearchAffordanceColor(int color) {
+        setSearchAffordanceColors(new SearchOrbView.Colors(color));
+    }
+
+    /**
+     * Returns the color used to draw the search affordance.
+     */
+    public int getSearchAffordanceColor() {
+        return getSearchAffordanceColors().color;
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mTitleView != null) {
+            mTitleView.setVisibility(mShowingTitle ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        if (mTitleView != null) {
+            mTitleView.enableAnimation(false);
+        }
+        super.onPause();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mTitleView != null) {
+            mTitleView.enableAnimation(true);
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
index 2207ac6..4b38fe9 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
@@ -13,6 +13,7 @@
  */
 package android.support.v17.leanback.app;
 
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.transition.LeanbackTransitionHelper;
 import android.support.v17.leanback.transition.TransitionHelper;
@@ -24,13 +25,12 @@
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v17.leanback.widget.RowHeaderPresenter;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.TitleView;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
-import android.support.v17.leanback.widget.OnItemClickedListener;
 import android.support.v17.leanback.widget.SearchOrbView;
 import android.support.v4.view.ViewCompat;
 import android.util.Log;
@@ -50,7 +50,6 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-
 import static android.support.v7.widget.RecyclerView.NO_POSITION;
 
 /**
@@ -77,8 +76,7 @@
     static final String HEADER_STACK_INDEX = "headerStackIndex";
     // BUNDLE attribute for saving header show/hide status when backstack is not used:
     static final String HEADER_SHOW = "headerShow";
-    // BUNDLE attribute for title is showing
-    static final String TITLE_SHOW = "titleShow";
+
 
     final class BackStackListener implements FragmentManager.OnBackStackChangedListener {
         int mLastEntryCount;
@@ -155,6 +153,47 @@
         }
     }
 
+    private class SetSelectionRunnable implements Runnable {
+        static final int TYPE_INVALID = -1;
+        static final int TYPE_INTERNAL_SYNC = 0;
+        static final int TYPE_USER_REQUEST = 1;
+
+        private int mPosition;
+        private int mType;
+        private boolean mSmooth;
+
+        SetSelectionRunnable() {
+            reset();
+        }
+
+        void post(int position, int type, boolean smooth) {
+            // Posting the set selection, rather than calling it immediately, prevents an issue
+            // with adapter changes.  Example: a row is added before the current selected row;
+            // first the fast lane view updates its selection, then the rows fragment has that
+            // new selection propagated immediately; THEN the rows view processes the same adapter
+            // change and moves the selection again.
+            if (type >= mType) {
+                mPosition = position;
+                mType = type;
+                mSmooth = smooth;
+                mBrowseFrame.removeCallbacks(this);
+                mBrowseFrame.post(this);
+            }
+        }
+
+        @Override
+        public void run() {
+            setSelection(mPosition, mSmooth);
+            reset();
+        }
+
+        private void reset() {
+            mPosition = -1;
+            mType = TYPE_INVALID;
+            mSmooth = false;
+        }
+    }
+
     private static final String TAG = "BrowseFragment";
 
     private static final String LB_HEADERS_BACKSTACK = "lbHeadersBackStack_";
@@ -170,22 +209,16 @@
     /** The headers fragment is disabled and will never be shown. */
     public static final int HEADERS_DISABLED = 3;
 
-    private static final float SLIDE_DISTANCE_FACTOR = 2;
-
     private RowsFragment mRowsFragment;
     private HeadersFragment mHeadersFragment;
 
     private ObjectAdapter mAdapter;
 
-    private String mTitle;
-    private Drawable mBadgeDrawable;
     private int mHeadersState = HEADERS_ENABLED;
     private int mBrandColor = Color.TRANSPARENT;
     private boolean mBrandColorSet;
 
     private BrowseFrameLayout mBrowseFrame;
-    private TitleView mTitleView;
-    private boolean mShowingTitle = true;
     private boolean mHeadersBackStackEnabled = true;
     private String mWithHeadersBackStackName;
     private boolean mShowingHeaders = true;
@@ -193,25 +226,17 @@
     private int mContainerListMarginStart;
     private int mContainerListAlignTop;
     private boolean mRowScaleEnabled = true;
-    private SearchOrbView.Colors mSearchAffordanceColors;
-    private boolean mSearchAffordanceColorSet;
-    private OnItemSelectedListener mExternalOnItemSelectedListener;
-    private OnClickListener mExternalOnSearchClickedListener;
-    private OnItemClickedListener mOnItemClickedListener;
     private OnItemViewSelectedListener mExternalOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
     private int mSelectedPosition = -1;
 
     private PresenterSelector mHeaderPresenterSelector;
+    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
 
     // transition related:
-    private Object mSceneWithTitle;
-    private Object mSceneWithoutTitle;
     private Object mSceneWithHeaders;
     private Object mSceneWithoutHeaders;
     private Object mSceneAfterEntranceTransition;
-    private Object mTitleUpTransition;
-    private Object mTitleDownTransition;
     private Object mHeadersTransition;
     private BackStackListener mBackStackChangedListener;
     private BrowseTransitionListener mBrowseTransitionListener;
@@ -248,7 +273,7 @@
      *
      * @param color The color to use as the brand color of the fragment.
      */
-    public void setBrandColor(int color) {
+    public void setBrandColor(@ColorInt int color) {
         mBrandColor = color;
         mBrandColorSet = true;
 
@@ -261,6 +286,7 @@
      * Returns the brand color for the browse fragment.
      * The default is transparent.
      */
+    @ColorInt
     public int getBrandColor() {
         return mBrandColor;
     }
@@ -291,17 +317,6 @@
     }
 
     /**
-     * Sets an item selection listener. This listener will be called when an
-     * item or row is selected by a user.
-     *
-     * @param listener The listener to call when an item or row is selected.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mExternalOnItemSelectedListener = listener;
-    }
-
-    /**
      * Sets an item selection listener.
      */
     public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
@@ -317,33 +332,6 @@
 
     /**
      * Sets an item clicked listener on the fragment.
-     *
-     * <p>OnItemClickedListener will override {@link View.OnClickListener} that
-     * an item presenter may set during
-     * {@link Presenter#onCreateViewHolder(ViewGroup)}. So in general, you
-     * should choose to use an {@link OnItemClickedListener} or a
-     * {@link View.OnClickListener} on your item views, but not both.
-     *
-     * @param listener The listener to call when an item is clicked.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-        if (mRowsFragment != null) {
-            mRowsFragment.setOnItemClickedListener(listener);
-        }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     * @deprecated Use {@link #getOnItemViewClickedListener()}
-     */
-    public OnItemClickedListener getOnItemClickedListener() {
-        return mOnItemClickedListener;
-    }
-
-    /**
-     * Sets an item clicked listener on the fragment.
      * OnItemViewClickedListener will override {@link View.OnClickListener} that
      * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
      * So in general,  developer should choose one of the listeners but not both.
@@ -363,66 +351,6 @@
     }
 
     /**
-     * Sets a click listener for the search affordance.
-     *
-     * <p>The presence of a listener will change the visibility of the search
-     * affordance in the fragment title. When set to non-null, the title will
-     * contain an element that a user may click to begin a search.
-     *
-     * <p>The listener's {@link View.OnClickListener#onClick onClick} method
-     * will be invoked when the user clicks on the search element.
-     *
-     * @param listener The listener to call when the search element is clicked.
-     */
-    public void setOnSearchClickedListener(View.OnClickListener listener) {
-        mExternalOnSearchClickedListener = listener;
-        if (mTitleView != null) {
-            mTitleView.setOnSearchClickedListener(listener);
-        }
-    }
-
-    /**
-     * Sets the {@link SearchOrbView.Colors} used to draw the search affordance.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-        mSearchAffordanceColors = colors;
-        mSearchAffordanceColorSet = true;
-        if (mTitleView != null) {
-            mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
-        }
-    }
-
-    /**
-     * Returns the {@link SearchOrbView.Colors} used to draw the search affordance.
-     */
-    public SearchOrbView.Colors getSearchAffordanceColors() {
-        if (mSearchAffordanceColorSet) {
-            return mSearchAffordanceColors;
-        }
-        if (mTitleView == null) {
-            throw new IllegalStateException("Fragment views not yet created");
-        }
-        return mTitleView.getSearchAffordanceColors();
-    }
-
-    /**
-     * Sets the color used to draw the search affordance.
-     * A default brighter color will be set by the framework.
-     *
-     * @param color The color to use for the search affordance.
-     */
-    public void setSearchAffordanceColor(int color) {
-        setSearchAffordanceColors(new SearchOrbView.Colors(color));
-    }
-
-    /**
-     * Returns the color used to draw the search affordance.
-     */
-    public int getSearchAffordanceColor() {
-        return getSearchAffordanceColors().color;
-    }
-
-    /**
      * Start a headers transition.
      *
      * <p>This method will begin a transition to either show or hide the
@@ -519,6 +447,7 @@
                 != HorizontalGridView.SCROLL_STATE_IDLE;
     }
 
+
     private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
             new BrowseFrameLayout.OnFocusSearchListener() {
         @Override
@@ -529,30 +458,27 @@
             }
             if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
 
-            final View searchOrbView = mTitleView.getSearchAffordanceView();
-            if (focused == searchOrbView && direction == View.FOCUS_DOWN) {
+            if (getTitleView() != null && focused != getTitleView() &&
+                    direction == View.FOCUS_UP) {
+                return getTitleView();
+            }
+            if (getTitleView() != null && getTitleView().hasFocus() &&
+                    direction == View.FOCUS_DOWN) {
                 return mCanShowHeaders && mShowingHeaders ?
                         mHeadersFragment.getVerticalGridView() :
                         mRowsFragment.getVerticalGridView();
-            } else if (focused != searchOrbView && searchOrbView.getVisibility() == View.VISIBLE
-                    && direction == View.FOCUS_UP) {
-                return searchOrbView;
             }
 
-            // If headers fragment is disabled, just return null.
-            if (!mCanShowHeaders) {
-                return null;
-            }
             boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;
             int towardStart = isRtl ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
             int towardEnd = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
-            if (direction == towardStart) {
+            if (mCanShowHeaders && direction == towardStart) {
                 if (isVerticalScrolling() || mShowingHeaders) {
                     return focused;
                 }
                 return mHeadersFragment.getVerticalGridView();
             } else if (direction == towardEnd) {
-                if (isVerticalScrolling() || !mShowingHeaders) {
+                if (isVerticalScrolling()) {
                     return focused;
                 }
                 return mRowsFragment.getVerticalGridView();
@@ -581,8 +507,8 @@
                     mRowsFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
                 return true;
             }
-            if (mTitleView != null &&
-                    mTitleView.requestFocus(direction, previouslyFocusedRect)) {
+            if (getTitleView() != null &&
+                    getTitleView().requestFocus(direction, previouslyFocusedRect)) {
                 return true;
             }
             return false;
@@ -605,12 +531,12 @@
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
         if (mBackStackChangedListener != null) {
             mBackStackChangedListener.save(outState);
         } else {
             outState.putBoolean(HEADER_SHOW, mShowingHeaders);
         }
-        outState.putBoolean(TITLE_SHOW, mShowingTitle);
     }
 
     @Override
@@ -618,9 +544,11 @@
         super.onCreate(savedInstanceState);
         TypedArray ta = getActivity().obtainStyledAttributes(R.styleable.LeanbackTheme);
         mContainerListMarginStart = (int) ta.getDimension(
-                R.styleable.LeanbackTheme_browseRowsMarginStart, 0);
+                R.styleable.LeanbackTheme_browseRowsMarginStart, getActivity().getResources()
+                .getDimensionPixelSize(R.dimen.lb_browse_rows_margin_start));
         mContainerListAlignTop = (int) ta.getDimension(
-                R.styleable.LeanbackTheme_browseRowsMarginTop, 0);
+                R.styleable.LeanbackTheme_browseRowsMarginTop, getActivity().getResources()
+                .getDimensionPixelSize(R.dimen.lb_browse_rows_margin_top));
         ta.recycle();
 
         readArguments(getArguments());
@@ -637,7 +565,6 @@
                 }
             }
         }
-
     }
 
     @Override
@@ -673,45 +600,23 @@
         mHeadersFragment.setAdapter(mAdapter);
 
         mRowsFragment.enableRowScaling(mRowScaleEnabled);
-        mRowsFragment.setOnItemSelectedListener(mRowSelectedListener);
         mRowsFragment.setOnItemViewSelectedListener(mRowViewSelectedListener);
-        mHeadersFragment.setOnItemSelectedListener(mHeaderSelectedListener);
+        mHeadersFragment.setOnHeaderViewSelectedListener(mHeaderViewSelectedListener);
         mHeadersFragment.setOnHeaderClickedListener(mHeaderClickedListener);
-        mRowsFragment.setOnItemClickedListener(mOnItemClickedListener);
         mRowsFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
 
         View root = inflater.inflate(R.layout.lb_browse_fragment, container, false);
 
-        mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
-        mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
-        mBrowseFrame.setOnChildFocusListener(mOnChildFocusListener);
+        setTitleView((TitleView) root.findViewById(R.id.browse_title_group));
 
-        mTitleView = (TitleView) root.findViewById(R.id.browse_title_group);
-        mTitleView.setTitle(mTitle);
-        mTitleView.setBadgeDrawable(mBadgeDrawable);
-        if (mSearchAffordanceColorSet) {
-            mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
-        }
-        if (mExternalOnSearchClickedListener != null) {
-            mTitleView.setOnSearchClickedListener(mExternalOnSearchClickedListener);
-        }
+        mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
+        mBrowseFrame.setOnChildFocusListener(mOnChildFocusListener);
+        mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
 
         if (mBrandColorSet) {
             mHeadersFragment.setBackgroundColor(mBrandColor);
         }
 
-        mSceneWithTitle = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
-            @Override
-            public void run() {
-                mTitleView.setVisibility(View.VISIBLE);
-            }
-        });
-        mSceneWithoutTitle = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
-            @Override
-            public void run() {
-                mTitleView.setVisibility(View.INVISIBLE);
-            }
-        });
         mSceneWithHeaders = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
@@ -730,17 +635,6 @@
                 setEntranceTransitionEndState();
             }
         });
-        Context context = getActivity();
-        mTitleUpTransition = LeanbackTransitionHelper.loadTitleOutTransition(context,
-                sTransitionHelper);
-        mTitleDownTransition = LeanbackTransitionHelper.loadTitleInTransition(context,
-                sTransitionHelper);
-
-        if (savedInstanceState != null) {
-            mShowingTitle = savedInstanceState.getBoolean(TITLE_SHOW);
-        }
-        mTitleView.setVisibility(mShowingTitle ? View.VISIBLE: View.INVISIBLE);
-
         return root;
     }
 
@@ -841,18 +735,10 @@
         }
     };
 
-    private OnItemSelectedListener mRowSelectedListener = new OnItemSelectedListener() {
+    private HeadersFragment.OnHeaderViewSelectedListener mHeaderViewSelectedListener =
+            new HeadersFragment.OnHeaderViewSelectedListener() {
         @Override
-        public void onItemSelected(Object item, Row row) {
-            if (mExternalOnItemSelectedListener != null) {
-                mExternalOnItemSelectedListener.onItemSelected(item, row);
-            }
-        }
-    };
-
-    private OnItemSelectedListener mHeaderSelectedListener = new OnItemSelectedListener() {
-        @Override
-        public void onItemSelected(Object item, Row row) {
+        public void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row) {
             int position = mHeadersFragment.getVerticalGridView().getSelectedPosition();
             if (DEBUG) Log.v(TAG, "header selected position " + position);
             onRowSelected(position);
@@ -861,32 +747,17 @@
 
     private void onRowSelected(int position) {
         if (position != mSelectedPosition) {
-            mSetSelectionRunnable.mPosition = position;
-            mBrowseFrame.getHandler().post(mSetSelectionRunnable);
+            mSetSelectionRunnable.post(
+                    position, SetSelectionRunnable.TYPE_INTERNAL_SYNC, true);
 
             if (getAdapter() == null || getAdapter().size() == 0 || position == 0) {
-                if (!mShowingTitle) {
-                    sTransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition);
-                    mShowingTitle = true;
-                }
-            } else if (mShowingTitle) {
-                sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition);
-                mShowingTitle = false;
+                showTitle(true);
+            } else {
+                showTitle(false);
             }
         }
     }
 
-    private class SetSelectionRunnable implements Runnable {
-        int mPosition;
-        boolean mSmooth = true;
-        @Override
-        public void run() {
-            setSelection(mPosition, mSmooth);
-        }
-    }
-
-    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
-
     private void setSelection(int position, boolean smooth) {
         if (position != NO_POSITION) {
             mRowsFragment.setSelectedPosition(position, smooth);
@@ -906,9 +777,8 @@
      * Sets the selected row position.
      */
     public void setSelectedPosition(int position, boolean smooth) {
-        mSetSelectionRunnable.mPosition = position;
-        mSetSelectionRunnable.mSmooth = smooth;
-        mBrowseFrame.getHandler().post(mSetSelectionRunnable);
+        mSetSelectionRunnable.post(
+                position, SetSelectionRunnable.TYPE_USER_REQUEST, smooth);
     }
 
     @Override
@@ -935,18 +805,6 @@
         }
     }
 
-    @Override
-    public void onPause() {
-        mTitleView.enableAnimation(false);
-        super.onPause();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mTitleView.enableAnimation(true);
-    }
-
     /**
      * Enable/disable headers transition on back key support. This is enabled by
      * default. The BrowseFragment will add a back stack entry when headers are
@@ -981,45 +839,7 @@
         }
     }
 
-    /**
-     * Sets the drawable displayed in the browse fragment title.
-     *
-     * @param drawable The Drawable to display in the browse fragment title.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-        if (mBadgeDrawable != drawable) {
-            mBadgeDrawable = drawable;
-            if (mTitleView != null) {
-                mTitleView.setBadgeDrawable(drawable);
-            }
-        }
-    }
 
-    /**
-     * Returns the badge drawable used in the fragment title.
-     */
-    public Drawable getBadgeDrawable() {
-        return mBadgeDrawable;
-    }
-
-    /**
-     * Sets a title for the browse fragment.
-     *
-     * @param title The title of the browse fragment.
-     */
-    public void setTitle(String title) {
-        mTitle = title;
-        if (mTitleView != null) {
-            mTitleView.setTitle(title);
-        }
-    }
-
-    /**
-     * Returns the title for the browse fragment.
-     */
-    public String getTitle() {
-        return mTitle;
-    }
 
     /**
      * Sets the state for the headers column in the browse fragment. Must be one
@@ -1091,7 +911,7 @@
     }
 
     void setSearchOrbViewOnScreen(boolean onScreen) {
-        View searchOrbView = mTitleView.getSearchAffordanceView();
+        View searchOrbView = getTitleView().getSearchAffordanceView();
         MarginLayoutParams lp = (MarginLayoutParams) searchOrbView.getLayoutParams();
         lp.setMarginStart(onScreen ? 0 : -mContainerListMarginStart);
         searchOrbView.setLayoutParams(lp);
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
index 68b7af3..1448d72 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
@@ -15,6 +15,7 @@
  */
 package android.support.v17.leanback.app;
 
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.transition.LeanbackTransitionHelper;
 import android.support.v17.leanback.transition.TransitionHelper;
@@ -26,13 +27,12 @@
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v17.leanback.widget.RowHeaderPresenter;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.TitleView;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
-import android.support.v17.leanback.widget.OnItemClickedListener;
 import android.support.v17.leanback.widget.SearchOrbView;
 import android.support.v4.view.ViewCompat;
 import android.util.Log;
@@ -52,7 +52,6 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-
 import static android.support.v7.widget.RecyclerView.NO_POSITION;
 
 /**
@@ -79,8 +78,7 @@
     static final String HEADER_STACK_INDEX = "headerStackIndex";
     // BUNDLE attribute for saving header show/hide status when backstack is not used:
     static final String HEADER_SHOW = "headerShow";
-    // BUNDLE attribute for title is showing
-    static final String TITLE_SHOW = "titleShow";
+
 
     final class BackStackListener implements FragmentManager.OnBackStackChangedListener {
         int mLastEntryCount;
@@ -157,6 +155,47 @@
         }
     }
 
+    private class SetSelectionRunnable implements Runnable {
+        static final int TYPE_INVALID = -1;
+        static final int TYPE_INTERNAL_SYNC = 0;
+        static final int TYPE_USER_REQUEST = 1;
+
+        private int mPosition;
+        private int mType;
+        private boolean mSmooth;
+
+        SetSelectionRunnable() {
+            reset();
+        }
+
+        void post(int position, int type, boolean smooth) {
+            // Posting the set selection, rather than calling it immediately, prevents an issue
+            // with adapter changes.  Example: a row is added before the current selected row;
+            // first the fast lane view updates its selection, then the rows fragment has that
+            // new selection propagated immediately; THEN the rows view processes the same adapter
+            // change and moves the selection again.
+            if (type >= mType) {
+                mPosition = position;
+                mType = type;
+                mSmooth = smooth;
+                mBrowseFrame.removeCallbacks(this);
+                mBrowseFrame.post(this);
+            }
+        }
+
+        @Override
+        public void run() {
+            setSelection(mPosition, mSmooth);
+            reset();
+        }
+
+        private void reset() {
+            mPosition = -1;
+            mType = TYPE_INVALID;
+            mSmooth = false;
+        }
+    }
+
     private static final String TAG = "BrowseSupportFragment";
 
     private static final String LB_HEADERS_BACKSTACK = "lbHeadersBackStack_";
@@ -172,22 +211,16 @@
     /** The headers fragment is disabled and will never be shown. */
     public static final int HEADERS_DISABLED = 3;
 
-    private static final float SLIDE_DISTANCE_FACTOR = 2;
-
     private RowsSupportFragment mRowsSupportFragment;
     private HeadersSupportFragment mHeadersSupportFragment;
 
     private ObjectAdapter mAdapter;
 
-    private String mTitle;
-    private Drawable mBadgeDrawable;
     private int mHeadersState = HEADERS_ENABLED;
     private int mBrandColor = Color.TRANSPARENT;
     private boolean mBrandColorSet;
 
     private BrowseFrameLayout mBrowseFrame;
-    private TitleView mTitleView;
-    private boolean mShowingTitle = true;
     private boolean mHeadersBackStackEnabled = true;
     private String mWithHeadersBackStackName;
     private boolean mShowingHeaders = true;
@@ -195,25 +228,17 @@
     private int mContainerListMarginStart;
     private int mContainerListAlignTop;
     private boolean mRowScaleEnabled = true;
-    private SearchOrbView.Colors mSearchAffordanceColors;
-    private boolean mSearchAffordanceColorSet;
-    private OnItemSelectedListener mExternalOnItemSelectedListener;
-    private OnClickListener mExternalOnSearchClickedListener;
-    private OnItemClickedListener mOnItemClickedListener;
     private OnItemViewSelectedListener mExternalOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
     private int mSelectedPosition = -1;
 
     private PresenterSelector mHeaderPresenterSelector;
+    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
 
     // transition related:
-    private Object mSceneWithTitle;
-    private Object mSceneWithoutTitle;
     private Object mSceneWithHeaders;
     private Object mSceneWithoutHeaders;
     private Object mSceneAfterEntranceTransition;
-    private Object mTitleUpTransition;
-    private Object mTitleDownTransition;
     private Object mHeadersTransition;
     private BackStackListener mBackStackChangedListener;
     private BrowseTransitionListener mBrowseTransitionListener;
@@ -250,7 +275,7 @@
      *
      * @param color The color to use as the brand color of the fragment.
      */
-    public void setBrandColor(int color) {
+    public void setBrandColor(@ColorInt int color) {
         mBrandColor = color;
         mBrandColorSet = true;
 
@@ -263,6 +288,7 @@
      * Returns the brand color for the browse fragment.
      * The default is transparent.
      */
+    @ColorInt
     public int getBrandColor() {
         return mBrandColor;
     }
@@ -293,17 +319,6 @@
     }
 
     /**
-     * Sets an item selection listener. This listener will be called when an
-     * item or row is selected by a user.
-     *
-     * @param listener The listener to call when an item or row is selected.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mExternalOnItemSelectedListener = listener;
-    }
-
-    /**
      * Sets an item selection listener.
      */
     public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
@@ -319,33 +334,6 @@
 
     /**
      * Sets an item clicked listener on the fragment.
-     *
-     * <p>OnItemClickedListener will override {@link View.OnClickListener} that
-     * an item presenter may set during
-     * {@link Presenter#onCreateViewHolder(ViewGroup)}. So in general, you
-     * should choose to use an {@link OnItemClickedListener} or a
-     * {@link View.OnClickListener} on your item views, but not both.
-     *
-     * @param listener The listener to call when an item is clicked.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-        if (mRowsSupportFragment != null) {
-            mRowsSupportFragment.setOnItemClickedListener(listener);
-        }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     * @deprecated Use {@link #getOnItemViewClickedListener()}
-     */
-    public OnItemClickedListener getOnItemClickedListener() {
-        return mOnItemClickedListener;
-    }
-
-    /**
-     * Sets an item clicked listener on the fragment.
      * OnItemViewClickedListener will override {@link View.OnClickListener} that
      * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
      * So in general,  developer should choose one of the listeners but not both.
@@ -365,66 +353,6 @@
     }
 
     /**
-     * Sets a click listener for the search affordance.
-     *
-     * <p>The presence of a listener will change the visibility of the search
-     * affordance in the fragment title. When set to non-null, the title will
-     * contain an element that a user may click to begin a search.
-     *
-     * <p>The listener's {@link View.OnClickListener#onClick onClick} method
-     * will be invoked when the user clicks on the search element.
-     *
-     * @param listener The listener to call when the search element is clicked.
-     */
-    public void setOnSearchClickedListener(View.OnClickListener listener) {
-        mExternalOnSearchClickedListener = listener;
-        if (mTitleView != null) {
-            mTitleView.setOnSearchClickedListener(listener);
-        }
-    }
-
-    /**
-     * Sets the {@link SearchOrbView.Colors} used to draw the search affordance.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-        mSearchAffordanceColors = colors;
-        mSearchAffordanceColorSet = true;
-        if (mTitleView != null) {
-            mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
-        }
-    }
-
-    /**
-     * Returns the {@link SearchOrbView.Colors} used to draw the search affordance.
-     */
-    public SearchOrbView.Colors getSearchAffordanceColors() {
-        if (mSearchAffordanceColorSet) {
-            return mSearchAffordanceColors;
-        }
-        if (mTitleView == null) {
-            throw new IllegalStateException("Fragment views not yet created");
-        }
-        return mTitleView.getSearchAffordanceColors();
-    }
-
-    /**
-     * Sets the color used to draw the search affordance.
-     * A default brighter color will be set by the framework.
-     *
-     * @param color The color to use for the search affordance.
-     */
-    public void setSearchAffordanceColor(int color) {
-        setSearchAffordanceColors(new SearchOrbView.Colors(color));
-    }
-
-    /**
-     * Returns the color used to draw the search affordance.
-     */
-    public int getSearchAffordanceColor() {
-        return getSearchAffordanceColors().color;
-    }
-
-    /**
      * Start a headers transition.
      *
      * <p>This method will begin a transition to either show or hide the
@@ -521,6 +449,7 @@
                 != HorizontalGridView.SCROLL_STATE_IDLE;
     }
 
+
     private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
             new BrowseFrameLayout.OnFocusSearchListener() {
         @Override
@@ -531,30 +460,27 @@
             }
             if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
 
-            final View searchOrbView = mTitleView.getSearchAffordanceView();
-            if (focused == searchOrbView && direction == View.FOCUS_DOWN) {
+            if (getTitleView() != null && focused != getTitleView() &&
+                    direction == View.FOCUS_UP) {
+                return getTitleView();
+            }
+            if (getTitleView() != null && getTitleView().hasFocus() &&
+                    direction == View.FOCUS_DOWN) {
                 return mCanShowHeaders && mShowingHeaders ?
                         mHeadersSupportFragment.getVerticalGridView() :
                         mRowsSupportFragment.getVerticalGridView();
-            } else if (focused != searchOrbView && searchOrbView.getVisibility() == View.VISIBLE
-                    && direction == View.FOCUS_UP) {
-                return searchOrbView;
             }
 
-            // If headers fragment is disabled, just return null.
-            if (!mCanShowHeaders) {
-                return null;
-            }
             boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;
             int towardStart = isRtl ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
             int towardEnd = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
-            if (direction == towardStart) {
+            if (mCanShowHeaders && direction == towardStart) {
                 if (isVerticalScrolling() || mShowingHeaders) {
                     return focused;
                 }
                 return mHeadersSupportFragment.getVerticalGridView();
             } else if (direction == towardEnd) {
-                if (isVerticalScrolling() || !mShowingHeaders) {
+                if (isVerticalScrolling()) {
                     return focused;
                 }
                 return mRowsSupportFragment.getVerticalGridView();
@@ -583,8 +509,8 @@
                     mRowsSupportFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
                 return true;
             }
-            if (mTitleView != null &&
-                    mTitleView.requestFocus(direction, previouslyFocusedRect)) {
+            if (getTitleView() != null &&
+                    getTitleView().requestFocus(direction, previouslyFocusedRect)) {
                 return true;
             }
             return false;
@@ -607,12 +533,12 @@
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
         if (mBackStackChangedListener != null) {
             mBackStackChangedListener.save(outState);
         } else {
             outState.putBoolean(HEADER_SHOW, mShowingHeaders);
         }
-        outState.putBoolean(TITLE_SHOW, mShowingTitle);
     }
 
     @Override
@@ -620,9 +546,11 @@
         super.onCreate(savedInstanceState);
         TypedArray ta = getActivity().obtainStyledAttributes(R.styleable.LeanbackTheme);
         mContainerListMarginStart = (int) ta.getDimension(
-                R.styleable.LeanbackTheme_browseRowsMarginStart, 0);
+                R.styleable.LeanbackTheme_browseRowsMarginStart, getActivity().getResources()
+                .getDimensionPixelSize(R.dimen.lb_browse_rows_margin_start));
         mContainerListAlignTop = (int) ta.getDimension(
-                R.styleable.LeanbackTheme_browseRowsMarginTop, 0);
+                R.styleable.LeanbackTheme_browseRowsMarginTop, getActivity().getResources()
+                .getDimensionPixelSize(R.dimen.lb_browse_rows_margin_top));
         ta.recycle();
 
         readArguments(getArguments());
@@ -639,7 +567,6 @@
                 }
             }
         }
-
     }
 
     @Override
@@ -675,45 +602,23 @@
         mHeadersSupportFragment.setAdapter(mAdapter);
 
         mRowsSupportFragment.enableRowScaling(mRowScaleEnabled);
-        mRowsSupportFragment.setOnItemSelectedListener(mRowSelectedListener);
         mRowsSupportFragment.setOnItemViewSelectedListener(mRowViewSelectedListener);
-        mHeadersSupportFragment.setOnItemSelectedListener(mHeaderSelectedListener);
+        mHeadersSupportFragment.setOnHeaderViewSelectedListener(mHeaderViewSelectedListener);
         mHeadersSupportFragment.setOnHeaderClickedListener(mHeaderClickedListener);
-        mRowsSupportFragment.setOnItemClickedListener(mOnItemClickedListener);
         mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
 
         View root = inflater.inflate(R.layout.lb_browse_fragment, container, false);
 
-        mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
-        mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
-        mBrowseFrame.setOnChildFocusListener(mOnChildFocusListener);
+        setTitleView((TitleView) root.findViewById(R.id.browse_title_group));
 
-        mTitleView = (TitleView) root.findViewById(R.id.browse_title_group);
-        mTitleView.setTitle(mTitle);
-        mTitleView.setBadgeDrawable(mBadgeDrawable);
-        if (mSearchAffordanceColorSet) {
-            mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
-        }
-        if (mExternalOnSearchClickedListener != null) {
-            mTitleView.setOnSearchClickedListener(mExternalOnSearchClickedListener);
-        }
+        mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
+        mBrowseFrame.setOnChildFocusListener(mOnChildFocusListener);
+        mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
 
         if (mBrandColorSet) {
             mHeadersSupportFragment.setBackgroundColor(mBrandColor);
         }
 
-        mSceneWithTitle = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
-            @Override
-            public void run() {
-                mTitleView.setVisibility(View.VISIBLE);
-            }
-        });
-        mSceneWithoutTitle = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
-            @Override
-            public void run() {
-                mTitleView.setVisibility(View.INVISIBLE);
-            }
-        });
         mSceneWithHeaders = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
@@ -732,17 +637,6 @@
                 setEntranceTransitionEndState();
             }
         });
-        Context context = getActivity();
-        mTitleUpTransition = LeanbackTransitionHelper.loadTitleOutTransition(context,
-                sTransitionHelper);
-        mTitleDownTransition = LeanbackTransitionHelper.loadTitleInTransition(context,
-                sTransitionHelper);
-
-        if (savedInstanceState != null) {
-            mShowingTitle = savedInstanceState.getBoolean(TITLE_SHOW);
-        }
-        mTitleView.setVisibility(mShowingTitle ? View.VISIBLE: View.INVISIBLE);
-
         return root;
     }
 
@@ -843,18 +737,10 @@
         }
     };
 
-    private OnItemSelectedListener mRowSelectedListener = new OnItemSelectedListener() {
+    private HeadersSupportFragment.OnHeaderViewSelectedListener mHeaderViewSelectedListener =
+            new HeadersSupportFragment.OnHeaderViewSelectedListener() {
         @Override
-        public void onItemSelected(Object item, Row row) {
-            if (mExternalOnItemSelectedListener != null) {
-                mExternalOnItemSelectedListener.onItemSelected(item, row);
-            }
-        }
-    };
-
-    private OnItemSelectedListener mHeaderSelectedListener = new OnItemSelectedListener() {
-        @Override
-        public void onItemSelected(Object item, Row row) {
+        public void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row) {
             int position = mHeadersSupportFragment.getVerticalGridView().getSelectedPosition();
             if (DEBUG) Log.v(TAG, "header selected position " + position);
             onRowSelected(position);
@@ -863,32 +749,17 @@
 
     private void onRowSelected(int position) {
         if (position != mSelectedPosition) {
-            mSetSelectionRunnable.mPosition = position;
-            mBrowseFrame.getHandler().post(mSetSelectionRunnable);
+            mSetSelectionRunnable.post(
+                    position, SetSelectionRunnable.TYPE_INTERNAL_SYNC, true);
 
             if (getAdapter() == null || getAdapter().size() == 0 || position == 0) {
-                if (!mShowingTitle) {
-                    sTransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition);
-                    mShowingTitle = true;
-                }
-            } else if (mShowingTitle) {
-                sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition);
-                mShowingTitle = false;
+                showTitle(true);
+            } else {
+                showTitle(false);
             }
         }
     }
 
-    private class SetSelectionRunnable implements Runnable {
-        int mPosition;
-        boolean mSmooth = true;
-        @Override
-        public void run() {
-            setSelection(mPosition, mSmooth);
-        }
-    }
-
-    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
-
     private void setSelection(int position, boolean smooth) {
         if (position != NO_POSITION) {
             mRowsSupportFragment.setSelectedPosition(position, smooth);
@@ -908,9 +779,8 @@
      * Sets the selected row position.
      */
     public void setSelectedPosition(int position, boolean smooth) {
-        mSetSelectionRunnable.mPosition = position;
-        mSetSelectionRunnable.mSmooth = smooth;
-        mBrowseFrame.getHandler().post(mSetSelectionRunnable);
+        mSetSelectionRunnable.post(
+                position, SetSelectionRunnable.TYPE_USER_REQUEST, smooth);
     }
 
     @Override
@@ -937,18 +807,6 @@
         }
     }
 
-    @Override
-    public void onPause() {
-        mTitleView.enableAnimation(false);
-        super.onPause();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mTitleView.enableAnimation(true);
-    }
-
     /**
      * Enable/disable headers transition on back key support. This is enabled by
      * default. The BrowseSupportFragment will add a back stack entry when headers are
@@ -983,45 +841,7 @@
         }
     }
 
-    /**
-     * Sets the drawable displayed in the browse fragment title.
-     *
-     * @param drawable The Drawable to display in the browse fragment title.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-        if (mBadgeDrawable != drawable) {
-            mBadgeDrawable = drawable;
-            if (mTitleView != null) {
-                mTitleView.setBadgeDrawable(drawable);
-            }
-        }
-    }
 
-    /**
-     * Returns the badge drawable used in the fragment title.
-     */
-    public Drawable getBadgeDrawable() {
-        return mBadgeDrawable;
-    }
-
-    /**
-     * Sets a title for the browse fragment.
-     *
-     * @param title The title of the browse fragment.
-     */
-    public void setTitle(String title) {
-        mTitle = title;
-        if (mTitleView != null) {
-            mTitleView.setTitle(title);
-        }
-    }
-
-    /**
-     * Returns the title for the browse fragment.
-     */
-    public String getTitle() {
-        return mTitle;
-    }
 
     /**
      * Sets the state for the headers column in the browse fragment. Must be one
@@ -1093,7 +913,7 @@
     }
 
     void setSearchOrbViewOnScreen(boolean onScreen) {
-        View searchOrbView = mTitleView.getSearchAffordanceView();
+        View searchOrbView = getTitleView().getSearchAffordanceView();
         MarginLayoutParams lp = (MarginLayoutParams) searchOrbView.getLayoutParams();
         lp.setMarginStart(onScreen ? 0 : -mContainerListMarginStart);
         searchOrbView.setLayoutParams(lp);
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
index 0c01c0d..8e3df94 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
@@ -14,17 +14,18 @@
 package android.support.v17.leanback.app;
 
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.widget.BrowseFrameLayout;
 import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnItemClickedListener;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.TitleHelper;
+import android.support.v17.leanback.widget.TitleView;
 import android.support.v17.leanback.widget.VerticalGridView;
-import android.app.Fragment;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -39,6 +40,7 @@
     private class SetSelectionRunnable implements Runnable {
         int mPosition;
         boolean mSmooth = true;
+
         @Override
         public void run() {
             mRowsFragment.setSelectedPosition(mPosition, mSmooth);
@@ -49,16 +51,28 @@
 
     private ObjectAdapter mAdapter;
     private int mContainerListAlignTop;
-    private OnItemSelectedListener mExternalOnItemSelectedListener;
-    private OnItemClickedListener mOnItemClickedListener;
     private OnItemViewSelectedListener mExternalOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
-    private int mSelectedPosition = -1;
 
     private Object mSceneAfterEntranceTransition;
 
     private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
 
+    private final OnItemViewSelectedListener mOnItemViewSelectedListener =
+            new OnItemViewSelectedListener() {
+        @Override
+        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
+                                   RowPresenter.ViewHolder rowViewHolder, Row row) {
+            int position = mRowsFragment.getVerticalGridView().getSelectedPosition();
+            if (DEBUG) Log.v(TAG, "row selected position " + position);
+            onRowSelected(position);
+            if (mExternalOnItemViewSelectedListener != null) {
+                mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
+                        rowViewHolder, row);
+            }
+        }
+    };
+
     /**
      * Sets the list of rows for the fragment.
      */
@@ -78,25 +92,6 @@
 
     /**
      * Sets an item selection listener.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mExternalOnItemSelectedListener = listener;
-    }
-
-    /**
-     * Sets an item Clicked listener.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-        if (mRowsFragment != null) {
-            mRowsFragment.setOnItemClickedListener(listener);
-        }
-    }
-
-    /**
-     * Sets an item selection listener.
      */
     public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
         mExternalOnItemViewSelectedListener = listener;
@@ -106,22 +101,16 @@
      * Sets an item Clicked listener.
      */
     public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        mOnItemViewClickedListener = listener;
-        if (mRowsFragment != null) {
-            mRowsFragment.setOnItemViewClickedListener(listener);
+        if (mOnItemViewClickedListener != listener) {
+            mOnItemViewClickedListener = listener;
+            if (mRowsFragment != null) {
+                mRowsFragment.setOnItemViewClickedListener(listener);
+            }
         }
     }
 
     /**
      * Returns the item Clicked listener.
-     * @deprecated Use {@link #getOnItemViewClickedListener()}
-     */
-    public OnItemClickedListener getOnItemClickedListener() {
-        return mOnItemClickedListener;
-    }
-
-    /**
-     * Returns the item Clicked listener.
      */
     public OnItemViewClickedListener getOnItemViewClickedListener() {
         return mOnItemViewClickedListener;
@@ -140,19 +129,20 @@
             Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.lb_details_fragment, container, false);
         mRowsFragment = (RowsFragment) getChildFragmentManager().findFragmentById(
-                R.id.fragment_dock); 
+                R.id.details_rows_dock);
         if (mRowsFragment == null) {
             mRowsFragment = new RowsFragment();
             getChildFragmentManager().beginTransaction()
-                    .replace(R.id.fragment_dock, mRowsFragment).commit();
+                    .replace(R.id.details_rows_dock, mRowsFragment).commit();
         }
         mRowsFragment.setAdapter(mAdapter);
-        mRowsFragment.setOnItemSelectedListener(mExternalOnItemSelectedListener);
-        mRowsFragment.setOnItemViewSelectedListener(mExternalOnItemViewSelectedListener);
-        mRowsFragment.setOnItemClickedListener(mOnItemClickedListener);
+        mRowsFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
         mRowsFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        mSceneAfterEntranceTransition = sTransitionHelper.createScene((ViewGroup) view,
-                new Runnable() {
+
+        setTitleView((TitleView) view.findViewById(R.id.browse_title_group));
+
+        mSceneAfterEntranceTransition = sTransitionHelper.createScene(
+                (ViewGroup) view, new Runnable() {
             @Override
             public void run() {
                 mRowsFragment.setEntranceTransitionState(true);
@@ -186,6 +176,12 @@
         setVerticalGridViewLayout(mRowsFragment.getVerticalGridView());
     }
 
+    private void setupFocusSearchListener() {
+        BrowseFrameLayout browseFrameLayout = (BrowseFrameLayout) getView().findViewById(
+                R.id.details_frame);
+        browseFrameLayout.setOnFocusSearchListener(getTitleHelper().getOnFocusSearchListener());
+    }
+
     /**
      * Sets the selected row position with smooth animation.
      */
@@ -204,10 +200,19 @@
         }
     }
 
+    private void onRowSelected(int position) {
+        if (getAdapter() == null || getAdapter().size() == 0 || position == 0) {
+            showTitle(true);
+        } else {
+            showTitle(false);
+        }
+    }
+
     @Override
     public void onStart() {
         super.onStart();
         setupChildFragmentLayout();
+        setupFocusSearchListener();
         mRowsFragment.getView().requestFocus();
         if (isEntranceTransitionEnabled()) {
             // make sure recycler view animation is disabled
@@ -232,5 +237,4 @@
     protected void onEntranceTransitionEnd() {
         mRowsFragment.onTransitionEnd();
     }
-
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
index d8ae895..d879125 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
@@ -16,17 +16,18 @@
 package android.support.v17.leanback.app;
 
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.widget.BrowseFrameLayout;
 import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnItemClickedListener;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.TitleHelper;
+import android.support.v17.leanback.widget.TitleView;
 import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v4.app.Fragment;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -41,6 +42,7 @@
     private class SetSelectionRunnable implements Runnable {
         int mPosition;
         boolean mSmooth = true;
+
         @Override
         public void run() {
             mRowsSupportFragment.setSelectedPosition(mPosition, mSmooth);
@@ -51,16 +53,28 @@
 
     private ObjectAdapter mAdapter;
     private int mContainerListAlignTop;
-    private OnItemSelectedListener mExternalOnItemSelectedListener;
-    private OnItemClickedListener mOnItemClickedListener;
     private OnItemViewSelectedListener mExternalOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
-    private int mSelectedPosition = -1;
 
     private Object mSceneAfterEntranceTransition;
 
     private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
 
+    private final OnItemViewSelectedListener mOnItemViewSelectedListener =
+            new OnItemViewSelectedListener() {
+        @Override
+        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
+                                   RowPresenter.ViewHolder rowViewHolder, Row row) {
+            int position = mRowsSupportFragment.getVerticalGridView().getSelectedPosition();
+            if (DEBUG) Log.v(TAG, "row selected position " + position);
+            onRowSelected(position);
+            if (mExternalOnItemViewSelectedListener != null) {
+                mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
+                        rowViewHolder, row);
+            }
+        }
+    };
+
     /**
      * Sets the list of rows for the fragment.
      */
@@ -80,25 +94,6 @@
 
     /**
      * Sets an item selection listener.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mExternalOnItemSelectedListener = listener;
-    }
-
-    /**
-     * Sets an item Clicked listener.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-        if (mRowsSupportFragment != null) {
-            mRowsSupportFragment.setOnItemClickedListener(listener);
-        }
-    }
-
-    /**
-     * Sets an item selection listener.
      */
     public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
         mExternalOnItemViewSelectedListener = listener;
@@ -108,22 +103,16 @@
      * Sets an item Clicked listener.
      */
     public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        mOnItemViewClickedListener = listener;
-        if (mRowsSupportFragment != null) {
-            mRowsSupportFragment.setOnItemViewClickedListener(listener);
+        if (mOnItemViewClickedListener != listener) {
+            mOnItemViewClickedListener = listener;
+            if (mRowsSupportFragment != null) {
+                mRowsSupportFragment.setOnItemViewClickedListener(listener);
+            }
         }
     }
 
     /**
      * Returns the item Clicked listener.
-     * @deprecated Use {@link #getOnItemViewClickedListener()}
-     */
-    public OnItemClickedListener getOnItemClickedListener() {
-        return mOnItemClickedListener;
-    }
-
-    /**
-     * Returns the item Clicked listener.
      */
     public OnItemViewClickedListener getOnItemViewClickedListener() {
         return mOnItemViewClickedListener;
@@ -142,19 +131,20 @@
             Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.lb_details_fragment, container, false);
         mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager().findFragmentById(
-                R.id.fragment_dock); 
+                R.id.details_rows_dock);
         if (mRowsSupportFragment == null) {
             mRowsSupportFragment = new RowsSupportFragment();
             getChildFragmentManager().beginTransaction()
-                    .replace(R.id.fragment_dock, mRowsSupportFragment).commit();
+                    .replace(R.id.details_rows_dock, mRowsSupportFragment).commit();
         }
         mRowsSupportFragment.setAdapter(mAdapter);
-        mRowsSupportFragment.setOnItemSelectedListener(mExternalOnItemSelectedListener);
-        mRowsSupportFragment.setOnItemViewSelectedListener(mExternalOnItemViewSelectedListener);
-        mRowsSupportFragment.setOnItemClickedListener(mOnItemClickedListener);
+        mRowsSupportFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
         mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        mSceneAfterEntranceTransition = sTransitionHelper.createScene((ViewGroup) view,
-                new Runnable() {
+
+        setTitleView((TitleView) view.findViewById(R.id.browse_title_group));
+
+        mSceneAfterEntranceTransition = sTransitionHelper.createScene(
+                (ViewGroup) view, new Runnable() {
             @Override
             public void run() {
                 mRowsSupportFragment.setEntranceTransitionState(true);
@@ -188,6 +178,12 @@
         setVerticalGridViewLayout(mRowsSupportFragment.getVerticalGridView());
     }
 
+    private void setupFocusSearchListener() {
+        BrowseFrameLayout browseFrameLayout = (BrowseFrameLayout) getView().findViewById(
+                R.id.details_frame);
+        browseFrameLayout.setOnFocusSearchListener(getTitleHelper().getOnFocusSearchListener());
+    }
+
     /**
      * Sets the selected row position with smooth animation.
      */
@@ -206,10 +202,19 @@
         }
     }
 
+    private void onRowSelected(int position) {
+        if (getAdapter() == null || getAdapter().size() == 0 || position == 0) {
+            showTitle(true);
+        } else {
+            showTitle(false);
+        }
+    }
+
     @Override
     public void onStart() {
         super.onStart();
         setupChildFragmentLayout();
+        setupFocusSearchListener();
         mRowsSupportFragment.getView().requestFocus();
         if (isEntranceTransitionEnabled()) {
             // make sure recycler view animation is disabled
@@ -234,5 +239,4 @@
     protected void onEntranceTransitionEnd() {
         mRowsSupportFragment.onTransitionEnd();
     }
-
 }
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 a637553..67b77bf 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
@@ -23,7 +23,7 @@
 import android.support.v17.leanback.widget.FocusHighlightHelper;
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
+import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowHeaderPresenter;
 import android.support.v17.leanback.widget.SinglePresenterSelector;
@@ -43,7 +43,11 @@
         void onHeaderClicked();
     }
 
-    private OnItemSelectedListener mOnItemSelectedListener;
+    interface OnHeaderViewSelectedListener {
+        void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row);
+    }
+
+    private OnHeaderViewSelectedListener mOnHeaderViewSelectedListener;
     private OnHeaderClickedListener mOnHeaderClickedListener;
     private boolean mHeadersEnabled = true;
     private boolean mHeadersGone = false;
@@ -61,8 +65,8 @@
         mOnHeaderClickedListener = listener;
     }
 
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mOnItemSelectedListener = listener;
+    public void setOnHeaderViewSelectedListener(OnHeaderViewSelectedListener listener) {
+        mOnHeaderViewSelectedListener = listener;
     }
 
     @Override
@@ -72,12 +76,19 @@
 
     @Override
     void onRowSelected(ViewGroup parent, View view, int position, long id) {
-        if (mOnItemSelectedListener != null) {
-            if (position >= 0) {
+        VerticalGridView listView = getVerticalGridView();
+        if (listView == null) {
+            return;
+        }
+        if (mOnHeaderViewSelectedListener != null) {
+            ItemBridgeAdapter.ViewHolder vh = view == null ? null :
+                (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view);
+            if (vh != null && position >= 0) {
                 Row row = (Row) getAdapter().get(position);
-                mOnItemSelectedListener.onItemSelected(null, row);
+                mOnHeaderViewSelectedListener.onHeaderSelected(
+                        (RowHeaderPresenter.ViewHolder) vh.getViewHolder(), row);
             } else {
-                mOnItemSelectedListener.onItemSelected(null, null);
+                mOnHeaderViewSelectedListener.onHeaderSelected(null, null);
             }
         }
     }
@@ -230,8 +241,10 @@
         }
 
         TypedValue outValue = new TypedValue();
-        getActivity().getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true);
-        return getResources().getColor(outValue.resourceId);
+        if (getActivity().getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) {
+            return getResources().getColor(outValue.resourceId);
+        }
+        return getResources().getColor(R.color.lb_default_brand_color);
     }
 
     @Override
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 229e5ad..1fd6223 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
@@ -25,7 +25,7 @@
 import android.support.v17.leanback.widget.FocusHighlightHelper;
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
+import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowHeaderPresenter;
 import android.support.v17.leanback.widget.SinglePresenterSelector;
@@ -45,7 +45,11 @@
         void onHeaderClicked();
     }
 
-    private OnItemSelectedListener mOnItemSelectedListener;
+    interface OnHeaderViewSelectedListener {
+        void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row);
+    }
+
+    private OnHeaderViewSelectedListener mOnHeaderViewSelectedListener;
     private OnHeaderClickedListener mOnHeaderClickedListener;
     private boolean mHeadersEnabled = true;
     private boolean mHeadersGone = false;
@@ -63,8 +67,8 @@
         mOnHeaderClickedListener = listener;
     }
 
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mOnItemSelectedListener = listener;
+    public void setOnHeaderViewSelectedListener(OnHeaderViewSelectedListener listener) {
+        mOnHeaderViewSelectedListener = listener;
     }
 
     @Override
@@ -74,12 +78,19 @@
 
     @Override
     void onRowSelected(ViewGroup parent, View view, int position, long id) {
-        if (mOnItemSelectedListener != null) {
-            if (position >= 0) {
+        VerticalGridView listView = getVerticalGridView();
+        if (listView == null) {
+            return;
+        }
+        if (mOnHeaderViewSelectedListener != null) {
+            ItemBridgeAdapter.ViewHolder vh = view == null ? null :
+                (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view);
+            if (vh != null && position >= 0) {
                 Row row = (Row) getAdapter().get(position);
-                mOnItemSelectedListener.onItemSelected(null, row);
+                mOnHeaderViewSelectedListener.onHeaderSelected(
+                        (RowHeaderPresenter.ViewHolder) vh.getViewHolder(), row);
             } else {
-                mOnItemSelectedListener.onItemSelected(null, null);
+                mOnHeaderViewSelectedListener.onHeaderSelected(null, null);
             }
         }
     }
@@ -232,8 +243,10 @@
         }
 
         TypedValue outValue = new TypedValue();
-        getActivity().getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true);
-        return getResources().getColor(outValue.resourceId);
+        if (getActivity().getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) {
+            return getResources().getColor(outValue.resourceId);
+        }
+        return getResources().getColor(R.color.lb_default_brand_color);
     }
 
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
index 41be7fc..8c56925 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
@@ -7,6 +7,7 @@
 import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
+import android.support.v17.leanback.widget.OnActionClickedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.PlaybackControlsRow;
 import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
@@ -18,6 +19,7 @@
 import android.util.Log;
 import android.view.InputEvent;
 import android.view.KeyEvent;
+import android.view.View;
 
 
 /**
@@ -35,11 +37,14 @@
  * </p>
  *
  * <p>To use an instance of the glue layer, first construct an instance.  Constructor parameters
- * inform the glue what speed levels are supported for fast forward/rewind.  If you have your own
- * controls row you must pass it to {@link #setControlsRow}.  The row will be updated by the glue
- * layer based on the media metadata and playback state.  Alternatively, you may call
- * {@link #createControlsRowAndPresenter()} which will set a controls row and return
- * a row presenter you can use to present the row.
+ * inform the glue what speed levels are supported for fast forward/rewind.  Providing a
+ * {@link android.support.v17.leanback.app.PlaybackOverlayFragment} is optional.
+ * </p>
+ *
+ * <p>If you have your own controls row you must pass it to {@link #setControlsRow}.
+ * The row will be updated by the glue layer based on the media metadata and playback state.
+ * Alternatively, you may call {@link #createControlsRowAndPresenter()} which will set a controls
+ * row and return a row presenter you can use to present the row.
  * </p>
  *
  * <p>The helper sets a {@link android.support.v17.leanback.widget.SparseArrayObjectAdapter}
@@ -48,9 +53,17 @@
  * deal in secondary actions so those you may add separately.
  * </p>
  *
- * <p>The helper sets an {@link android.support.v17.leanback.widget.OnItemViewClickedListener}
- * on the fragment.  To receive callbacks on clicks for elements unknown to the helper, pass
- * a listener to {@link #setOnItemViewClickedListener}.
+ * <p>Provide a click listener on your fragment and if an action is clicked, call
+ * {@link #onActionClicked}.  There is no need to call {@link #setOnItemViewClickedListener}
+ * but if you do a click listener will be installed on the fragment and recognized action clicks
+ * will be handled.  Your listener will be called only for unhandled actions.
+ * </p>
+ *
+ * <p>The helper implements a key event handler.  If you pass a
+ * {@link android.support.v17.leanback.app.PlaybackOverlayFragment} the fragment's input event
+ * handler will be set.  Otherwise, you should set the glue object as key event handler to the
+ * ViewHolder when bound by your row presenter; see
+ * {@link RowPresenter.ViewHolder#setOnKeyListener(android.view.View.OnKeyListener)}.
  * </p>
  *
  * <p>To update the controls row progress during playback, override {@link #enableProgressUpdating}
@@ -59,7 +72,7 @@
  * </p>
  *
  */
-public abstract class PlaybackControlGlue {
+public abstract class PlaybackControlGlue implements OnActionClickedListener, View.OnKeyListener {
     /**
      * The adapter key for the first custom control on the right side
      * of the predefined primary controls.
@@ -182,7 +195,7 @@
             if (DEBUG) Log.v(TAG, "onItemClicked " + object);
             boolean handled = false;
             if (object instanceof Action) {
-                handled = handleActionClicked((Action) object);
+                handled = dispatchAction((Action) object, null);
             }
             if (!handled && mExternalOnItemViewClickedListener != null) {
                 mExternalOnItemViewClickedListener.onItemClicked(viewHolder, object,
@@ -191,74 +204,34 @@
         }
     };
 
-    private final PlaybackOverlayFragment.InputEventHandler mInputEventHandler =
-            new PlaybackOverlayFragment.InputEventHandler() {
-        @Override
-        public boolean handleInputEvent(InputEvent event) {
-            boolean result = false;
-            if (event instanceof KeyEvent &&
-                    ((KeyEvent) event).getAction() == KeyEvent.ACTION_DOWN) {
-                int keyCode = ((KeyEvent) event).getKeyCode();
-                switch (keyCode) {
-                    case KeyEvent.KEYCODE_DPAD_UP:
-                    case KeyEvent.KEYCODE_DPAD_DOWN:
-                    case KeyEvent.KEYCODE_DPAD_RIGHT:
-                    case KeyEvent.KEYCODE_DPAD_LEFT:
-                    case KeyEvent.KEYCODE_BACK:
-                        if (mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0 ||
-                                mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0) {
-                            mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-                            startPlayback(mPlaybackSpeed);
-                            updatePlaybackStatusAfterUserAction();
-                            result = (keyCode == KeyEvent.KEYCODE_BACK);
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                        if (mPlayPauseAction != null) {
-                            handleActionClicked(mPlayPauseAction);
-                            result = true;
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-                        if (mFastForwardAction != null) {
-                            handleActionClicked(mFastForwardAction);
-                            result = true;
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_MEDIA_REWIND:
-                        if (mRewindAction != null) {
-                            handleActionClicked(mRewindAction);
-                            result = true;
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-                        if (mSkipPreviousAction != null) {
-                            handleActionClicked(mSkipPreviousAction);
-                            result = true;
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_MEDIA_NEXT:
-                        if (mSkipNextAction != null) {
-                            handleActionClicked(mSkipNextAction);
-                            result = true;
-                        }
-                        break;
-                }
-            }
-            return result;
-        }
-    };
+    /**
+     * Constructor for the glue.
+     *
+     * @param context
+     * @param seekSpeeds Array of seek speeds for fast forward and rewind.
+     */
+    public PlaybackControlGlue(Context context, int[] seekSpeeds) {
+        this(context, null, seekSpeeds, seekSpeeds);
+    }
 
     /**
      * Constructor for the glue.
      *
-     * <p>The {@link PlaybackOverlayFragment} must be passed in.
-     * A {@link OnItemViewClickedListener} and {@link PlaybackOverlayFragment.InputEventHandler}
-     * will be set on the fragment.
-     * </p>
+     * @param context
+     * @param fastForwardSpeeds Array of seek speeds for fast forward.
+     * @param rewindSpeeds Array of seek speeds for rewind.
+     */
+    public PlaybackControlGlue(Context context,
+                               int[] fastForwardSpeeds,
+                               int[] rewindSpeeds) {
+        this(context, null, fastForwardSpeeds, rewindSpeeds);
+    }
+
+    /**
+     * Constructor for the glue.
      *
      * @param context
-     * @param fragment
+     * @param fragment Optional; if using a {@link PlaybackOverlayFragment}, pass it in.
      * @param seekSpeeds Array of seek speeds for fast forward and rewind.
      */
     public PlaybackControlGlue(Context context,
@@ -270,13 +243,8 @@
     /**
      * Constructor for the glue.
      *
-     * <p>The {@link PlaybackOverlayFragment} must be passed in.
-     * A {@link OnItemViewClickedListener} and {@link PlaybackOverlayFragment.InputEventHandler}
-     * will be set on the fragment.
-     * </p>
-     *
      * @param context
-     * @param fragment
+     * @param fragment Optional; if using a {@link PlaybackOverlayFragment}, pass it in.
      * @param fastForwardSpeeds Array of seek speeds for fast forward.
      * @param rewindSpeeds Array of seek speeds for rewind.
      */
@@ -286,14 +254,9 @@
                                int[] rewindSpeeds) {
         mContext = context;
         mFragment = fragment;
-        if (mFragment.getOnItemViewClickedListener() != null) {
-            throw new IllegalStateException("Fragment OnItemViewClickedListener already present");
+        if (fragment != null) {
+            attachToFragment();
         }
-        mFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        if (mFragment.getInputEventHandler() != null) {
-            throw new IllegalStateException("Fragment InputEventListener already present");
-        }
-        mFragment.setInputEventHandler(mInputEventHandler);
         if (fastForwardSpeeds.length == 0 || fastForwardSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
             throw new IllegalStateException("invalid fastForwardSpeeds array size");
         }
@@ -304,6 +267,22 @@
         mRewindSpeeds = rewindSpeeds;
     }
 
+    private final PlaybackOverlayFragment.InputEventHandler mOnInputEventHandler =
+            new PlaybackOverlayFragment.InputEventHandler() {
+        @Override
+        public boolean handleInputEvent(InputEvent event) {
+            if (event instanceof KeyEvent) {
+                KeyEvent keyEvent = (KeyEvent) event;
+                return onKey(null, keyEvent.getKeyCode(), keyEvent);
+            }
+            return false;
+        }
+    };
+
+    private void attachToFragment() {
+        mFragment.setInputEventHandler(mOnInputEventHandler);
+    }
+
     /**
      * Helper method for instantiating a
      * {@link android.support.v17.leanback.widget.PlaybackControlsRow} and corresponding
@@ -313,7 +292,8 @@
         PlaybackControlsRow controlsRow = new PlaybackControlsRow(this);
         setControlsRow(controlsRow);
 
-        return new PlaybackControlsRowPresenter(new AbstractDetailsDescriptionPresenter() {
+        AbstractDetailsDescriptionPresenter detailsPresenter =
+                new AbstractDetailsDescriptionPresenter() {
             @Override
             protected void onBindDescription(AbstractDetailsDescriptionPresenter.ViewHolder
                                                      viewHolder, Object object) {
@@ -326,7 +306,19 @@
                     viewHolder.getSubtitle().setText("");
                 }
             }
-        });
+        };
+        return new PlaybackControlsRowPresenter(detailsPresenter) {
+            @Override
+            protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
+                super.onBindRowViewHolder(vh, item);
+                vh.setOnKeyListener(PlaybackControlGlue.this);
+            }
+            @Override
+            protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) {
+                super.onUnbindRowViewHolder(vh);
+                vh.setOnKeyListener(null);
+            }
+        };
     }
 
     /**
@@ -362,7 +354,7 @@
      */
     public void setFadingEnabled(boolean enable) {
         mFadeWhenPlaying = enable;
-        if (!mFadeWhenPlaying) {
+        if (!mFadeWhenPlaying && mFragment != null) {
             mFragment.setFadingEnabled(false);
         }
     }
@@ -378,9 +370,14 @@
      * Set the {@link OnItemViewClickedListener} to be called if the click event
      * is not handled internally.
      * @param listener
+     * @deprecated Don't call this.  Instead set the listener on the fragment yourself,
+     * and call {@link #onActionClicked} to handle clicks.
      */
     public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
         mExternalOnItemViewClickedListener = listener;
+        if (mFragment != null) {
+            mFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
+        }
     }
 
     /**
@@ -435,13 +432,71 @@
         mControlsRow.setCurrentTime(position);
     }
 
-    private boolean handleActionClicked(Action action) {
+    /**
+     * Handles action clicks.  A subclass may override this add support for additional actions.
+     */
+    @Override
+    public void onActionClicked(Action action) {
+        dispatchAction(action, null);
+    }
+
+    /**
+     * Handles key events and returns true if handled.  A subclass may override this to provide
+     * additional support.
+     */
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_BACK:
+            case KeyEvent.KEYCODE_ESCAPE:
+                boolean abortSeek = mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0 ||
+                        mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0;
+                if (abortSeek) {
+                    mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
+                    startPlayback(mPlaybackSpeed);
+                    updatePlaybackStatusAfterUserAction();
+                    return keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE;
+                }
+                return false;
+        }
+        Action action = mControlsRow.getActionForKeyCode(mPrimaryActionsAdapter, keyCode);
+        if (action != null) {
+            if (action == mPrimaryActionsAdapter.lookup(ACTION_PLAY_PAUSE) ||
+                    action == mPrimaryActionsAdapter.lookup(ACTION_REWIND) ||
+                    action == mPrimaryActionsAdapter.lookup(ACTION_FAST_FORWARD) ||
+                    action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_PREVIOUS) ||
+                    action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_NEXT)) {
+                if (((KeyEvent) event).getAction() == KeyEvent.ACTION_DOWN) {
+                    dispatchAction(action, (KeyEvent) event);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called when the given action is invoked, either by click or keyevent.
+     */
+    private boolean dispatchAction(Action action, KeyEvent keyEvent) {
         boolean handled = false;
         if (action == mPlayPauseAction) {
+            boolean canPlay = keyEvent == null ||
+                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
+                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY;
+            boolean canPause = keyEvent == null ||
+                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
+                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE;
             if (mPlaybackSpeed != PLAYBACK_SPEED_NORMAL) {
-                mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-                startPlayback(mPlaybackSpeed);
-            } else {
+                if (canPlay) {
+                    mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
+                    startPlayback(mPlaybackSpeed);
+                }
+            } else if (canPause) {
                 mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
                 pausePlayback();
             }
@@ -456,16 +511,15 @@
         } else if (action == mFastForwardAction) {
             if (mPlaybackSpeed < getMaxForwardSpeedId()) {
                 switch (mPlaybackSpeed) {
-                    case PLAYBACK_SPEED_NORMAL:
-                    case PLAYBACK_SPEED_PAUSED:
-                        mPlaybackSpeed = PLAYBACK_SPEED_FAST_L0;
-                        break;
                     case PLAYBACK_SPEED_FAST_L0:
                     case PLAYBACK_SPEED_FAST_L1:
                     case PLAYBACK_SPEED_FAST_L2:
                     case PLAYBACK_SPEED_FAST_L3:
                         mPlaybackSpeed++;
                         break;
+                    default:
+                        mPlaybackSpeed = PLAYBACK_SPEED_FAST_L0;
+                        break;
                 }
                 startPlayback(mPlaybackSpeed);
                 updatePlaybackStatusAfterUserAction();
@@ -474,16 +528,15 @@
         } else if (action == mRewindAction) {
             if (mPlaybackSpeed > -getMaxRewindSpeedId()) {
                 switch (mPlaybackSpeed) {
-                    case PLAYBACK_SPEED_NORMAL:
-                    case PLAYBACK_SPEED_PAUSED:
-                        mPlaybackSpeed = -PLAYBACK_SPEED_FAST_L0;
-                        break;
                     case -PLAYBACK_SPEED_FAST_L0:
                     case -PLAYBACK_SPEED_FAST_L1:
                     case -PLAYBACK_SPEED_FAST_L2:
                     case -PLAYBACK_SPEED_FAST_L3:
                         mPlaybackSpeed--;
                         break;
+                    default:
+                        mPlaybackSpeed = -PLAYBACK_SPEED_FAST_L0;
+                        break;
                 }
                 startPlayback(mPlaybackSpeed);
                 updatePlaybackStatusAfterUserAction();
@@ -630,7 +683,7 @@
             enableProgressUpdating(true);
         }
 
-        if (mFadeWhenPlaying) {
+        if (mFadeWhenPlaying && mFragment != null) {
             mFragment.setFadingEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL);
         }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
index 0d81005..72c08a3 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
@@ -19,6 +19,9 @@
 import android.animation.AnimatorInflater;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.Row;
 import android.view.InputEvent;
 import android.view.animation.AccelerateInterpolator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -198,6 +201,7 @@
             mResetControlsToPrimaryActionsPending = false;
             ((PlaybackControlsRowPresenter) vh.getPresenter()).showPrimaryActions(
                     (PlaybackControlsRowPresenter.ViewHolder) vh.getViewHolder());
+            vh.getViewHolder().view.clearFocus();
         }
     }
 
@@ -278,42 +282,52 @@
         }
     }
 
-    private static boolean isConsumableKey(KeyEvent keyEvent) {
-        if (keyEvent.isSystem()) {
-            return false;
-        }
-        return true;
+    private boolean areControlsHidden() {
+        return mFadingStatus == IDLE && mBgAlpha == 0;
     }
 
     private boolean onInterceptInputEvent(InputEvent event) {
-        if (DEBUG) Log.v(TAG, "onInterceptInputEvent status " + mFadingStatus +
-                " mBgAlpha " + mBgAlpha + " event " + event);
-        final boolean controlsHidden = (mFadingStatus == IDLE && mBgAlpha == 0);
-        boolean consumeEvent = controlsHidden;
+        final boolean controlsHidden = areControlsHidden();
+        if (DEBUG) Log.v(TAG, "onInterceptInputEvent hidden " + controlsHidden + " " + event);
+        boolean consumeEvent = false;
         int keyCode = KeyEvent.KEYCODE_UNKNOWN;
 
-        if (event instanceof KeyEvent) {
-            if (consumeEvent) {
-                consumeEvent = isConsumableKey((KeyEvent) event);
-            }
-            keyCode = ((KeyEvent) event).getKeyCode();
-        }
-        if (!consumeEvent && mInputEventHandler != null) {
+        if (mInputEventHandler != null) {
             consumeEvent = mInputEventHandler.handleInputEvent(event);
         }
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
-            // If fading enabled and controls are not hidden, back will be consumed to fade
-            // them out (even if the key was consumed by the handler).
-            if (mFadingEnabled && !controlsHidden) {
-                consumeEvent = true;
-                mHandler.removeMessages(START_FADE_OUT);
-                fade(false);
-            } else if (consumeEvent) {
+        if (event instanceof KeyEvent) {
+            keyCode = ((KeyEvent) event).getKeyCode();
+        }
+
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                // Event may be consumed; regardless, if controls are hidden then these keys will
+                // bring up the controls.
+                if (controlsHidden) {
+                    consumeEvent = true;
+                }
                 tickle();
-            }
-        } else {
-            // Any other key will show the controls
-            tickle();
+                break;
+            case KeyEvent.KEYCODE_BACK:
+            case KeyEvent.KEYCODE_ESCAPE:
+                // If fading enabled and controls are not hidden, back will be consumed to fade
+                // them out (even if the key was consumed by the handler).
+                if (mFadingEnabled && !controlsHidden) {
+                    consumeEvent = true;
+                    mHandler.removeMessages(START_FADE_OUT);
+                    fade(false);
+                } else if (consumeEvent) {
+                    tickle();
+                }
+                break;
+            default:
+                if (consumeEvent) {
+                    tickle();
+                }
         }
         return consumeEvent;
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
index a453f24..51efadb 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
@@ -21,6 +21,9 @@
 import android.animation.AnimatorInflater;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.Row;
 import android.view.InputEvent;
 import android.view.animation.AccelerateInterpolator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -200,6 +203,7 @@
             mResetControlsToPrimaryActionsPending = false;
             ((PlaybackControlsRowPresenter) vh.getPresenter()).showPrimaryActions(
                     (PlaybackControlsRowPresenter.ViewHolder) vh.getViewHolder());
+            vh.getViewHolder().view.clearFocus();
         }
     }
 
@@ -280,42 +284,52 @@
         }
     }
 
-    private static boolean isConsumableKey(KeyEvent keyEvent) {
-        if (keyEvent.isSystem()) {
-            return false;
-        }
-        return true;
+    private boolean areControlsHidden() {
+        return mFadingStatus == IDLE && mBgAlpha == 0;
     }
 
     private boolean onInterceptInputEvent(InputEvent event) {
-        if (DEBUG) Log.v(TAG, "onInterceptInputEvent status " + mFadingStatus +
-                " mBgAlpha " + mBgAlpha + " event " + event);
-        final boolean controlsHidden = (mFadingStatus == IDLE && mBgAlpha == 0);
-        boolean consumeEvent = controlsHidden;
+        final boolean controlsHidden = areControlsHidden();
+        if (DEBUG) Log.v(TAG, "onInterceptInputEvent hidden " + controlsHidden + " " + event);
+        boolean consumeEvent = false;
         int keyCode = KeyEvent.KEYCODE_UNKNOWN;
 
-        if (event instanceof KeyEvent) {
-            if (consumeEvent) {
-                consumeEvent = isConsumableKey((KeyEvent) event);
-            }
-            keyCode = ((KeyEvent) event).getKeyCode();
-        }
-        if (!consumeEvent && mInputEventHandler != null) {
+        if (mInputEventHandler != null) {
             consumeEvent = mInputEventHandler.handleInputEvent(event);
         }
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
-            // If fading enabled and controls are not hidden, back will be consumed to fade
-            // them out (even if the key was consumed by the handler).
-            if (mFadingEnabled && !controlsHidden) {
-                consumeEvent = true;
-                mHandler.removeMessages(START_FADE_OUT);
-                fade(false);
-            } else if (consumeEvent) {
+        if (event instanceof KeyEvent) {
+            keyCode = ((KeyEvent) event).getKeyCode();
+        }
+
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                // Event may be consumed; regardless, if controls are hidden then these keys will
+                // bring up the controls.
+                if (controlsHidden) {
+                    consumeEvent = true;
+                }
                 tickle();
-            }
-        } else {
-            // Any other key will show the controls
-            tickle();
+                break;
+            case KeyEvent.KEYCODE_BACK:
+            case KeyEvent.KEYCODE_ESCAPE:
+                // If fading enabled and controls are not hidden, back will be consumed to fade
+                // them out (even if the key was consumed by the handler).
+                if (mFadingEnabled && !controlsHidden) {
+                    consumeEvent = true;
+                    mHandler.removeMessages(START_FADE_OUT);
+                    fade(false);
+                } else if (consumeEvent) {
+                    tickle();
+                }
+                break;
+            default:
+                if (consumeEvent) {
+                    tickle();
+                }
         }
         return consumeEvent;
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
index ff61927..47ad9e4 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
@@ -26,8 +26,6 @@
 import android.support.v17.leanback.widget.ScaleFrameLayout;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.support.v17.leanback.widget.HorizontalGridView;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
-import android.support.v17.leanback.widget.OnItemClickedListener;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
@@ -89,7 +87,7 @@
         }
 
         void animateSelect(boolean select, boolean immediate) {
-            endSelectAnimation();
+            mSelectAnimator.end();
             final float end = select ? 1 : 0;
             if (immediate) {
                 mRowPresenter.setSelectLevel(mRowViewHolder, end);
@@ -102,14 +100,6 @@
             }
         }
 
-        void endAnimations() {
-            endSelectAnimation();
-        }
-
-        void endSelectAnimation() {
-            mSelectAnimator.end();
-        }
-
     }
 
     private static final String TAG = "RowsFragment";
@@ -125,9 +115,7 @@
     private boolean mInTransition;
     private boolean mAfterEntranceTransition = true;
 
-    private OnItemSelectedListener mOnItemSelectedListener;
     private OnItemViewSelectedListener mOnItemViewSelectedListener;
-    private OnItemClickedListener mOnItemClickedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
 
     // Select animation and interpolator are not intended to be
@@ -148,29 +136,6 @@
 
     /**
      * Sets an item clicked listener on the fragment.
-     * OnItemClickedListener will override {@link View.OnClickListener} that
-     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
-     * So in general,  developer should choose one of the listeners but not both.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-        if (mViewsCreated) {
-            throw new IllegalStateException(
-                    "Item clicked listener must be set before views are created");
-        }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     * @deprecated Use {@link #getOnItemClickedListener()}
-     */
-    public OnItemClickedListener getOnItemClickedListener() {
-        return mOnItemClickedListener;
-    }
-
-    /**
-     * Sets an item clicked listener on the fragment.
      * OnItemViewClickedListener will override {@link View.OnClickListener} that
      * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
      * So in general,  developer should choose one of the listeners but not both.
@@ -210,24 +175,6 @@
 
     /**
      * Sets an item selection listener.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mOnItemSelectedListener = listener;
-        VerticalGridView listView = getVerticalGridView();
-        if (listView != null) {
-            final int count = listView.getChildCount();
-            for (int i = 0; i < count; i++) {
-                View view = listView.getChildAt(i);
-                ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
-                        listView.getChildViewHolder(view);
-                setOnItemSelectedListener(vh, mOnItemSelectedListener);
-            }
-        }
-    }
-
-    /**
-     * Sets an item selection listener.
      */
     public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
         mOnItemViewSelectedListener = listener;
@@ -236,9 +183,11 @@
             final int count = listView.getChildCount();
             for (int i = 0; i < count; i++) {
                 View view = listView.getChildAt(i);
-                ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
+                ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder)
                         listView.getChildViewHolder(view);
-                setOnItemViewSelectedListener(vh, mOnItemViewSelectedListener);
+                RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter();
+                RowPresenter.ViewHolder vh = rowPresenter.getRowViewHolder(ibvh.getViewHolder());
+                vh.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
             }
         }
     }
@@ -362,22 +311,10 @@
         ((RowPresenter) vh.getPresenter()).setRowViewSelected(vh.getViewHolder(), selected);
     }
 
-    private static void setOnItemSelectedListener(ItemBridgeAdapter.ViewHolder vh,
-            OnItemSelectedListener listener) {
-        ((RowPresenter) vh.getPresenter()).setOnItemSelectedListener(listener);
-    }
-
-    private static void setOnItemViewSelectedListener(ItemBridgeAdapter.ViewHolder vh,
-            OnItemViewSelectedListener listener) {
-        ((RowPresenter) vh.getPresenter()).setOnItemViewSelectedListener(listener);
-    }
-
     private final ItemBridgeAdapter.AdapterListener mBridgeAdapterListener =
             new ItemBridgeAdapter.AdapterListener() {
         @Override
         public void onAddPresenter(Presenter presenter, int type) {
-            ((RowPresenter) presenter).setOnItemClickedListener(mOnItemClickedListener);
-            ((RowPresenter) presenter).setOnItemViewClickedListener(mOnItemViewClickedListener);
             if (mExternalAdapterListener != null) {
                 mExternalAdapterListener.onAddPresenter(presenter, type);
             }
@@ -409,10 +346,10 @@
             // but again it should use the unchanged mExpand value,  so we don't need do any
             // thing in onBind.
             setRowViewExpanded(vh, mExpand);
-            setOnItemSelectedListener(vh, mOnItemSelectedListener);
-            setOnItemViewSelectedListener(vh, mOnItemViewSelectedListener);
             RowPresenter rowPresenter = (RowPresenter) vh.getPresenter();
             RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(vh.getViewHolder());
+            rowVh.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
+            rowVh.setOnItemViewClickedListener(mOnItemViewClickedListener);
             rowPresenter.setEntranceTransitionState(rowVh, mAfterEntranceTransition);
             if (mExternalAdapterListener != null) {
                 mExternalAdapterListener.onAttachedToWindow(vh);
@@ -436,8 +373,7 @@
         }
         @Override
         public void onUnbind(ItemBridgeAdapter.ViewHolder vh) {
-            RowViewHolderExtra extra = (RowViewHolderExtra) vh.getExtraObject();
-            extra.endAnimations();
+            setRowViewSelected(vh, false, true);
             if (mExternalAdapterListener != null) {
                 mExternalAdapterListener.onUnbind(vh);
             }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
index 4e95878..029ddbd 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
@@ -28,8 +28,6 @@
 import android.support.v17.leanback.widget.ScaleFrameLayout;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.support.v17.leanback.widget.HorizontalGridView;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
-import android.support.v17.leanback.widget.OnItemClickedListener;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
@@ -91,7 +89,7 @@
         }
 
         void animateSelect(boolean select, boolean immediate) {
-            endSelectAnimation();
+            mSelectAnimator.end();
             final float end = select ? 1 : 0;
             if (immediate) {
                 mRowPresenter.setSelectLevel(mRowViewHolder, end);
@@ -104,14 +102,6 @@
             }
         }
 
-        void endAnimations() {
-            endSelectAnimation();
-        }
-
-        void endSelectAnimation() {
-            mSelectAnimator.end();
-        }
-
     }
 
     private static final String TAG = "RowsSupportFragment";
@@ -127,9 +117,7 @@
     private boolean mInTransition;
     private boolean mAfterEntranceTransition = true;
 
-    private OnItemSelectedListener mOnItemSelectedListener;
     private OnItemViewSelectedListener mOnItemViewSelectedListener;
-    private OnItemClickedListener mOnItemClickedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
 
     // Select animation and interpolator are not intended to be
@@ -150,29 +138,6 @@
 
     /**
      * Sets an item clicked listener on the fragment.
-     * OnItemClickedListener will override {@link View.OnClickListener} that
-     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
-     * So in general,  developer should choose one of the listeners but not both.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-        if (mViewsCreated) {
-            throw new IllegalStateException(
-                    "Item clicked listener must be set before views are created");
-        }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     * @deprecated Use {@link #getOnItemClickedListener()}
-     */
-    public OnItemClickedListener getOnItemClickedListener() {
-        return mOnItemClickedListener;
-    }
-
-    /**
-     * Sets an item clicked listener on the fragment.
      * OnItemViewClickedListener will override {@link View.OnClickListener} that
      * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
      * So in general,  developer should choose one of the listeners but not both.
@@ -212,24 +177,6 @@
 
     /**
      * Sets an item selection listener.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mOnItemSelectedListener = listener;
-        VerticalGridView listView = getVerticalGridView();
-        if (listView != null) {
-            final int count = listView.getChildCount();
-            for (int i = 0; i < count; i++) {
-                View view = listView.getChildAt(i);
-                ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
-                        listView.getChildViewHolder(view);
-                setOnItemSelectedListener(vh, mOnItemSelectedListener);
-            }
-        }
-    }
-
-    /**
-     * Sets an item selection listener.
      */
     public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
         mOnItemViewSelectedListener = listener;
@@ -238,9 +185,11 @@
             final int count = listView.getChildCount();
             for (int i = 0; i < count; i++) {
                 View view = listView.getChildAt(i);
-                ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
+                ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder)
                         listView.getChildViewHolder(view);
-                setOnItemViewSelectedListener(vh, mOnItemViewSelectedListener);
+                RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter();
+                RowPresenter.ViewHolder vh = rowPresenter.getRowViewHolder(ibvh.getViewHolder());
+                vh.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
             }
         }
     }
@@ -364,22 +313,10 @@
         ((RowPresenter) vh.getPresenter()).setRowViewSelected(vh.getViewHolder(), selected);
     }
 
-    private static void setOnItemSelectedListener(ItemBridgeAdapter.ViewHolder vh,
-            OnItemSelectedListener listener) {
-        ((RowPresenter) vh.getPresenter()).setOnItemSelectedListener(listener);
-    }
-
-    private static void setOnItemViewSelectedListener(ItemBridgeAdapter.ViewHolder vh,
-            OnItemViewSelectedListener listener) {
-        ((RowPresenter) vh.getPresenter()).setOnItemViewSelectedListener(listener);
-    }
-
     private final ItemBridgeAdapter.AdapterListener mBridgeAdapterListener =
             new ItemBridgeAdapter.AdapterListener() {
         @Override
         public void onAddPresenter(Presenter presenter, int type) {
-            ((RowPresenter) presenter).setOnItemClickedListener(mOnItemClickedListener);
-            ((RowPresenter) presenter).setOnItemViewClickedListener(mOnItemViewClickedListener);
             if (mExternalAdapterListener != null) {
                 mExternalAdapterListener.onAddPresenter(presenter, type);
             }
@@ -411,10 +348,10 @@
             // but again it should use the unchanged mExpand value,  so we don't need do any
             // thing in onBind.
             setRowViewExpanded(vh, mExpand);
-            setOnItemSelectedListener(vh, mOnItemSelectedListener);
-            setOnItemViewSelectedListener(vh, mOnItemViewSelectedListener);
             RowPresenter rowPresenter = (RowPresenter) vh.getPresenter();
             RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(vh.getViewHolder());
+            rowVh.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
+            rowVh.setOnItemViewClickedListener(mOnItemViewClickedListener);
             rowPresenter.setEntranceTransitionState(rowVh, mAfterEntranceTransition);
             if (mExternalAdapterListener != null) {
                 mExternalAdapterListener.onAttachedToWindow(vh);
@@ -438,8 +375,7 @@
         }
         @Override
         public void onUnbind(ItemBridgeAdapter.ViewHolder vh) {
-            RowViewHolderExtra extra = (RowViewHolderExtra) vh.getExtraObject();
-            extra.endAnimations();
+            setRowViewSelected(vh, false, true);
             if (mExternalAdapterListener != null) {
                 mExternalAdapterListener.onUnbind(vh);
             }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java b/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
index 2299b5b..4572840 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
@@ -22,8 +22,6 @@
 import android.speech.RecognizerIntent;
 import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter.DataObserver;
-import android.support.v17.leanback.widget.OnItemClickedListener;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Row;
@@ -200,8 +198,6 @@
     private SearchResultProvider mProvider;
     private String mPendingQuery = null;
 
-    private OnItemSelectedListener mOnItemSelectedListener;
-    private OnItemClickedListener mOnItemClickedListener;
     private OnItemViewSelectedListener mOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
     private ObjectAdapter mResultAdapter;
@@ -314,9 +310,6 @@
                 int position = mRowsFragment.getVerticalGridView().getSelectedPosition();
                 if (DEBUG) Log.v(TAG, String.format("onItemSelected %d", position));
                 mSearchBar.setVisibility(0 >= position ? View.VISIBLE : View.GONE);
-                if (null != mOnItemSelectedListener) {
-                    mOnItemSelectedListener.onItemSelected(item, row);
-                }
                 if (null != mOnItemViewSelectedListener) {
                     mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
                             rowViewHolder, row);
@@ -329,9 +322,6 @@
                     RowPresenter.ViewHolder rowViewHolder, Row row) {
                 int position = mRowsFragment.getVerticalGridView().getSelectedPosition();
                 if (DEBUG) Log.v(TAG, String.format("onItemClicked %d", position));
-                if (null != mOnItemClickedListener) {
-                    mOnItemClickedListener.onItemClicked(item, row);
-                }
                 if (null != mOnItemViewClickedListener) {
                     mOnItemViewClickedListener.onItemClicked(itemViewHolder, item,
                             rowViewHolder, row);
@@ -424,28 +414,6 @@
      *
      * @param listener The item selection listener to be invoked when an item in
      *        the search results is selected.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mOnItemSelectedListener = listener;
-    }
-
-    /**
-     * Sets an item clicked listener for the results.
-     *
-     * @param listener The item clicked listener to be invoked when an item in
-     *        the search results is clicked.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-    }
-
-    /**
-     * Sets an item selection listener for the results.
-     *
-     * @param listener The item selection listener to be invoked when an item in
-     *        the search results is selected.
      */
     public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
         mOnItemViewSelectedListener = listener;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
index b3c280f..76f94eb 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
@@ -24,8 +24,6 @@
 import android.speech.RecognizerIntent;
 import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter.DataObserver;
-import android.support.v17.leanback.widget.OnItemClickedListener;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Row;
@@ -202,8 +200,6 @@
     private SearchResultProvider mProvider;
     private String mPendingQuery = null;
 
-    private OnItemSelectedListener mOnItemSelectedListener;
-    private OnItemClickedListener mOnItemClickedListener;
     private OnItemViewSelectedListener mOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
     private ObjectAdapter mResultAdapter;
@@ -316,9 +312,6 @@
                 int position = mRowsSupportFragment.getVerticalGridView().getSelectedPosition();
                 if (DEBUG) Log.v(TAG, String.format("onItemSelected %d", position));
                 mSearchBar.setVisibility(0 >= position ? View.VISIBLE : View.GONE);
-                if (null != mOnItemSelectedListener) {
-                    mOnItemSelectedListener.onItemSelected(item, row);
-                }
                 if (null != mOnItemViewSelectedListener) {
                     mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
                             rowViewHolder, row);
@@ -331,9 +324,6 @@
                     RowPresenter.ViewHolder rowViewHolder, Row row) {
                 int position = mRowsSupportFragment.getVerticalGridView().getSelectedPosition();
                 if (DEBUG) Log.v(TAG, String.format("onItemClicked %d", position));
-                if (null != mOnItemClickedListener) {
-                    mOnItemClickedListener.onItemClicked(item, row);
-                }
                 if (null != mOnItemViewClickedListener) {
                     mOnItemViewClickedListener.onItemClicked(itemViewHolder, item,
                             rowViewHolder, row);
@@ -426,28 +416,6 @@
      *
      * @param listener The item selection listener to be invoked when an item in
      *        the search results is selected.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mOnItemSelectedListener = listener;
-    }
-
-    /**
-     * Sets an item clicked listener for the results.
-     *
-     * @param listener The item clicked listener to be invoked when an item in
-     *        the search results is clicked.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-    }
-
-    /**
-     * Sets an item selection listener for the results.
-     *
-     * @param listener The item selection listener to be invoked when an item in
-     *        the search results is selected.
      */
     public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
         mOnItemViewSelectedListener = listener;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
index a5e5f0a..6b6cc2e 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
@@ -13,32 +13,24 @@
  */
 package android.support.v17.leanback.app;
 
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.BrowseFrameLayout;
+import android.support.v17.leanback.widget.OnChildLaidOutListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.TitleHelper;
 import android.support.v17.leanback.widget.TitleView;
 import android.support.v17.leanback.widget.VerticalGridPresenter;
 import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnItemClickedListener;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
-import android.support.v17.leanback.widget.SearchOrbView;
-import android.support.v4.view.ViewCompat;
-import android.app.Fragment;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.ImageView;
-import android.widget.TextView;
 
 /**
  * A fragment for creating leanback vertical grids.
@@ -46,71 +38,17 @@
  * <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and
  * an {@link ObjectAdapter}.
  */
-public class VerticalGridFragment extends Fragment {
+public class VerticalGridFragment extends BrandedFragment {
     private static final String TAG = "VerticalGridFragment";
     private static boolean DEBUG = false;
 
-    private BrowseFrameLayout mBrowseFrame;
-    private String mTitle;
-    private Drawable mBadgeDrawable;
     private ObjectAdapter mAdapter;
     private VerticalGridPresenter mGridPresenter;
     private VerticalGridPresenter.ViewHolder mGridViewHolder;
-    private OnItemSelectedListener mOnItemSelectedListener;
-    private OnItemClickedListener mOnItemClickedListener;
     private OnItemViewSelectedListener mOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
-    private View.OnClickListener mExternalOnSearchClickedListener;
     private int mSelectedPosition = -1;
 
-    private TitleView mTitleView;
-    private SearchOrbView.Colors mSearchAffordanceColors;
-    private boolean mSearchAffordanceColorSet;
-    private boolean mShowingTitle = true;
-
-    // transition related
-    private static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
-    private Object mTitleUpTransition;
-    private Object mTitleDownTransition;
-    private Object mSceneWithTitle;
-    private Object mSceneWithoutTitle;
-
-    /**
-     * Sets the badge drawable displayed in the title area.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-        if (drawable != mBadgeDrawable) {
-            mBadgeDrawable = drawable;
-            if (mTitleView != null) {
-                mTitleView.setBadgeDrawable(drawable);
-            }
-        }
-    }
-
-    /**
-     * Returns the badge drawable.
-     */
-    public Drawable getBadgeDrawable() {
-        return mBadgeDrawable;
-    }
-
-    /**
-     * Sets a title for the fragment.
-     */
-    public void setTitle(String title) {
-        mTitle = title;
-        if (mTitleView != null) {
-            mTitleView.setTitle(mTitle);
-        }
-    }
-
-    /**
-     * Returns the title for the fragment.
-     */
-    public String getTitle() {
-        return mTitle;
-    }
-
     /**
      * Sets the grid presenter.
      */
@@ -119,13 +57,10 @@
             throw new IllegalArgumentException("Grid presenter may not be null");
         }
         mGridPresenter = gridPresenter;
-        mGridPresenter.setOnItemViewSelectedListener(mRowSelectedListener);
+        mGridPresenter.setOnItemViewSelectedListener(mViewSelectedListener);
         if (mOnItemViewClickedListener != null) {
             mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener);
         }
-        if (mOnItemClickedListener != null) {
-            mGridPresenter.setOnItemClickedListener(mOnItemClickedListener);
-        }
     }
 
     /**
@@ -150,17 +85,14 @@
         return mAdapter;
     }
 
-    final private OnItemViewSelectedListener mRowSelectedListener =
+    final private OnItemViewSelectedListener mViewSelectedListener =
             new OnItemViewSelectedListener() {
         @Override
         public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
                 RowPresenter.ViewHolder rowViewHolder, Row row) {
             int position = mGridViewHolder.getGridView().getSelectedPosition();
-            if (DEBUG) Log.v(TAG, "row selected position " + position);
-            onRowSelected(position);
-            if (mOnItemSelectedListener != null) {
-                mOnItemSelectedListener.onItemSelected(item, row);
-            }
+            if (DEBUG) Log.v(TAG, "grid selected position " + position);
+            gridOnItemSelected(position);
             if (mOnItemViewSelectedListener != null) {
                 mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
                         rowViewHolder, row);
@@ -168,13 +100,15 @@
         }
     };
 
-    /**
-     * Sets an item selection listener.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mOnItemSelectedListener = listener;
-    }
+    final private OnChildLaidOutListener mChildLaidOutListener =
+            new OnChildLaidOutListener() {
+        @Override
+        public void onChildLaidOut(ViewGroup parent, View view, int position, long id) {
+            if (position == 0) {
+                showOrHideTitle();
+            }
+        }
+    };
 
     /**
      * Sets an item selection listener.
@@ -183,39 +117,23 @@
         mOnItemViewSelectedListener = listener;
     }
 
-    private void onRowSelected(int position) {
+    private void gridOnItemSelected(int position) {
         if (position != mSelectedPosition) {
-            if (!mGridViewHolder.getGridView().hasPreviousViewInSameRow(position)) {
-                // if has no sibling in front of it,  show title
-                if (!mShowingTitle) {
-                    sTransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition);
-                    mShowingTitle = true;
-                }
-            } else if (mShowingTitle) {
-                sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition);
-                mShowingTitle = false;
-            }
             mSelectedPosition = position;
+            showOrHideTitle();
         }
     }
 
-    /**
-     * Sets an item clicked listener.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-        if (mGridPresenter != null) {
-            mGridPresenter.setOnItemClickedListener(mOnItemClickedListener);
+    private void showOrHideTitle() {
+        if (mGridViewHolder.getGridView().findViewHolderForAdapterPosition(mSelectedPosition)
+                == null) {
+            return;
         }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     * @deprecated Use {@link #getOnItemViewClickedListener()}
-     */
-    public OnItemClickedListener getOnItemClickedListener() {
-        return mOnItemClickedListener;
+        if (!mGridViewHolder.getGridView().hasPreviousViewInSameRow(mSelectedPosition)) {
+            showTitle(true);
+        } else {
+            showTitle(false);
+        }
     }
 
     /**
@@ -235,157 +153,40 @@
         return mOnItemViewClickedListener;
     }
 
-    /**
-     * Sets a click listener for the search affordance.
-     *
-     * <p>The presence of a listener will change the visibility of the search
-     * affordance in the title area. When set to non-null, the title area will
-     * contain a call to search action.
-     *
-     * <p>The listener's onClick method will be invoked when the user clicks on
-     * the search action.
-     *
-     * @param listener The listener to invoke when the search affordance is
-     *        clicked, or null to hide the search affordance.
-     */
-    public void setOnSearchClickedListener(View.OnClickListener listener) {
-        mExternalOnSearchClickedListener = listener;
-        if (mTitleView != null) {
-            mTitleView.setOnSearchClickedListener(listener);
-        }
-    }
-
-    /**
-     * Sets the {@link SearchOrbView.Colors} used to draw the search affordance.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-        mSearchAffordanceColors = colors;
-        mSearchAffordanceColorSet = true;
-        if (mTitleView != null) {
-            mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
-        }
-    }
-
-    /**
-     * Returns the {@link SearchOrbView.Colors} used to draw the search affordance.
-     */
-    public SearchOrbView.Colors getSearchAffordanceColors() {
-        if (mSearchAffordanceColorSet) {
-            return mSearchAffordanceColors;
-        }
-        if (mTitleView == null) {
-            throw new IllegalStateException("Fragment views not yet created");
-        }
-        return mTitleView.getSearchAffordanceColors();
-    }
-
-    /**
-     * Sets the color used to draw the search affordance.
-     * A default brighter color will be set by the framework.
-     *
-     * @param color The color to use for the search affordance.
-     */
-    public void setSearchAffordanceColor(int color) {
-        setSearchAffordanceColors(new SearchOrbView.Colors(color));
-    }
-
-    /**
-     * Returns the color used to draw the search affordance.
-     */
-    public int getSearchAffordanceColor() {
-        return getSearchAffordanceColors().color;
-    }
-
-    private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
-            new BrowseFrameLayout.OnFocusSearchListener() {
-        @Override
-        public View onFocusSearch(View focused, int direction) {
-            if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
-
-            final View searchOrbView = mTitleView.getSearchAffordanceView();
-            final boolean isRtl = ViewCompat.getLayoutDirection(focused) ==
-                    View.LAYOUT_DIRECTION_RTL;
-            final int forward = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
-            if (focused == searchOrbView && (
-                    direction == View.FOCUS_DOWN || direction == forward)) {
-                return mGridViewHolder.view;
-
-            } else if (focused != searchOrbView && searchOrbView.getVisibility() == View.VISIBLE
-                    && direction == View.FOCUS_UP) {
-                return searchOrbView;
-
-            } else {
-                return null;
-            }
-        }
-    };
-
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         ViewGroup root = (ViewGroup) inflater.inflate(R.layout.lb_vertical_grid_fragment,
                 container, false);
-
-        mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
-        mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
-
-        mTitleView = (TitleView) root.findViewById(R.id.browse_title_group);
-        mTitleView.setBadgeDrawable(mBadgeDrawable);
-        mTitleView.setTitle(mTitle);
-        if (mSearchAffordanceColorSet) {
-            mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
-        }
-        if (mExternalOnSearchClickedListener != null) {
-            mTitleView.setOnSearchClickedListener(mExternalOnSearchClickedListener);
-        }
-
-        mSceneWithTitle = sTransitionHelper.createScene(root, new Runnable() {
-            @Override
-            public void run() {
-                mTitleView.setVisibility(View.VISIBLE);
-            }
-        });
-        mSceneWithoutTitle = sTransitionHelper.createScene(root, new Runnable() {
-            @Override
-            public void run() {
-                mTitleView.setVisibility(View.INVISIBLE);
-            }
-        });
-        Context context = getActivity();
-        mTitleUpTransition = sTransitionHelper.loadTransition(context, R.transition.lb_title_out);
-        mTitleDownTransition = sTransitionHelper.loadTransition(context, R.transition.lb_title_in);
-
+        setTitleView((TitleView) root.findViewById(R.id.browse_title_group));
         return root;
     }
 
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
         ViewGroup gridDock = (ViewGroup) view.findViewById(R.id.browse_grid_dock);
         mGridViewHolder = mGridPresenter.onCreateViewHolder(gridDock);
         gridDock.addView(mGridViewHolder.view);
+        mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener);
 
         updateAdapter();
     }
 
+    private void setupFocusSearchListener() {
+        BrowseFrameLayout browseFrameLayout = (BrowseFrameLayout) getView().findViewById(
+                R.id.grid_frame);
+        browseFrameLayout.setOnFocusSearchListener(getTitleHelper().getOnFocusSearchListener());
+    }
+
     @Override
     public void onStart() {
         super.onStart();
+        setupFocusSearchListener();
         mGridViewHolder.getGridView().requestFocus();
     }
 
     @Override
-    public void onPause() {
-        mTitleView.enableAnimation(false);
-        super.onPause();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mTitleView.enableAnimation(true);
-    }
-
-    @Override
     public void onDestroyView() {
         super.onDestroyView();
         mGridViewHolder = null;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
index 4353a5d..0770761 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
@@ -15,32 +15,24 @@
  */
 package android.support.v17.leanback.app;
 
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.BrowseFrameLayout;
+import android.support.v17.leanback.widget.OnChildLaidOutListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.TitleHelper;
 import android.support.v17.leanback.widget.TitleView;
 import android.support.v17.leanback.widget.VerticalGridPresenter;
 import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnItemClickedListener;
-import android.support.v17.leanback.widget.OnItemSelectedListener;
-import android.support.v17.leanback.widget.SearchOrbView;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.app.Fragment;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.ImageView;
-import android.widget.TextView;
 
 /**
  * A fragment for creating leanback vertical grids.
@@ -48,71 +40,17 @@
  * <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and
  * an {@link ObjectAdapter}.
  */
-public class VerticalGridSupportFragment extends Fragment {
+public class VerticalGridSupportFragment extends BrandedSupportFragment {
     private static final String TAG = "VerticalGridSupportFragment";
     private static boolean DEBUG = false;
 
-    private BrowseFrameLayout mBrowseFrame;
-    private String mTitle;
-    private Drawable mBadgeDrawable;
     private ObjectAdapter mAdapter;
     private VerticalGridPresenter mGridPresenter;
     private VerticalGridPresenter.ViewHolder mGridViewHolder;
-    private OnItemSelectedListener mOnItemSelectedListener;
-    private OnItemClickedListener mOnItemClickedListener;
     private OnItemViewSelectedListener mOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
-    private View.OnClickListener mExternalOnSearchClickedListener;
     private int mSelectedPosition = -1;
 
-    private TitleView mTitleView;
-    private SearchOrbView.Colors mSearchAffordanceColors;
-    private boolean mSearchAffordanceColorSet;
-    private boolean mShowingTitle = true;
-
-    // transition related
-    private static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
-    private Object mTitleUpTransition;
-    private Object mTitleDownTransition;
-    private Object mSceneWithTitle;
-    private Object mSceneWithoutTitle;
-
-    /**
-     * Sets the badge drawable displayed in the title area.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-        if (drawable != mBadgeDrawable) {
-            mBadgeDrawable = drawable;
-            if (mTitleView != null) {
-                mTitleView.setBadgeDrawable(drawable);
-            }
-        }
-    }
-
-    /**
-     * Returns the badge drawable.
-     */
-    public Drawable getBadgeDrawable() {
-        return mBadgeDrawable;
-    }
-
-    /**
-     * Sets a title for the fragment.
-     */
-    public void setTitle(String title) {
-        mTitle = title;
-        if (mTitleView != null) {
-            mTitleView.setTitle(mTitle);
-        }
-    }
-
-    /**
-     * Returns the title for the fragment.
-     */
-    public String getTitle() {
-        return mTitle;
-    }
-
     /**
      * Sets the grid presenter.
      */
@@ -121,13 +59,10 @@
             throw new IllegalArgumentException("Grid presenter may not be null");
         }
         mGridPresenter = gridPresenter;
-        mGridPresenter.setOnItemViewSelectedListener(mRowSelectedListener);
+        mGridPresenter.setOnItemViewSelectedListener(mViewSelectedListener);
         if (mOnItemViewClickedListener != null) {
             mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener);
         }
-        if (mOnItemClickedListener != null) {
-            mGridPresenter.setOnItemClickedListener(mOnItemClickedListener);
-        }
     }
 
     /**
@@ -152,17 +87,14 @@
         return mAdapter;
     }
 
-    final private OnItemViewSelectedListener mRowSelectedListener =
+    final private OnItemViewSelectedListener mViewSelectedListener =
             new OnItemViewSelectedListener() {
         @Override
         public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
                 RowPresenter.ViewHolder rowViewHolder, Row row) {
             int position = mGridViewHolder.getGridView().getSelectedPosition();
-            if (DEBUG) Log.v(TAG, "row selected position " + position);
-            onRowSelected(position);
-            if (mOnItemSelectedListener != null) {
-                mOnItemSelectedListener.onItemSelected(item, row);
-            }
+            if (DEBUG) Log.v(TAG, "grid selected position " + position);
+            gridOnItemSelected(position);
             if (mOnItemViewSelectedListener != null) {
                 mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
                         rowViewHolder, row);
@@ -170,13 +102,15 @@
         }
     };
 
-    /**
-     * Sets an item selection listener.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mOnItemSelectedListener = listener;
-    }
+    final private OnChildLaidOutListener mChildLaidOutListener =
+            new OnChildLaidOutListener() {
+        @Override
+        public void onChildLaidOut(ViewGroup parent, View view, int position, long id) {
+            if (position == 0) {
+                showOrHideTitle();
+            }
+        }
+    };
 
     /**
      * Sets an item selection listener.
@@ -185,39 +119,23 @@
         mOnItemViewSelectedListener = listener;
     }
 
-    private void onRowSelected(int position) {
+    private void gridOnItemSelected(int position) {
         if (position != mSelectedPosition) {
-            if (!mGridViewHolder.getGridView().hasPreviousViewInSameRow(position)) {
-                // if has no sibling in front of it,  show title
-                if (!mShowingTitle) {
-                    sTransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition);
-                    mShowingTitle = true;
-                }
-            } else if (mShowingTitle) {
-                sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition);
-                mShowingTitle = false;
-            }
             mSelectedPosition = position;
+            showOrHideTitle();
         }
     }
 
-    /**
-     * Sets an item clicked listener.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-        if (mGridPresenter != null) {
-            mGridPresenter.setOnItemClickedListener(mOnItemClickedListener);
+    private void showOrHideTitle() {
+        if (mGridViewHolder.getGridView().findViewHolderForAdapterPosition(mSelectedPosition)
+                == null) {
+            return;
         }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     * @deprecated Use {@link #getOnItemViewClickedListener()}
-     */
-    public OnItemClickedListener getOnItemClickedListener() {
-        return mOnItemClickedListener;
+        if (!mGridViewHolder.getGridView().hasPreviousViewInSameRow(mSelectedPosition)) {
+            showTitle(true);
+        } else {
+            showTitle(false);
+        }
     }
 
     /**
@@ -237,157 +155,40 @@
         return mOnItemViewClickedListener;
     }
 
-    /**
-     * Sets a click listener for the search affordance.
-     *
-     * <p>The presence of a listener will change the visibility of the search
-     * affordance in the title area. When set to non-null, the title area will
-     * contain a call to search action.
-     *
-     * <p>The listener's onClick method will be invoked when the user clicks on
-     * the search action.
-     *
-     * @param listener The listener to invoke when the search affordance is
-     *        clicked, or null to hide the search affordance.
-     */
-    public void setOnSearchClickedListener(View.OnClickListener listener) {
-        mExternalOnSearchClickedListener = listener;
-        if (mTitleView != null) {
-            mTitleView.setOnSearchClickedListener(listener);
-        }
-    }
-
-    /**
-     * Sets the {@link SearchOrbView.Colors} used to draw the search affordance.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-        mSearchAffordanceColors = colors;
-        mSearchAffordanceColorSet = true;
-        if (mTitleView != null) {
-            mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
-        }
-    }
-
-    /**
-     * Returns the {@link SearchOrbView.Colors} used to draw the search affordance.
-     */
-    public SearchOrbView.Colors getSearchAffordanceColors() {
-        if (mSearchAffordanceColorSet) {
-            return mSearchAffordanceColors;
-        }
-        if (mTitleView == null) {
-            throw new IllegalStateException("Fragment views not yet created");
-        }
-        return mTitleView.getSearchAffordanceColors();
-    }
-
-    /**
-     * Sets the color used to draw the search affordance.
-     * A default brighter color will be set by the framework.
-     *
-     * @param color The color to use for the search affordance.
-     */
-    public void setSearchAffordanceColor(int color) {
-        setSearchAffordanceColors(new SearchOrbView.Colors(color));
-    }
-
-    /**
-     * Returns the color used to draw the search affordance.
-     */
-    public int getSearchAffordanceColor() {
-        return getSearchAffordanceColors().color;
-    }
-
-    private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
-            new BrowseFrameLayout.OnFocusSearchListener() {
-        @Override
-        public View onFocusSearch(View focused, int direction) {
-            if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
-
-            final View searchOrbView = mTitleView.getSearchAffordanceView();
-            final boolean isRtl = ViewCompat.getLayoutDirection(focused) ==
-                    View.LAYOUT_DIRECTION_RTL;
-            final int forward = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
-            if (focused == searchOrbView && (
-                    direction == View.FOCUS_DOWN || direction == forward)) {
-                return mGridViewHolder.view;
-
-            } else if (focused != searchOrbView && searchOrbView.getVisibility() == View.VISIBLE
-                    && direction == View.FOCUS_UP) {
-                return searchOrbView;
-
-            } else {
-                return null;
-            }
-        }
-    };
-
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         ViewGroup root = (ViewGroup) inflater.inflate(R.layout.lb_vertical_grid_fragment,
                 container, false);
-
-        mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
-        mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
-
-        mTitleView = (TitleView) root.findViewById(R.id.browse_title_group);
-        mTitleView.setBadgeDrawable(mBadgeDrawable);
-        mTitleView.setTitle(mTitle);
-        if (mSearchAffordanceColorSet) {
-            mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
-        }
-        if (mExternalOnSearchClickedListener != null) {
-            mTitleView.setOnSearchClickedListener(mExternalOnSearchClickedListener);
-        }
-
-        mSceneWithTitle = sTransitionHelper.createScene(root, new Runnable() {
-            @Override
-            public void run() {
-                mTitleView.setVisibility(View.VISIBLE);
-            }
-        });
-        mSceneWithoutTitle = sTransitionHelper.createScene(root, new Runnable() {
-            @Override
-            public void run() {
-                mTitleView.setVisibility(View.INVISIBLE);
-            }
-        });
-        Context context = getActivity();
-        mTitleUpTransition = sTransitionHelper.loadTransition(context, R.transition.lb_title_out);
-        mTitleDownTransition = sTransitionHelper.loadTransition(context, R.transition.lb_title_in);
-
+        setTitleView((TitleView) root.findViewById(R.id.browse_title_group));
         return root;
     }
 
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
         ViewGroup gridDock = (ViewGroup) view.findViewById(R.id.browse_grid_dock);
         mGridViewHolder = mGridPresenter.onCreateViewHolder(gridDock);
         gridDock.addView(mGridViewHolder.view);
+        mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener);
 
         updateAdapter();
     }
 
+    private void setupFocusSearchListener() {
+        BrowseFrameLayout browseFrameLayout = (BrowseFrameLayout) getView().findViewById(
+                R.id.grid_frame);
+        browseFrameLayout.setOnFocusSearchListener(getTitleHelper().getOnFocusSearchListener());
+    }
+
     @Override
     public void onStart() {
         super.onStart();
+        setupFocusSearchListener();
         mGridViewHolder.getGridView().requestFocus();
     }
 
     @Override
-    public void onPause() {
-        mTitleView.enableAnimation(false);
-        super.onPause();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mTitleView.enableAnimation(true);
-    }
-
-    @Override
     public void onDestroyView() {
         super.onDestroyView();
         mGridViewHolder = null;
diff --git a/v17/leanback/src/android/support/v17/leanback/os/TraceHelper.java b/v17/leanback/src/android/support/v17/leanback/os/TraceHelper.java
new file mode 100644
index 0000000..09a082d
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/os/TraceHelper.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 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.os;
+
+import android.os.Build;
+import android.support.v17.leanback.os.TraceHelperJbmr2;
+
+
+/**
+ * Helper for systrace events.
+ * @hide
+ */
+public final class TraceHelper {
+
+    final static TraceHelperVersionImpl sImpl;
+
+    static interface TraceHelperVersionImpl {
+        public void beginSection(String section);
+        public void endSection();
+    }
+
+    private static final class TraceHelperStubImpl implements TraceHelperVersionImpl {
+        @Override
+        public void beginSection(String section) {
+        }
+
+        @Override
+        public void endSection() {
+        }
+    }
+
+    private static final class TraceHelperJbmr2Impl implements TraceHelperVersionImpl {
+        @Override
+        public void beginSection(String section) {
+            TraceHelperJbmr2.beginSection(section);
+        }
+
+        @Override
+        public void endSection() {
+            TraceHelperJbmr2.endSection();
+        }
+    }
+
+    private TraceHelper() {
+    }
+
+    static {
+        if (Build.VERSION.SDK_INT >= 18) {
+            sImpl = new TraceHelperJbmr2Impl();
+        } else {
+            sImpl = new TraceHelperStubImpl();
+        }
+    }
+
+    public static void beginSection(String section) {
+        sImpl.beginSection(section);
+    }
+
+    public static void endSection() {
+        sImpl.endSection();
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
index 1fd6ea2..f2332d4 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
@@ -20,11 +20,13 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.widget.TextView;
 
 /**
  * An abstract {@link Presenter} for rendering a detailed description of an
- * item. Typically this Presenter will be used in a DetailsOveriewRowPresenter.
+ * item. Typically this Presenter will be used in a {@link DetailsOverviewRowPresenter}
+ * or {@link PlaybackControlsRowPresenter}.
  *
  * <p>Subclasses will override {@link #onBindDescription} to implement the data
  * binding for this Presenter.
@@ -45,8 +47,10 @@
         private final FontMetricsInt mTitleFontMetricsInt;
         private final FontMetricsInt mSubtitleFontMetricsInt;
         private final FontMetricsInt mBodyFontMetricsInt;
+        private final int mTitleMaxLines;
+        private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
 
-        public ViewHolder(View view) {
+        public ViewHolder(final View view) {
             super(view);
             mTitle = (TextView) view.findViewById(R.id.lb_details_description_title);
             mSubtitle = (TextView) view.findViewById(R.id.lb_details_description_subtitle);
@@ -72,6 +76,7 @@
                     R.integer.lb_details_description_body_max_lines);
             mBodyMinLines = view.getResources().getInteger(
                     R.integer.lb_details_description_body_min_lines);
+            mTitleMaxLines = mTitle.getMaxLines();
 
             mTitleFontMetricsInt = getFontMetricsInt(mTitle);
             mSubtitleFontMetricsInt = getFontMetricsInt(mSubtitle);
@@ -79,13 +84,47 @@
 
             mTitle.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) {
-                    mBody.setMaxLines(mTitle.getLineCount() > 1 ? mBodyMinLines : mBodyMaxLines);
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                                           int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    addPreDrawListener();
                 }
             });
         }
 
+        void addPreDrawListener() {
+            if (mPreDrawListener != null) {
+                return;
+            }
+            mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    if (mSubtitle.getVisibility() == View.VISIBLE &&
+                            mSubtitle.getTop() > view.getHeight() &&
+                            mTitle.getLineCount() > 1) {
+                        mTitle.setMaxLines(mTitle.getLineCount() - 1);
+                        return false;
+                    }
+                    final int titleLines = mTitle.getLineCount();
+                    final int maxLines = titleLines > 1 ? mBodyMinLines : mBodyMaxLines;
+                    if (mBody.getMaxLines() != maxLines) {
+                        mBody.setMaxLines(maxLines);
+                        return false;
+                    } else {
+                        removePreDrawListener();
+                        return true;
+                    }
+                }
+            };
+            view.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
+        }
+
+        void removePreDrawListener() {
+            if (mPreDrawListener != null) {
+                view.getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener);
+                mPreDrawListener = null;
+            }
+        }
+
         public TextView getTitle() {
             return mTitle;
         }
@@ -126,6 +165,7 @@
             vh.mTitle.setVisibility(View.VISIBLE);
             vh.mTitle.setLineSpacing(vh.mTitleLineSpacing - vh.mTitle.getLineHeight() +
                     vh.mTitle.getLineSpacingExtra(), vh.mTitle.getLineSpacingMultiplier());
+            vh.mTitle.setMaxLines(vh.mTitleMaxLines);
         }
         setTopMargin(vh.mTitle, vh.mTitleMargin);
 
@@ -174,6 +214,22 @@
     @Override
     public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {}
 
+    @Override
+    public void onViewAttachedToWindow(Presenter.ViewHolder holder) {
+        // In case predraw listener was removed in detach, make sure
+        // we have the proper layout.
+        ViewHolder vh = (ViewHolder) holder;
+        vh.addPreDrawListener();
+        super.onViewAttachedToWindow(holder);
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(Presenter.ViewHolder holder) {
+        ViewHolder vh = (ViewHolder) holder;
+        vh.removePreDrawListener();
+        super.onViewDetachedFromWindow(holder);
+    }
+
     private void setTopMargin(TextView textView, int topMargin) {
         ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) textView.getLayoutParams();
         lp.topMargin = topMargin;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Action.java b/v17/leanback/src/android/support/v17/leanback/widget/Action.java
index deb36c4..c573a60 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/Action.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Action.java
@@ -15,6 +15,9 @@
 
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
+import android.view.KeyEvent;
+
+import java.util.ArrayList;
 
 import static android.support.v17.leanback.widget.ObjectAdapter.NO_ID;
 
@@ -28,6 +31,7 @@
     private Drawable mIcon;
     private CharSequence mLabel1;
     private CharSequence mLabel2;
+    private ArrayList mKeyCodes = new ArrayList();
 
     /**
      * Constructor for an Action.
@@ -130,6 +134,27 @@
         return mIcon;
     }
 
+    /**
+     * Add a keycode used to invoke this Action.
+     */
+    public final void addKeyCode(int keyCode) {
+        mKeyCodes.add(keyCode);
+    }
+
+    /**
+     * Removes a keycode used to invoke this Action.
+     */
+    public final void removeKeyCode(int keyCode) {
+        mKeyCodes.remove(keyCode);
+    }
+
+    /**
+     * Returns true if the Action should respond to the given keycode.
+     */
+    public final boolean respondsToKeyCode(int keyCode) {
+        return mKeyCodes.contains(keyCode);
+    }
+
     @Override
     public String toString(){
         StringBuilder sb = new StringBuilder();
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java
index d11e5b1..19803ca 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java
@@ -15,6 +15,8 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * An ObjectAdapter implemented with an {@link ArrayList}.
@@ -173,4 +175,11 @@
         mItems.clear();
         notifyItemRangeRemoved(0, itemCount);
     }
+
+    /**
+     * Gets a read-only view of the list of object of this ArrayObjectAdapter.
+     */
+    public <E> List<E> unmodifiableList() {
+        return Collections.unmodifiableList((List<E>) mItems);
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
index bb81bb6..d89baa9 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
@@ -163,6 +163,13 @@
         public boolean onInterceptKeyEvent(KeyEvent event);
     }
 
+    public interface OnUnhandledKeyListener {
+        /**
+         * Returns true if the key event should be consumed.
+         */
+        public boolean onUnhandledKey(KeyEvent event);
+    }
+
     protected final GridLayoutManager mLayoutManager;
 
     /**
@@ -177,6 +184,8 @@
     private OnTouchInterceptListener mOnTouchInterceptListener;
     private OnMotionInterceptListener mOnMotionInterceptListener;
     private OnKeyInterceptListener mOnKeyInterceptListener;
+    private RecyclerView.RecyclerListener mChainedRecyclerListener;
+    private OnUnhandledKeyListener mOnUnhandledKeyListener;
 
     public BaseGridView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
@@ -191,6 +200,15 @@
         // Change animation will create a new view and cause undesired
         // focus animation between the old view and new view.
         getItemAnimator().setSupportsChangeAnimations(false);
+        super.setRecyclerListener(new RecyclerView.RecyclerListener() {
+            @Override
+            public void onViewRecycled(RecyclerView.ViewHolder holder) {
+                mLayoutManager.onChildRecycled(holder);
+                if (mChainedRecyclerListener != null) {
+                    mChainedRecyclerListener.onViewRecycled(holder);
+                }
+            }
+        });
     }
 
     protected void initBaseGridViewAttributes(Context context, AttributeSet attrs) {
@@ -433,6 +451,16 @@
 
     /**
      * Register a callback to be invoked when an item in BaseGridView has
+     * been laid out.
+     *
+     * @param listener The listener to be invoked.
+     */
+    public void setOnChildLaidOutListener(OnChildLaidOutListener listener) {
+        mLayoutManager.setOnChildLaidOutListener(listener);
+    }
+
+    /**
+     * Register a callback to be invoked when an item in BaseGridView has
      * been selected.  Note that the listener may be invoked when there is a
      * layout pending on the view, affording the listener an opportunity to
      * adjust the upcoming layout based on the selection state.
@@ -447,7 +475,16 @@
      * Change the selected item immediately without animation.
      */
     public void setSelectedPosition(int position) {
-        mLayoutManager.setSelection(this, position);
+        mLayoutManager.setSelection(this, position, 0);
+    }
+
+    /**
+     * Change the selected item immediately without animation, scrollExtra is
+     * applied in primary scroll direction.  The scrollExtra will be kept until
+     * another {@link #setSelectedPosition} or {@link #setSelectedPositionSmooth} call.
+     */
+    public void setSelectedPosition(int position, int scrollExtra) {
+        mLayoutManager.setSelection(this, position, scrollExtra);
     }
 
     /**
@@ -533,6 +570,9 @@
      * Disable or enable focus search.
      */
     public final void setFocusSearchDisabled(boolean disabled) {
+        // LayoutManager may detachView and attachView in fastRelayout, it causes RowsFragment
+        // re-gain focus after a BACK key pressed, so block children focus during transition.
+        setDescendantFocusability(disabled ? FOCUS_BLOCK_DESCENDANTS: FOCUS_AFTER_DESCENDANTS);
         mLayoutManager.setFocusSearchDisabled(disabled);
     }
 
@@ -581,7 +621,9 @@
 
     /**
      * Returns true if the view at the given position has a same row sibling
-     * in front of it.
+     * in front of it.  This will return true if first item view is not created.
+     * So application should check in both {@link OnChildSelectedListener} and {@link
+     * OnChildLaidOutListener}.
      *
      * @param position Position in adapter.
      */
@@ -624,14 +666,32 @@
         mOnKeyInterceptListener = listener;
     }
 
+    /**
+     * Sets the unhandled key listener.
+     */
+    public void setOnUnhandledKeyListener(OnUnhandledKeyListener listener) {
+        mOnUnhandledKeyListener = listener;
+    }
+
+    /**
+     * Returns the unhandled key listener.
+     */
+    public OnUnhandledKeyListener getOnUnhandledKeyListener() {
+        return mOnUnhandledKeyListener;
+    }
+
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
-        if (mOnKeyInterceptListener != null) {
-            if (mOnKeyInterceptListener.onInterceptKeyEvent(event)) {
-                return true;
-            }
+        if (mOnKeyInterceptListener != null && mOnKeyInterceptListener.onInterceptKeyEvent(event)) {
+            return true;
         }
-        return super.dispatchKeyEvent(event);
+        if (super.dispatchKeyEvent(event)) {
+            return true;
+        }
+        if (mOnUnhandledKeyListener != null && mOnUnhandledKeyListener.onUnhandledKey(event)) {
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -703,5 +763,8 @@
         mLayoutManager.onRtlPropertiesChanged(layoutDirection);
     }
 
-
+    @Override
+    public void setRecyclerListener(RecyclerView.RecyclerListener listener) {
+        mChainedRecyclerListener = listener;
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BrowseFrameLayout.java b/v17/leanback/src/android/support/v17/leanback/widget/BrowseFrameLayout.java
index 654f39b..3638658 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BrowseFrameLayout.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BrowseFrameLayout.java
@@ -20,16 +20,25 @@
 import android.widget.FrameLayout;
 
 /**
- * Top level implementation viewgroup for browse to manage transitions between
- * browse sub fragments.
- * @hide
+ * Top level implementation viewgroup for managing focus behavior between overlapping views.
  */
 public class BrowseFrameLayout extends FrameLayout {
 
+    /**
+     * Interface for selecting a focused view when the system focus finder couldn't find a view
+     * to focus.
+     */
     public interface OnFocusSearchListener {
+        /**
+         * Returns the view where focus should be requested given the current focused view and
+         * the direction of focus search.
+         */
         public View onFocusSearch(View focused, int direction);
     }
 
+    /**
+     * Interface for managing child focus.
+     */
     public interface OnChildFocusListener {
         public boolean onRequestFocusInDescendants(int direction,
                 Rect previouslyFocusedRect);
@@ -51,14 +60,34 @@
     private OnFocusSearchListener mListener;
     private OnChildFocusListener mOnChildFocusListener;
 
+    /**
+     * Sets an {@link OnFocusSearchListener}.
+     */
     public void setOnFocusSearchListener(OnFocusSearchListener listener) {
         mListener = listener;
     }
 
+    /**
+     * Returns the {@link OnFocusSearchListener}.
+     */
+    public OnFocusSearchListener getOnFocusSearchListener() {
+        return mListener;
+    }
+
+    /**
+     * Sets an {@link OnChildFocusListener}.
+     */
     public void setOnChildFocusListener(OnChildFocusListener listener) {
         mOnChildFocusListener = listener;
     }
 
+    /**
+     * Returns the {@link OnChildFocusListener}.
+     */
+    public OnChildFocusListener getOnChildFocusListener() {
+        return mOnChildFocusListener;
+    }
+
     @Override
     protected boolean onRequestFocusInDescendants(int direction,
             Rect previouslyFocusedRect) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java
index 60fe6be..d35878f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java
@@ -17,22 +17,63 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.view.KeyEvent;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 /**
  * An overview row for a details fragment. This row consists of an image, a
  * description view, and optionally a series of {@link Action}s that can be taken for
  * the item.
+ *
+ * <h3>Actions</h3>
+ * Application uses {@link #setActionsAdapter(ObjectAdapter)} to set actions on the overview
+ * row.  {@link SparseArrayObjectAdapter} is recommended for easy updating actions while
+ * keeping the order.  Application can add or remove actions on UI thread after the row is
+ * bound to view.
+ *
+ * <h3>Updating main item</h3>
+ * After the row is bound to view, application still can call ({@link #setItem(Object)})
+ * on UI thread.
+ *
+ * <h3>Updating image</h3>
+ * After the row is bound to view, application still can change image by calling ({@link
+ * #setImageBitmap(Context, Bitmap)}) or {@link #setImageDrawable(Drawable)}) on UI thread.
  */
 public class DetailsOverviewRow extends Row {
 
+    /**
+     * Listener for changes of DetailsOverViewRow.
+     */
+    static class Listener {
+
+        /**
+         * Called when DetailsOverviewRow has changed image drawable.
+         */
+        public void onImageDrawableChanged(DetailsOverviewRow row) {
+        }
+
+        /**
+         * Called when DetailsOverviewRow has changed main item.
+         */
+        public void onItemChanged(DetailsOverviewRow row) {
+        }
+
+        /**
+         * Called when DetailsOverviewRow has changed actions adapter.
+         */
+        public void onActionsAdapterChanged(DetailsOverviewRow row) {
+        }
+    }
+
     private Object mItem;
     private Drawable mImageDrawable;
-    private ArrayList<Action> mActions = new ArrayList<Action>();
     private boolean mImageScaleUpAllowed = true;
+    private ArrayList<WeakReference<Listener>> mListeners;
+    private PresenterSelector mDefaultActionPresenter = new ActionPresenterSelector();
+    private ObjectAdapter mActionsAdapter = new ArrayObjectAdapter(mDefaultActionPresenter);
 
     /**
      * Constructor for a DetailsOverviewRow.
@@ -46,6 +87,99 @@
     }
 
     /**
+     * Adds listener for the details page.
+     */
+    final void addListener(Listener listener) {
+        if (mListeners == null) {
+            mListeners = new ArrayList<WeakReference<Listener>>();
+        } else {
+            for (int i = 0; i < mListeners.size();) {
+                Listener l = mListeners.get(i).get();
+                if (l == null) {
+                    mListeners.remove(i);
+                } else {
+                    if (l == listener) {
+                        return;
+                    }
+                    i++;
+                }
+            }
+        }
+        mListeners.add(new WeakReference<Listener>(listener));
+    }
+
+    /**
+     * Removes listener of the details page.
+     */
+    final void removeListener(Listener listener) {
+        if (mListeners != null) {
+            for (int i = 0; i < mListeners.size();) {
+                Listener l = mListeners.get(i).get();
+                if (l == null) {
+                    mListeners.remove(i);
+                } else {
+                    if (l == listener) {
+                        mListeners.remove(i);
+                        return;
+                    }
+                    i++;
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies listeners for main item change on UI thread.
+     */
+    final void notifyItemChanged() {
+        if (mListeners != null) {
+            for (int i = 0; i < mListeners.size();) {
+                Listener l = mListeners.get(i).get();
+                if (l == null) {
+                    mListeners.remove(i);
+                } else {
+                    l.onItemChanged(this);
+                    i++;
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies listeners for image related change on UI thread.
+     */
+    final void notifyImageDrawableChanged() {
+        if (mListeners != null) {
+            for (int i = 0; i < mListeners.size();) {
+                Listener l = mListeners.get(i).get();
+                if (l == null) {
+                    mListeners.remove(i);
+                } else {
+                    l.onImageDrawableChanged(this);
+                    i++;
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies listeners for actions adapter changed on UI thread.
+     */
+    final void notifyActionsAdapterChanged() {
+        if (mListeners != null) {
+            for (int i = 0; i < mListeners.size();) {
+                Listener l = mListeners.get(i).get();
+                if (l == null) {
+                    mListeners.remove(i);
+                } else {
+                    l.onActionsAdapterChanged(this);
+                    i++;
+                }
+            }
+        }
+    }
+
+    /**
      * Gets the main item for the details page.
      */
     public final Object getItem() {
@@ -53,22 +187,39 @@
     }
 
     /**
-     * Sets a drawable as the image of this details overview.
+     * Sets the main item for the details page.  Must be called on UI thread after
+     * row is bound to view.
+     */
+    public final void setItem(Object item) {
+        if (item != mItem) {
+            mItem = item;
+            notifyItemChanged();
+        }
+    }
+
+    /**
+     * Sets a drawable as the image of this details overview.  Must be called on UI thread
+     * after row is bound to view.
      *
      * @param drawable The drawable to set.
      */
     public final void setImageDrawable(Drawable drawable) {
-        mImageDrawable = drawable;
+        if (mImageDrawable != drawable) {
+            mImageDrawable = drawable;
+            notifyImageDrawableChanged();
+        }
     }
 
     /**
-     * Sets a Bitmap as the image of this details overview.
+     * Sets a Bitmap as the image of this details overview.  Must be called on UI thread
+     * after row is bound to view.
      *
      * @param context The context to retrieve display metrics from.
      * @param bm The bitmap to set.
      */
     public final void setImageBitmap(Context context, Bitmap bm) {
         mImageDrawable = new BitmapDrawable(context.getResources(), bm);
+        notifyImageDrawableChanged();
     }
 
     /**
@@ -83,10 +234,14 @@
 
     /**
      * Allows or disallows scaling up of images.
-     * Images will always be scaled down if necessary.
+     * Images will always be scaled down if necessary.  Must be called on UI thread
+     * after row is bound to view.
      */
     public void setImageScaleUpAllowed(boolean allowed) {
-        mImageScaleUpAllowed = allowed;
+        if (allowed != mImageScaleUpAllowed) {
+            mImageScaleUpAllowed = allowed;
+            notifyImageDrawableChanged();
+        }
     }
 
     /**
@@ -97,41 +252,96 @@
     }
 
     /**
-     * Add an Action to the overview.
-     *
-     * @param action The Action to add.
+     * Get array object adapter.  Throws ClassCastException if the current ObjectAdapter is not
+     * ArrayObjectAdapter.
      */
-    public final void addAction(Action action) {
-        mActions.add(action);
+    private ArrayObjectAdapter getArrayObjectAdapter() {
+        return (ArrayObjectAdapter) mActionsAdapter;
     }
 
     /**
-     * Add an Action to the overview at the specified position.
+     * Add an Action to the overview. It will throw ClassCastException if current actions adapter
+     * is not {@link ArrayObjectAdapter}. Must be called on UI thread.
+     *
+     * @param action The Action to add.
+     * @deprecated Use {@link #setActionsAdapter(ObjectAdapter)} and {@link #getActionsAdapter()}
+     */
+    public final void addAction(Action action) {
+        getArrayObjectAdapter().add(action);
+    }
+
+    /**
+     * Add an Action to the overview at the specified position. It will throw ClassCastException if
+     * current actions adapter is not {@link ArrayObjectAdapter}. Must be called on UI thread.
      *
      * @param pos The position to insert the Action.
      * @param action The Action to add.
+     * @deprecated Use {@link #setActionsAdapter(ObjectAdapter)} and {@link #getActionsAdapter()}
      */
     public final void addAction(int pos, Action action) {
-        mActions.add(pos, action);
+        getArrayObjectAdapter().add(pos, action);
     }
 
     /**
-     * Remove the given Action from the overview.
+     * Remove the given Action from the overview. It will throw ClassCastException if current
+     * actions adapter is not {@link ArrayObjectAdapter}. Must be called on UI thread.
      *
      * @param action The Action to remove.
      * @return true if the overview contained the specified Action.
+     * @deprecated Use {@link #setActionsAdapter(ObjectAdapter)} and {@link #getActionsAdapter()}
      */
     public final boolean removeAction(Action action) {
-        return mActions.remove(action);
+        return getArrayObjectAdapter().remove(action);
     }
 
     /**
-     * Gets a read-only view of the list of Actions of this details overview.
+     * Gets a read-only view of the list of Actions of this details overview. It will throw
+     * ClassCastException if current actions adapter is not {@link ArrayObjectAdapter}. Must be
+     * called on UI thread.
      *
      * @return An unmodifiable view of the list of Actions.
+     * @deprecated Use {@link #setActionsAdapter(ObjectAdapter)} and {@link #getActionsAdapter()}
      */
     public final List<Action> getActions() {
-        return Collections.unmodifiableList(mActions);
+        return getArrayObjectAdapter().unmodifiableList();
+    }
+
+    /**
+     * Gets {@link ObjectAdapter} for actions.
+     */
+    public final ObjectAdapter getActionsAdapter() {
+        return mActionsAdapter;
+    }
+
+    /**
+     * Sets {@link ObjectAdapter} for actions.
+     * @param adapter  Adapter for actions, a default {@link PresenterSelector} will be attached
+     *                 to the adapter if it doesn't have one.
+     */
+    public final void setActionsAdapter(ObjectAdapter adapter) {
+        if (adapter != mActionsAdapter) {
+            mActionsAdapter = adapter;
+            if (mActionsAdapter.getPresenterSelector() == null) {
+                mActionsAdapter.setPresenterSelector(mDefaultActionPresenter);
+            }
+            notifyActionsAdapterChanged();
+        }
+    }
+
+    /**
+     * Returns the Action associated with the given keycode, or null if no associated action exists.
+     */
+    public Action getActionForKeyCode(int keyCode) {
+        ObjectAdapter adapter = getActionsAdapter();
+        if (adapter != null) {
+            for (int i = 0; i < adapter.size(); i++) {
+                Action action = (Action) adapter.get(i);
+                if (action.respondsToKeyCode(keyCode)) {
+                    return action;
+                }
+            }
+        }
+        return null;
     }
 
     private void verify() {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
index 8556e16..f5772ac 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
@@ -20,10 +20,13 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
+import android.os.Handler;
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.util.TypedValue;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -65,6 +68,53 @@
     private static final int MORE_ACTIONS_FADE_MS = 100;
     private static final long DEFAULT_TIMEOUT = 5000;
 
+    class ActionsItemBridgeAdapter extends ItemBridgeAdapter {
+        DetailsOverviewRowPresenter.ViewHolder mViewHolder;
+
+        ActionsItemBridgeAdapter(DetailsOverviewRowPresenter.ViewHolder viewHolder) {
+            mViewHolder = viewHolder;
+        }
+
+        @Override
+        public void onBind(final ItemBridgeAdapter.ViewHolder ibvh) {
+            if (mViewHolder.getOnItemViewClickedListener() != null ||
+                    mActionClickedListener != null) {
+                ibvh.getPresenter().setOnClickListener(
+                        ibvh.getViewHolder(), new View.OnClickListener() {
+                            @Override
+                            public void onClick(View v) {
+                                if (mViewHolder.getOnItemViewClickedListener() != null) {
+                                    mViewHolder.getOnItemViewClickedListener().onItemClicked(
+                                            ibvh.getViewHolder(), ibvh.getItem(),
+                                            mViewHolder, mViewHolder.getRow());
+                                }
+                                if (mActionClickedListener != null) {
+                                    mActionClickedListener.onActionClicked((Action) ibvh.getItem());
+                                }
+                            }
+                        });
+            }
+        }
+        @Override
+        public void onUnbind(final ItemBridgeAdapter.ViewHolder ibvh) {
+            if (mViewHolder.getOnItemViewClickedListener() != null ||
+                    mActionClickedListener != null) {
+                ibvh.getPresenter().setOnClickListener(ibvh.getViewHolder(), null);
+            }
+        }
+        @Override
+        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
+            // Remove first to ensure we don't add ourselves more than once.
+            viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
+            viewHolder.itemView.addOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
+        }
+        @Override
+        public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
+            viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
+            mViewHolder.checkFirstAndLastPosition(false);
+        }
+    }
+
     /**
      * A ViewHolder for the DetailsOverviewRow.
      */
@@ -79,9 +129,38 @@
         int mNumItems;
         boolean mShowMoreRight;
         boolean mShowMoreLeft;
-        final ItemBridgeAdapter mActionBridgeAdapter = new ItemBridgeAdapter();
+        ItemBridgeAdapter mActionBridgeAdapter;
+        final Handler mHandler = new Handler();
 
-        void bind(ObjectAdapter adapter) {
+        final Runnable mUpdateDrawableCallback = new Runnable() {
+            @Override
+            public void run() {
+                bindImageDrawable(ViewHolder.this);
+            }
+        };
+
+        final DetailsOverviewRow.Listener mListener = new DetailsOverviewRow.Listener() {
+            @Override
+            public void onImageDrawableChanged(DetailsOverviewRow row) {
+                mHandler.removeCallbacks(mUpdateDrawableCallback);
+                mHandler.post(mUpdateDrawableCallback);
+            }
+
+            @Override
+            public void onItemChanged(DetailsOverviewRow row) {
+                if (mDetailsDescriptionViewHolder != null) {
+                    mDetailsPresenter.onUnbindViewHolder(mDetailsDescriptionViewHolder);
+                }
+                mDetailsPresenter.onBindViewHolder(mDetailsDescriptionViewHolder, row.getItem());
+            }
+
+            @Override
+            public void onActionsAdapterChanged(DetailsOverviewRow row) {
+                bindActions(row.getActionsAdapter());
+            }
+        };
+
+        void bindActions(ObjectAdapter adapter) {
             mActionBridgeAdapter.setAdapter(adapter);
             mActionsRow.setAdapter(mActionBridgeAdapter);
             mNumItems = mActionBridgeAdapter.getItemCount();
@@ -117,17 +196,11 @@
                     mActionsRow.getChildViewHolder(view) :
                     mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition()));
             if (ibvh == null) {
-                if (getOnItemSelectedListener() != null) {
-                    getOnItemSelectedListener().onItemSelected(null, getRow());
-                }
                 if (getOnItemViewSelectedListener() != null) {
                     getOnItemViewSelectedListener().onItemSelected(null, null,
                             ViewHolder.this, getRow());
                 }
             } else {
-                if (getOnItemSelectedListener() != null) {
-                    getOnItemSelectedListener().onItemSelected(ibvh.getItem(), getRow());
-                }
                 if (getOnItemViewSelectedListener() != null) {
                     getOnItemViewSelectedListener().onItemSelected(ibvh.getViewHolder(), ibvh.getItem(),
                             ViewHolder.this, getRow());
@@ -135,48 +208,6 @@
             }
         };
 
-        final ItemBridgeAdapter.AdapterListener mAdapterListener =
-                new ItemBridgeAdapter.AdapterListener() {
-
-            @Override
-            public void onBind(final ItemBridgeAdapter.ViewHolder ibvh) {
-                if (getOnItemViewClickedListener() != null || getOnItemClickedListener() != null
-                        || mActionClickedListener != null) {
-                    ibvh.getPresenter().setOnClickListener(
-                            ibvh.getViewHolder(), new View.OnClickListener() {
-                                @Override
-                                public void onClick(View v) {
-                                    if (getOnItemViewClickedListener() != null) {
-                                        getOnItemViewClickedListener().onItemClicked(ibvh.getViewHolder(),
-                                                ibvh.getItem(), ViewHolder.this, getRow());
-                                    }
-                                    if (mActionClickedListener != null) {
-                                        mActionClickedListener.onActionClicked((Action) ibvh.getItem());
-                                    }
-                                }
-                            });
-                }
-            }
-            @Override
-            public void onUnbind(final ItemBridgeAdapter.ViewHolder ibvh) {
-                if (getOnItemViewClickedListener() != null || getOnItemClickedListener() != null
-                        || mActionClickedListener != null) {
-                    ibvh.getPresenter().setOnClickListener(ibvh.getViewHolder(), null);
-                }
-            }
-            @Override
-            public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-                // Remove first to ensure we don't add ourselves more than once.
-                viewHolder.itemView.removeOnLayoutChangeListener(mLayoutChangeListener);
-                viewHolder.itemView.addOnLayoutChangeListener(mLayoutChangeListener);
-            }
-            @Override
-            public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-                viewHolder.itemView.removeOnLayoutChangeListener(mLayoutChangeListener);
-                checkFirstAndLastPosition(false);
-            }
-        };
-
         final RecyclerView.OnScrollListener mScrollListener =
                 new RecyclerView.OnScrollListener() {
 
@@ -252,13 +283,10 @@
             mDetailsDescriptionViewHolder =
                     detailsPresenter.onCreateViewHolder(mDetailsDescriptionFrame);
             mDetailsDescriptionFrame.addView(mDetailsDescriptionViewHolder.view);
-
-            mActionBridgeAdapter.setAdapterListener(mAdapterListener);
         }
     }
 
     private final Presenter mDetailsPresenter;
-    private final ActionPresenterSelector mActionPresenterSelector;
     private OnActionClickedListener mActionClickedListener;
 
     private int mBackgroundColor = Color.TRANSPARENT;
@@ -277,7 +305,6 @@
         setHeaderPresenter(null);
         setSelectEffectEnabled(false);
         mDetailsPresenter = detailsPresenter;
-        mActionPresenterSelector = new ActionPresenterSelector();
     }
 
     /**
@@ -297,7 +324,7 @@
     /**
      * Sets the background color.  If not set, a default from the theme will be used.
      */
-    public void setBackgroundColor(int color) {
+    public void setBackgroundColor(@ColorInt int color) {
         mBackgroundColor = color;
         mBackgroundColorSet = true;
     }
@@ -306,6 +333,7 @@
      * Returns the background color.  If no background color was set, transparent
      * is returned.
      */
+    @ColorInt
     public int getBackgroundColor() {
         return mBackgroundColor;
     }
@@ -365,10 +393,13 @@
 
     private int getDefaultBackgroundColor(Context context) {
         TypedValue outValue = new TypedValue();
-        context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true);
-        return context.getResources().getColor(outValue.resourceId);
+        if (context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) {
+            return context.getResources().getColor(outValue.resourceId);
+        }
+        return context.getResources().getColor(R.color.lb_default_brand_color);
     }
 
+    @Override
     protected void onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected) {
         super.onRowViewSelected(vh, selected);
         if (selected) {
@@ -393,7 +424,8 @@
         return context.getResources().getDimensionPixelSize(resId);
     }
 
-    private void initDetailsOverview(ViewHolder vh) {
+    private void initDetailsOverview(final ViewHolder vh) {
+        vh.mActionBridgeAdapter = new ActionsItemBridgeAdapter(vh);
         final View overview = vh.mOverviewFrame;
         ViewGroup.LayoutParams lp = overview.getLayoutParams();
         lp.height = getCardHeight(overview.getContext());
@@ -402,6 +434,17 @@
         if (!getSelectEffectEnabled()) {
             vh.mOverviewFrame.setForeground(null);
         }
+        vh.mActionsRow.setOnUnhandledKeyListener(new BaseGridView.OnUnhandledKeyListener() {
+            @Override
+            public boolean onUnhandledKey(KeyEvent event) {
+                if (vh.getOnKeyListener() != null) {
+                    if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        });
     }
 
     private static int getNonNegativeWidth(Drawable drawable) {
@@ -414,12 +457,8 @@
         return (height > 0 ? height : 0);
     }
 
-    @Override
-    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
-        super.onBindRowViewHolder(holder, item);
-
-        DetailsOverviewRow row = (DetailsOverviewRow) item;
-        ViewHolder vh = (ViewHolder) holder;
+    private void bindImageDrawable(ViewHolder vh) {
+        DetailsOverviewRow row = (DetailsOverviewRow) vh.getRow();
 
         ViewGroup.MarginLayoutParams layoutParams =
                 (ViewGroup.MarginLayoutParams) vh.mImageView.getLayoutParams();
@@ -494,26 +533,33 @@
         }
         vh.mImageView.setLayoutParams(layoutParams);
         vh.mImageView.setImageDrawable(row.getImageDrawable());
-
-        mDetailsPresenter.onBindViewHolder(vh.mDetailsDescriptionViewHolder, row.getItem());
-
-        ArrayObjectAdapter aoa = new ArrayObjectAdapter(mActionPresenterSelector);
-        aoa.addAll(0, (Collection)row.getActions());
-        vh.bind(aoa);
-
         if (row.getImageDrawable() != null && mSharedElementHelper != null) {
             mSharedElementHelper.onBindToDrawable(vh);
         }
     }
 
     @Override
-    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
-        super.onUnbindRowViewHolder(holder);
+    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
+        super.onBindRowViewHolder(holder, item);
 
+        DetailsOverviewRow row = (DetailsOverviewRow) item;
         ViewHolder vh = (ViewHolder) holder;
+
+        bindImageDrawable(vh);
+        mDetailsPresenter.onBindViewHolder(vh.mDetailsDescriptionViewHolder, row.getItem());
+        vh.bindActions(row.getActionsAdapter());
+        row.addListener(vh.mListener);
+    }
+
+    @Override
+    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
+        ViewHolder vh = (ViewHolder) holder;
+        DetailsOverviewRow dor = (DetailsOverviewRow) vh.getRow();
+        dor.removeListener(vh.mListener);
         if (vh.mDetailsDescriptionViewHolder != null) {
             mDetailsPresenter.onUnbindViewHolder(vh.mDetailsDescriptionViewHolder);
         }
+        super.onUnbindRowViewHolder(holder);
     }
 
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlight.java b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlight.java
index 834b7bf..5b1ad6c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlight.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlight.java
@@ -38,4 +38,8 @@
      */
     public static final int ZOOM_FACTOR_LARGE = 3;
 
+    /**
+     * An extra small zoom factor.
+     */
+    public static final int ZOOM_FACTOR_XSMALL = 4;
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
index f6b633e..1a4ea48 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
@@ -22,6 +22,7 @@
 import android.content.res.Resources;
 import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_NONE;
 import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_SMALL;
+import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_XSMALL;
 import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_MEDIUM;
 import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_LARGE;
 
@@ -30,6 +31,26 @@
  */
 public class FocusHighlightHelper {
 
+    static boolean isValidZoomIndex(int zoomIndex) {
+        return zoomIndex == ZOOM_FACTOR_NONE || getResId(zoomIndex) > 0;
+    }
+
+    private static int getResId(int zoomIndex) {
+        switch (zoomIndex) {
+            case ZOOM_FACTOR_SMALL:
+                return R.fraction.lb_focus_zoom_factor_small;
+            case ZOOM_FACTOR_XSMALL:
+                return R.fraction.lb_focus_zoom_factor_xsmall;
+            case ZOOM_FACTOR_MEDIUM:
+                return R.fraction.lb_focus_zoom_factor_medium;
+            case ZOOM_FACTOR_LARGE:
+                return R.fraction.lb_focus_zoom_factor_large;
+            default:
+                return 0;
+        }
+    }
+
+
     static class FocusAnimator implements TimeAnimator.TimeListener {
         private final View mView;
         private final int mDuration;
@@ -112,32 +133,20 @@
     static class BrowseItemFocusHighlight implements FocusHighlightHandler {
         private static final int DURATION_MS = 150;
 
-        private static float[] sScaleFactor = new float[4];
-
         private int mScaleIndex;
         private final boolean mUseDimmer;
 
         BrowseItemFocusHighlight(int zoomIndex, boolean useDimmer) {
-            mScaleIndex = (zoomIndex >= 0 && zoomIndex < sScaleFactor.length) ?
-                    zoomIndex : ZOOM_FACTOR_MEDIUM;
+            if (!isValidZoomIndex(zoomIndex)) {
+                throw new IllegalArgumentException("Unhandled zoom index");
+            }
+            mScaleIndex = zoomIndex;
             mUseDimmer = useDimmer;
         }
 
-        private static void lazyInit(Resources resources) {
-            if (sScaleFactor[ZOOM_FACTOR_NONE] == 0f) {
-                sScaleFactor[ZOOM_FACTOR_NONE] = 1f;
-                sScaleFactor[ZOOM_FACTOR_SMALL] =
-                        resources.getFraction(R.fraction.lb_focus_zoom_factor_small, 1, 1);
-                sScaleFactor[ZOOM_FACTOR_MEDIUM] =
-                        resources.getFraction(R.fraction.lb_focus_zoom_factor_medium, 1, 1);
-                sScaleFactor[ZOOM_FACTOR_LARGE] =
-                        resources.getFraction(R.fraction.lb_focus_zoom_factor_large, 1, 1);
-            }
-        }
-
-        private float getScale(View view) {
-            lazyInit(view.getResources());
-            return sScaleFactor[mScaleIndex];
+        private float getScale(Resources res) {
+            return mScaleIndex == ZOOM_FACTOR_NONE ? 1f :
+                    res.getFraction(getResId(mScaleIndex), 1, 1);
         }
 
         @Override
@@ -154,7 +163,8 @@
         private FocusAnimator getOrCreateAnimator(View view) {
             FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator);
             if (animator == null) {
-                animator = new FocusAnimator(view, getScale(view), mUseDimmer, DURATION_MS);
+                animator = new FocusAnimator(
+                        view, getScale(view.getResources()), mUseDimmer, DURATION_MS);
                 view.setTag(R.id.lb_focus_animator, animator);
             }
             return animator;
@@ -164,8 +174,11 @@
 
     /**
      * Setup the focus highlight behavior of a focused item in browse list row.
-     * @param zoomIndex One of {@link FocusHighlight#ZOOM_FACTOR_SMALL} {@link FocusHighlight#ZOOM_FACTOR_MEDIUM}
-     * {@link FocusHighlight#ZOOM_FACTOR_LARGE} {@link FocusHighlight#ZOOM_FACTOR_NONE}.
+     * @param zoomIndex One of {@link FocusHighlight#ZOOM_FACTOR_SMALL}
+     * {@link FocusHighlight#ZOOM_FACTOR_XSMALL}
+     * {@link FocusHighlight#ZOOM_FACTOR_MEDIUM}
+     * {@link FocusHighlight#ZOOM_FACTOR_LARGE}
+     * {@link FocusHighlight#ZOOM_FACTOR_NONE}.
      * @param useDimmer Allow dimming browse item when unselected.
      * @param adapter  adapter of the list row.
      */
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Grid.java b/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
new file mode 100644
index 0000000..5ebc4a6
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2014 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.support.v4.util.CircularIntArray;
+import android.util.Log;
+
+import java.io.PrintWriter;
+
+/**
+ * A grid is representation of multiple row layout data structure and algorithm.
+ * Grid is the base class for both staggered case or simple non-staggered case.
+ * <p>
+ * User calls Grid.createStaggeredMutipleRows() to create an staggered instance.
+ * TODO add createNonStaggeredRows().
+ * To use the Grid, user must implement a Provider to create or remove visible item.
+ * Grid maintains a list of visible items.  Visible items are created when
+ * user calls appendVisibleItems() or prependVisibleItems() with certain limitation
+ * (e.g. a max edge that append up to).  Visible items are deleted when user calls
+ * removeInvisibleItemsAtEnd() or removeInvisibleItemsAtFront().  Grid's algorithm
+ * uses size of visible item returned from Provider.createItem() to decide which row
+ * to add a new visible item and may cache the algorithm results.   User must call
+ * invalidateItemsAfter() when it detects item size changed to ask Grid to remove cached
+ * results.
+ */
+abstract class Grid {
+
+    /**
+     * A constant representing a default starting index, indicating that the
+     * developer did not provide a start index.
+     */
+    public static final int START_DEFAULT = -1;
+
+    /**
+     * When user uses Grid,  he should provide count of items and
+     * the method to create item and remove item.
+     */
+    public static interface Provider {
+
+        /**
+         * Return how many items (are in the adapter).
+         */
+        public abstract int getCount();
+
+        /**
+         * Create visible item and where the provider should measure it.
+         * The call is always followed by addItem().
+         * @param index     0-based index of the item in provider
+         * @param append  True if new item is after last visible item, false if new item is
+         *                before first visible item.
+         * @param item    item[0] returns created item that will be passed in addItem() call.
+         * @return length of the item.
+         */
+        public abstract int createItem(int index, boolean append, Object[] item);
+
+        /**
+         * add item to given row and given edge.  The call is always after createItem().
+         * @param item      The object returned by createItem()
+         * @param index     0-based index of the item in provider
+         * @param length    The size of the object
+         * @param rowIndex  Row index to put the item
+         * @param edge      min_edge if not reversed or max_edge if reversed.
+         */
+        public abstract void addItem(Object item, int index, int length, int rowIndex, int edge);
+
+        /**
+         * Remove visible item at index.
+         * @param index     0-based index of the item in provider
+         */
+        public abstract void removeItem(int index);
+
+        /**
+         * Get edge of an existing visible item. edge will be the min_edge
+         * if not reversed or the max_edge if reversed.
+         * @param index     0-based index of the item in provider
+         */
+        public abstract int getEdge(int index);
+
+        /**
+         * Get size of an existing visible item.
+         * @param index     0-based index of the item in provider
+         */
+        public abstract int getSize(int index);
+    }
+
+    /**
+     * Cached representation of an item in Grid.  May be subclassed.
+     */
+    public static class Location {
+        /**
+         * The index of the row for this Location.
+         */
+        public int row;
+
+        public Location(int row) {
+            this.row = row;
+        }
+    }
+
+    protected Provider mProvider;
+    protected boolean mReversedFlow;
+    protected int mMargin;
+    protected int mNumRows;
+    protected int mFirstVisibleIndex = -1;
+    protected int mLastVisibleIndex = -1;
+
+    protected CircularIntArray[] mTmpItemPositionsInRows;
+
+    // the first index that grid will layout
+    protected int mStartIndex = START_DEFAULT;
+
+    /**
+     * Creates a multiple rows staggered grid.
+     */
+    public static Grid createStaggeredMultipleRows(int rows) {
+        StaggeredGridDefault grid = new StaggeredGridDefault();
+        grid.setNumRows(rows);
+        return grid;
+    }
+
+    /**
+     * Sets the margin between items in a row
+     */
+    public final void setMargin(int margin) {
+        mMargin = margin;
+    }
+
+    /**
+     * Sets if reversed flow (rtl)
+     */
+    public final void setReversedFlow(boolean reversedFlow) {
+        mReversedFlow = reversedFlow;
+    }
+
+    /**
+     * Returns true if reversed flow (rtl)
+     */
+    public boolean isReversedFlow() {
+        return mReversedFlow;
+    }
+
+    /**
+     * Sets the {@link Provider} for this grid.
+     *
+     * @param provider The provider for this grid.
+     */
+    public void setProvider(Provider provider) {
+        mProvider = provider;
+    }
+
+    /**
+     * Sets the first item index to create when there are no items.
+     *
+     * @param startIndex the index of the first item
+     */
+    public void setStart(int startIndex) {
+        mStartIndex = startIndex;
+    }
+
+    /**
+     * Returns the number of rows in the grid.
+     */
+    public int getNumRows() {
+        return mNumRows;
+    }
+
+    /**
+     * Sets number of rows to fill into. For views that represent a
+     * horizontal list, this will be the rows of the view. For views that
+     * represent a vertical list, this will be the columns.
+     *
+     * @param numRows numberOfRows
+     */
+    void setNumRows(int numRows) {
+        if (numRows <= 0) {
+            throw new IllegalArgumentException();
+        }
+        if (mNumRows == numRows) {
+            return;
+        }
+        mNumRows = numRows;
+        mTmpItemPositionsInRows = new CircularIntArray[mNumRows];
+        for (int i = 0; i < mNumRows; i++) {
+            mTmpItemPositionsInRows[i] = new CircularIntArray();
+        }
+    }
+
+    /**
+     * Returns index of first visible item in the staggered grid.  Returns negative value
+     * if no visible item.
+     */
+    public final int getFirstVisibleIndex() {
+        return mFirstVisibleIndex;
+    }
+
+    /**
+     * Returns index of last visible item in the staggered grid.  Returns negative value
+     * if no visible item.
+     */
+    public final int getLastVisibleIndex() {
+        return mLastVisibleIndex;
+    }
+
+    /**
+     * Reset visible indices and keep cache (if exists)
+     */
+    public void resetVisibleIndex() {
+        mFirstVisibleIndex = mLastVisibleIndex = -1;
+    }
+
+    /**
+     * Invalidate items after or equal to index. This will remove visible items
+     * after that and invalidate cache of layout results after that.
+     */
+    public void invalidateItemsAfter(int index) {
+        if (index < 0) {
+            return;
+        }
+        if (mLastVisibleIndex < 0) {
+            return;
+        }
+        while (mLastVisibleIndex >= index) {
+            mProvider.removeItem(mLastVisibleIndex);
+            mLastVisibleIndex--;
+        }
+        resetVisbileIndexIfEmpty();
+        if (getFirstVisibleIndex() < 0) {
+            setStart(index);
+        }
+    }
+
+    /**
+     * Gets the row index of item at given index.
+     */
+    public final int getRowIndex(int index) {
+        return getLocation(index).row;
+    }
+
+    /**
+     * Gets {@link Location} of item.  The return object is read only and temporarily.
+     */
+    public abstract Location getLocation(int index);
+
+    /**
+     * Finds the largest or smallest row min edge of visible items,
+     * the row index is returned in indices[0], the item index is returned in indices[1].
+     */
+    public final int findRowMin(boolean findLarge, int[] indices) {
+        return findRowMin(findLarge, mReversedFlow ? mLastVisibleIndex : mFirstVisibleIndex,
+                indices);
+    }
+
+    /**
+     * Finds the largest or smallest row min edge of visible items, starts searching from
+     * indexLimit, the row index is returned in indices[0], the item index is returned in indices[1].
+     */
+    protected abstract int findRowMin(boolean findLarge, int indexLimit, int[] rowIndex);
+
+    /**
+     * Finds the largest or smallest row max edge of visible items, the row index is returned in
+     * indices[0], the item index is returned in indices[1].
+     */
+    public final int findRowMax(boolean findLarge, int[] indices) {
+        return findRowMax(findLarge, mReversedFlow ? mFirstVisibleIndex : mLastVisibleIndex,
+                indices);
+    }
+
+    /**
+     * Find largest or smallest row max edge of visible items, starts searching from indexLimit,
+     * the row index is returned in indices[0], the item index is returned in indices[1].
+     */
+    protected abstract int findRowMax(boolean findLarge, int indexLimit, int[] indices);
+
+    /**
+     * Returns true if appending item has reached "toLimit"
+     */
+    protected final boolean checkAppendOverLimit(int toLimit) {
+        if (mLastVisibleIndex < 0) {
+            return false;
+        }
+        return mReversedFlow ? findRowMin(true, null) <= toLimit + mMargin :
+                    findRowMax(false, null) >= toLimit - mMargin;
+    }
+
+    /**
+     * Returns true if prepending item has reached "toLimit"
+     */
+    protected final boolean checkPrependOverLimit(int toLimit) {
+        if (mLastVisibleIndex < 0) {
+            return false;
+        }
+        return mReversedFlow ? findRowMax(false, null) >= toLimit + mMargin :
+                    findRowMin(true, null) <= toLimit - mMargin;
+    }
+
+    /**
+     * Return array of int array for all rows, each int array contains visible item positions
+     * in pair on that row between startPos(included) and endPositions(included).
+     * Returned value is read only, do not change it.
+     * <p>
+     * E.g. First row has 3,7,8, second row has 4,5,6.
+     * getItemPositionsInRows(3, 8) returns { {3,3,7,8}, {4,6} }
+     */
+    public abstract CircularIntArray[] getItemPositionsInRows(int startPos, int endPos);
+
+    /**
+     * Return array of int array for all rows, each int array contains visible item positions
+     * in pair on that row.
+     * Returned value is read only, do not change it.
+     * <p>
+     * E.g. First row has 3,7,8, second row has 4,5,6  { {3,3,7,8}, {4,6} }
+     */
+    public final CircularIntArray[] getItemPositionsInRows() {
+        return getItemPositionsInRows(getFirstVisibleIndex(), getLastVisibleIndex());
+    }
+
+    /**
+     * Prepends items and stops after one column is filled.
+     * (i.e. filled items from row 0 to row mNumRows - 1)
+     * @return true if at least one item is filled.
+     */
+    public final boolean prependOneColumnVisibleItems() {
+        return prependVisibleItems(mReversedFlow ? Integer.MIN_VALUE : Integer.MAX_VALUE, true);
+    }
+
+    /**
+     * Prepends items until first item or reaches toLimit (min edge when not reversed or
+     * max edge when reversed)
+     */
+    public final void prependVisibleItems(int toLimit) {
+        prependVisibleItems(toLimit, false);
+    }
+
+    /**
+     * Prepends items until first item or reaches toLimit (min edge when not reversed or
+     * max edge when reversed).
+     * @param oneColumnMode  true when fills one column and stops,  false
+     * when checks if condition matches before filling first column.
+     * @return true if at least one item is filled.
+     */
+    protected abstract boolean prependVisibleItems(int toLimit, boolean oneColumnMode);
+
+    /**
+     * Appends items and stops after one column is filled.
+     * (i.e. filled items from row 0 to row mNumRows - 1)
+     * @return true if at least one item is filled.
+     */
+    public boolean appendOneColumnVisibleItems() {
+        return appendVisibleItems(mReversedFlow ? Integer.MAX_VALUE : Integer.MIN_VALUE, true);
+    }
+
+    /**
+     * Append items until last item or reaches toLimit (max edge when not
+     * reversed or min edge when reversed)
+     */
+    public final void appendVisibleItems(int toLimit) {
+        appendVisibleItems(toLimit, false);
+    }
+
+    /**
+     * Appends items until last or reaches toLimit (high edge when not
+     * reversed or low edge when reversed).
+     * @param oneColumnMode True when fills one column and stops,  false
+     * when checks if condition matches before filling first column.
+     * @return true if filled at least one item
+     */
+    protected abstract boolean appendVisibleItems(int toLimit, boolean oneColumnMode);
+
+    /**
+     * Removes invisible items from end until reaches item at aboveIndex or toLimit.
+     */
+    public void removeInvisibleItemsAtEnd(int aboveIndex, int toLimit) {
+        while(mLastVisibleIndex >= mFirstVisibleIndex && mLastVisibleIndex > aboveIndex) {
+            boolean offEnd = !mReversedFlow ? mProvider.getEdge(mLastVisibleIndex) >= toLimit
+                    : mProvider.getEdge(mLastVisibleIndex) <= toLimit;
+            if (offEnd) {
+                mProvider.removeItem(mLastVisibleIndex);
+                mLastVisibleIndex--;
+            } else {
+                break;
+            }
+        }
+        resetVisbileIndexIfEmpty();
+    }
+
+    /**
+     * Removes invisible items from front until reaches item at belowIndex or toLimit.
+     */
+    public void removeInvisibleItemsAtFront(int belowIndex, int toLimit) {
+        while(mLastVisibleIndex >= mFirstVisibleIndex && mFirstVisibleIndex < belowIndex) {
+            boolean offFront = !mReversedFlow ? mProvider.getEdge(mFirstVisibleIndex)
+                    + mProvider.getSize(mFirstVisibleIndex) <= toLimit
+                    : mProvider.getEdge(mFirstVisibleIndex)
+                            - mProvider.getSize(mFirstVisibleIndex) >= toLimit;
+            if (offFront) {
+                mProvider.removeItem(mFirstVisibleIndex);
+                mFirstVisibleIndex++;
+            } else {
+                break;
+            }
+        }
+        resetVisbileIndexIfEmpty();
+    }
+
+    private void resetVisbileIndexIfEmpty() {
+        if (mLastVisibleIndex < mFirstVisibleIndex) {
+            resetVisibleIndex();
+        }
+    }
+
+    public abstract void debugPrint(PrintWriter pw);
+}
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 e32e30f..3858b34 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -19,11 +19,13 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.v4.util.CircularIntArray;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.widget.LinearSmoothScroller;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.Recycler;
 import android.support.v7.widget.RecyclerView.State;
+import android.support.v17.leanback.os.TraceHelper;
 
 import static android.support.v7.widget.RecyclerView.NO_ID;
 import static android.support.v7.widget.RecyclerView.NO_POSITION;
@@ -164,8 +166,219 @@
         }
     }
 
+    /**
+     * Base class which scrolls to selected view in onStop().
+     */
+    abstract class GridLinearSmoothScroller extends LinearSmoothScroller {
+        GridLinearSmoothScroller() {
+            super(mBaseGridView.getContext());
+        }
+
+        @Override
+        protected void onStop() {
+            // onTargetFound() may not be called if we hit the "wall" first.
+            View targetView = findViewByPosition(getTargetPosition());
+            if (hasFocus() && targetView != null) {
+                mInSelection = true;
+                targetView.requestFocus();
+                mInSelection = false;
+            }
+            if (needsDispatchChildSelectedOnStop()) {
+                dispatchChildSelected();
+            }
+            super.onStop();
+        }
+
+        boolean needsDispatchChildSelectedOnStop() {
+            return true;
+        }
+
+        @Override
+        protected void onTargetFound(View targetView,
+                RecyclerView.State state, Action action) {
+            if (getScrollPosition(targetView, sTwoInts)) {
+                int dx, dy;
+                if (mOrientation == HORIZONTAL) {
+                    dx = sTwoInts[0];
+                    dy = sTwoInts[1];
+                } else {
+                    dx = sTwoInts[1];
+                    dy = sTwoInts[0];
+                }
+                final int distance = (int) Math.sqrt(dx * dx + dy * dy);
+                final int time = calculateTimeForDeceleration(distance);
+                action.update(dx, dy, time, mDecelerateInterpolator);
+            }
+        }
+    }
+
+    /**
+     * The SmoothScroller that remembers pending DPAD keys and consume pending keys
+     * during scroll.
+     */
+    final class PendingMoveSmoothScroller extends GridLinearSmoothScroller {
+        // -2 is a target position that LinearSmoothScroller can never find until
+        // consumePendingMovesXXX() sets real targetPosition.
+        final static int TARGET_UNDEFINED = -2;
+        // whether the grid is staggered.
+        private final boolean mStaggeredGrid;
+        // Number of pending movements on primary direction, negative if PREV_ITEM.
+        private int mPendingMoves;
+
+        PendingMoveSmoothScroller(int initialPendingMoves, boolean staggeredGrid) {
+            mPendingMoves = initialPendingMoves;
+            mStaggeredGrid = staggeredGrid;
+            setTargetPosition(TARGET_UNDEFINED);
+        }
+
+        void increasePendingMoves() {
+            if (mPendingMoves < MAX_PENDING_MOVES) {
+                mPendingMoves++;
+                if (mPendingMoves == 0) {
+                    dispatchChildSelected();
+                }
+            }
+        }
+
+        void decreasePendingMoves() {
+            if (mPendingMoves > -MAX_PENDING_MOVES) {
+                mPendingMoves--;
+                if (mPendingMoves == 0) {
+                    dispatchChildSelected();
+                }
+            }
+        }
+
+        /**
+         * Called before laid out an item when non-staggered grid can handle pending movements
+         * by skipping "mNumRows" per movement;  staggered grid will have to wait the item
+         * has been laid out in consumePendingMovesAfterLayout().
+         */
+        void consumePendingMovesBeforeLayout() {
+            if (mStaggeredGrid || mPendingMoves == 0) {
+                return;
+            }
+            View newSelected = null;
+            int startPos = mPendingMoves > 0 ? mFocusPosition + mNumRows :
+                    mFocusPosition - mNumRows;
+            for (int pos = startPos; mPendingMoves != 0;
+                    pos = mPendingMoves > 0 ? pos + mNumRows: pos - mNumRows) {
+                View v = findViewByPosition(pos);
+                if (v == null) {
+                    break;
+                }
+                if (!canScrollTo(v)) {
+                    continue;
+                }
+                newSelected = v;
+                mFocusPosition = pos;
+                if (mPendingMoves > 0) {
+                    mPendingMoves--;
+                } else {
+                    mPendingMoves++;
+                }
+            }
+            if (newSelected != null && hasFocus()) {
+                mInSelection = true;
+                newSelected.requestFocus();
+                mInSelection = false;
+            }
+        }
+
+        /**
+         * Called after laid out an item.  Staggered grid should find view on same
+         * Row and consume pending movements.
+         */
+        void consumePendingMovesAfterLayout() {
+            if (mStaggeredGrid && mPendingMoves != 0) {
+                // consume pending moves, focus to item on the same row.
+                final int focusedRow = mGrid != null && mFocusPosition != NO_POSITION ?
+                        mGrid.getLocation(mFocusPosition).row : NO_POSITION;
+                View newSelected = null;
+                for (int i = 0, count = getChildCount(); i < count && mPendingMoves != 0; i++) {
+                    int index = mPendingMoves > 0 ? i : count - 1 - i;
+                    final View child = getChildAt(index);
+                    if (!canScrollTo(child)) {
+                        continue;
+                    }
+                    int position = getPositionByIndex(index);
+                    Grid.Location loc = mGrid.getLocation(position);
+                    if (focusedRow == NO_POSITION || (loc != null && loc.row == focusedRow)) {
+                        if (mFocusPosition == NO_POSITION) {
+                            mFocusPosition = position;
+                            newSelected = child;
+                        } else if ((mPendingMoves > 0 && position > mFocusPosition)
+                                || (mPendingMoves < 0 && position < mFocusPosition)) {
+                            mFocusPosition = position;
+                            if (mPendingMoves > 0) {
+                                mPendingMoves--;
+                            } else {
+                                mPendingMoves++;
+                            }
+                            newSelected = child;
+                        }
+                    }
+                }
+                if (newSelected != null && hasFocus()) {
+                    mInSelection = true;
+                    newSelected.requestFocus();
+                    mInSelection = false;
+                }
+                if (mPendingMoves == 0) {
+                    dispatchChildSelected();
+                }
+            }
+            if (mPendingMoves == 0 || (mPendingMoves > 0 && hasCreatedLastItem())
+                    || (mPendingMoves < 0 && hasCreatedFirstItem())) {
+                setTargetPosition(mFocusPosition);
+                stop();
+            }
+        }
+
+        @Override
+        protected void updateActionForInterimTarget(Action action) {
+            if (mPendingMoves == 0) {
+                return;
+            }
+            super.updateActionForInterimTarget(action);
+        }
+
+        @Override
+        public PointF computeScrollVectorForPosition(int targetPosition) {
+            if (mPendingMoves == 0) {
+                return null;
+            }
+            int direction = (mReverseFlowPrimary ? mPendingMoves > 0 : mPendingMoves < 0) ?
+                    -1 : 1;
+            if (mOrientation == HORIZONTAL) {
+                return new PointF(direction, 0);
+            } else {
+                return new PointF(0, direction);
+            }
+        }
+
+        @Override
+        boolean needsDispatchChildSelectedOnStop() {
+            return mPendingMoves != 0;
+        }
+
+        @Override
+        protected void onStop() {
+            super.onStop();
+            // if we hit wall,  need clear the remaining pending moves.
+            mPendingMoves = 0;
+            mPendingMoveSmoothScroller = null;
+            View v = findViewByPosition(getTargetPosition());
+            if (v != null) scrollToView(v, true);
+        }
+    };
+
     private static final String TAG = "GridLayoutManager";
     private static final boolean DEBUG = false;
+    private static final boolean TRACE = false;
+
+    // maximum pending movement in one direction.
+    private final static int MAX_PENDING_MOVES = 10;
 
     private String getTag() {
         return TAG + ":" + mBaseGridView.getId();
@@ -206,10 +419,18 @@
     private RecyclerView.Recycler mRecycler;
 
     private boolean mInLayout = false;
+    private boolean mInFastRelayout;
+    /**
+     * During full layout pass, when GridView had focus: onLayoutChildren will
+     * skip non-focusable child and adjust mFocusPosition.
+     */
+    private boolean mInLayoutSearchFocus;
     private boolean mInSelection = false;
 
     private OnChildSelectedListener mChildSelectedListener = null;
 
+    private OnChildLaidOutListener mChildLaidOutListener = null;
+
     /**
      * The focused position, it's not the currently visually aligned position
      * but it is the final position that we intend to focus on. If there are
@@ -218,6 +439,11 @@
     private int mFocusPosition = NO_POSITION;
 
     /**
+     * LinearSmoothScroller that consume pending DPAD movements.
+     */
+    private PendingMoveSmoothScroller mPendingMoveSmoothScroller;
+
+    /**
      * The offset to be applied to mFocusPosition, due to adapter change, on the next
      * layout.  Set to Integer.MIN_VALUE means item was removed.
      * TODO:  This is somewhat duplication of RecyclerView getOldPosition() which is
@@ -226,7 +452,12 @@
     private int mFocusPositionOffset = 0;
 
     /**
-     * Force a full layout under certain situations.
+     * Extra pixels applied on primary direction.
+     */
+    private int mPrimaryScrollExtra;
+
+    /**
+     * Force a full layout under certain situations.  E.g. Rows change, jump to invisible child.
      */
     private boolean mForceFullLayout;
 
@@ -305,22 +536,9 @@
     private int mNumRowsRequested = 1;
 
     /**
-     * Tracking start/end position of each row for visible items.
-     */
-    private StaggeredGrid.Row[] mRows;
-
-    /**
      * Saves grid information of each view.
      */
-    private StaggeredGrid mGrid;
-    /**
-     * Position of first item (included) that has attached views.
-     */
-    private int mFirstVisiblePos;
-    /**
-     * Position of last item (included) that has attached views.
-     */
-    private int mLastVisiblePos;
+    Grid mGrid;
 
     /**
      * Focus Scroll strategy.
@@ -367,7 +585,10 @@
      */
     private boolean mScrollEnabled = true;
 
-    private int[] mTempDeltas = new int[2];
+    /**
+     * Temporary variable: an int array of length=2.
+     */
+    private static int[] sTwoInts = new int[2];
 
     /**
      * Set to true for RTL layout in horizontal orientation
@@ -544,6 +765,10 @@
         mChildSelectedListener = listener;
     }
 
+    void setOnChildLaidOutListener(OnChildLaidOutListener listener) {
+        mChildLaidOutListener = listener;
+    }
+
     private int getPositionByView(View view) {
         if (view == null) {
             return NO_POSITION;
@@ -565,6 +790,7 @@
             return;
         }
 
+        if (TRACE) TraceHelper.beginSection("onChildSelected");
         View view = mFocusPosition == NO_POSITION ? null : findViewByPosition(mFocusPosition);
         if (view != null) {
             RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(view);
@@ -573,6 +799,7 @@
         } else {
             mChildSelectedListener.onChildSelected(mBaseGridView, null, NO_POSITION, NO_ID);
         }
+        if (TRACE) TraceHelper.endSection();
 
         // Children may request layout when a child selection event occurs (such as a change of
         // padding on the current and previously selected rows).
@@ -701,131 +928,59 @@
      * Re-initialize data structures for a data change or handling invisible
      * selection. The method tries its best to preserve position information so
      * that staggered grid looks same before and after re-initialize.
-     * @param focusPosition The initial focusPosition that we would like to
-     *        focus on.
-     * @return Actual position that can be focused on.
+     * @return true if can fastRelayout()
      */
-    private int init(int focusPosition) {
-
+    private boolean layoutInit() {
+        boolean focusViewWasInTree = mGrid != null && mFocusPosition >= 0
+                && mFocusPosition >= mGrid.getFirstVisibleIndex()
+                && mFocusPosition <= mGrid.getLastVisibleIndex();
         final int newItemCount = mState.getItemCount();
-
-        // Force the re-init path in the following conditional
         if (newItemCount == 0) {
-            focusPosition = NO_POSITION;
-        } else if (focusPosition == NO_POSITION && newItemCount > 0) {
+            mFocusPosition = NO_POSITION;
+        } else if (mFocusPosition >= newItemCount) {
+            mFocusPosition = newItemCount - 1;
+        } else if (mFocusPosition == NO_POSITION && newItemCount > 0) {
             // if focus position is never set before,  initialize it to 0
-            focusPosition = 0;
+            mFocusPosition = 0;
         }
-
-        // If adapter has changed then caches are invalid; otherwise,
-        // we try to maintain each row's position if number of rows keeps the same
-        // and existing mGrid contains the focusPosition.
-        if (mRows != null && mNumRows == mRows.length &&
-                mGrid != null && mGrid.getSize() > 0 && focusPosition >= 0 &&
-                focusPosition >= mGrid.getFirstIndex() &&
-                focusPosition <= mGrid.getLastIndex()) {
-            // strip mGrid to a subset (like a column) that contains focusPosition
-            mGrid.stripDownTo(focusPosition);
-            // make sure that remaining items do not exceed new adapter size
-            int firstIndex = mGrid.getFirstIndex();
-            int lastIndex = mGrid.getLastIndex();
-            if (DEBUG) {
-                Log .v(getTag(), "mGrid firstIndex " + firstIndex + " lastIndex " + lastIndex);
-            }
-            for (int i = lastIndex; i >=firstIndex; i--) {
-                if (i >= newItemCount) {
-                    mGrid.removeLast();
-                }
-            }
-            if (mGrid.getSize() == 0) {
-                focusPosition = newItemCount - 1;
-                // initialize row start locations
-                for (int i = 0; i < mNumRows; i++) {
-                    mRows[i].low = 0;
-                    mRows[i].high = 0;
-                }
-                if (DEBUG) Log.v(getTag(), "mGrid zero size");
-            } else {
-                // initialize row start locations
-                for (int i = 0; i < mNumRows; i++) {
-                    mRows[i].low = Integer.MAX_VALUE;
-                    mRows[i].high = Integer.MIN_VALUE;
-                }
-                firstIndex = mGrid.getFirstIndex();
-                lastIndex = mGrid.getLastIndex();
-                if (focusPosition > lastIndex) {
-                    focusPosition = mGrid.getLastIndex();
-                }
-                if (DEBUG) {
-                    Log.v(getTag(), "mGrid firstIndex " + firstIndex + " lastIndex "
-                        + lastIndex + " focusPosition " + focusPosition);
-                }
-                // fill rows with minimal view positions of the subset
-                for (int i = firstIndex; i <= lastIndex; i++) {
-                    View v = findViewByPosition(i);
-                    if (v == null) {
-                        continue;
-                    }
-                    int row = mGrid.getLocation(i).row;
-                    int low = getViewMin(v) + mScrollOffsetPrimary;
-                    if (low < mRows[row].low) {
-                        mRows[row].low = mRows[row].high = low;
-                    }
-                }
-                int firstItemRowPosition = mRows[mGrid.getLocation(firstIndex).row].low;
-                if (firstItemRowPosition == Integer.MAX_VALUE) {
-                    firstItemRowPosition = 0;
-                }
-                if (mState.didStructureChange()) {
-                    // if there is structure change, the removed item might be in the
-                    // subset,  so it is meaningless to maintain the low locations.
-                    for (int i = 0; i < mNumRows; i++) {
-                        mRows[i].low = firstItemRowPosition;
-                        mRows[i].high = firstItemRowPosition;
-                    }
-                } else {
-                    // fill other rows that does not include the subset using first item
-                    for (int i = 0; i < mNumRows; i++) {
-                        if (mRows[i].low == Integer.MAX_VALUE) {
-                            mRows[i].low = mRows[i].high = firstItemRowPosition;
-                        }
-                    }
-                }
-            }
-
-            // Same adapter, we can reuse any attached views
-            detachAndScrapAttachedViews(mRecycler);
+        if (!mState.didStructureChange() && !mForceFullLayout && mGrid != null) {
             updateScrollController();
-
+            updateScrollSecondAxis();
+            mGrid.setMargin(mMarginPrimary);
+            if (!focusViewWasInTree && mFocusPosition != NO_POSITION) {
+                mGrid.setStart(mFocusPosition);
+            }
+            return true;
         } else {
-            // otherwise recreate data structure
-            mRows = new StaggeredGrid.Row[mNumRows];
+            mForceFullLayout = false;
+            int firstVisibleIndex = focusViewWasInTree ? mGrid.getFirstVisibleIndex() : 0;
 
-            for (int i = 0; i < mNumRows; i++) {
-                mRows[i] = new StaggeredGrid.Row();
+            if (mGrid == null || mNumRows != mGrid.getNumRows() ||
+                    mReverseFlowPrimary != mGrid.isReversedFlow()) {
+                mGrid = Grid.createStaggeredMultipleRows(mNumRows);
+                mGrid.setProvider(mGridProvider);
+                mGrid.setReversedFlow(mReverseFlowPrimary);
             }
-            mGrid = new StaggeredGridDefault();
-            mGrid.setReversedFlow(mOrientation == HORIZONTAL && mReverseFlowPrimary);
-            if (newItemCount == 0) {
-                focusPosition = NO_POSITION;
-            } else if (focusPosition >= newItemCount) {
-                focusPosition = newItemCount - 1;
-            }
-
-            // Adapter may have changed so remove all attached views permanently
-            removeAndRecycleAllViews(mRecycler);
-
             initScrollController();
+            updateScrollSecondAxis();
+            mGrid.setMargin(mMarginPrimary);
+            detachAndScrapAttachedViews(mRecycler);
+            mGrid.resetVisibleIndex();
+            if (mFocusPosition == NO_POSITION) {
+                mBaseGridView.clearFocus();
+            }
+            mWindowAlignment.mainAxis().invalidateScrollMin();
+            mWindowAlignment.mainAxis().invalidateScrollMax();
+            if (focusViewWasInTree && firstVisibleIndex <= mFocusPosition) {
+                // if focusView was in tree, we will add item from first visible item
+                mGrid.setStart(firstVisibleIndex);
+            } else {
+                // if focusView was not in tree, it's probably because focus position jumped
+                // far away from visible range,  so use mFocusPosition as start
+                mGrid.setStart(mFocusPosition);
+            }
+            return false;
         }
-
-        mGrid.setProvider(mGridProvider);
-        // mGrid share the same Row array information
-        mGrid.setRows(mRows);
-        mFirstVisiblePos = mLastVisiblePos = NO_POSITION;
-
-        updateScrollSecondAxis();
-
-        return focusPosition;
     }
 
     private int getRowSizeSecondary(int rowIndex) {
@@ -880,30 +1035,33 @@
             return false;
         }
 
-        List<Integer>[] rows = mGrid == null ? null :
-            mGrid.getItemPositionsInRows(mFirstVisiblePos, mLastVisiblePos);
+        if (TRACE) TraceHelper.beginSection("processRowSizeSecondary");
+        CircularIntArray[] rows = mGrid == null ? null : mGrid.getItemPositionsInRows();
         boolean changed = false;
         int scrapChildWidth = -1;
         int scrapChildHeight = -1;
 
         for (int rowIndex = 0; rowIndex < mNumRows; rowIndex++) {
-            final int rowItemCount = rows == null ? 0 : rows[rowIndex].size();
-            if (DEBUG) Log.v(getTag(), "processRowSizeSecondary row " + rowIndex +
-                    " rowItemCount " + rowItemCount);
-
+            CircularIntArray row = rows == null ? null : rows[rowIndex];
+            final int rowItemsPairCount = row == null ? 0 : row.size();
             int rowSize = -1;
-            for (int i = 0; i < rowItemCount; i++) {
-                final View view = findViewByPosition(rows[rowIndex].get(i));
-                if (view == null) {
-                    continue;
-                }
-                if (measure && view.isLayoutRequested()) {
-                    measureChild(view);
-                }
-                final int secondarySize = mOrientation == HORIZONTAL ?
-                        view.getMeasuredHeight() : view.getMeasuredWidth();
-                if (secondarySize > rowSize) {
-                    rowSize = secondarySize;
+            for (int rowItemPairIndex = 0; rowItemPairIndex < rowItemsPairCount;
+                    rowItemPairIndex += 2) {
+                final int rowIndexStart = row.get(rowItemPairIndex);
+                final int rowIndexEnd = row.get(rowItemPairIndex + 1);
+                for (int i = rowIndexStart; i <= rowIndexEnd; i++) {
+                    final View view = findViewByPosition(i);
+                    if (view == null) {
+                        continue;
+                    }
+                    if (measure && view.isLayoutRequested()) {
+                        measureChild(view);
+                    }
+                    final int secondarySize = mOrientation == HORIZONTAL ?
+                            view.getMeasuredHeight() : view.getMeasuredWidth();
+                    if (secondarySize > rowSize) {
+                        rowSize = secondarySize;
+                    }
                 }
             }
 
@@ -929,23 +1087,18 @@
                 }
                 rowSize = mOrientation == HORIZONTAL ? scrapChildHeight : scrapChildWidth;
             }
-
             if (rowSize < 0) {
                 rowSize = 0;
             }
-
-            if (DEBUG) Log.v(getTag(), "row " + rowIndex + " rowItemCount " + rowItemCount +
-                    " rowSize " + rowSize);
-
             if (mRowSizeSecondary[rowIndex] != rowSize) {
                 if (DEBUG) Log.v(getTag(), "row size secondary changed: " + mRowSizeSecondary[rowIndex] +
                         ", " + rowSize);
-
                 mRowSizeSecondary[rowIndex] = rowSize;
                 changed = true;
             }
         }
 
+        if (TRACE) TraceHelper.endSection();
         return changed;
     }
 
@@ -1087,11 +1240,11 @@
                     " mFixedRowSizeSecondary " + mFixedRowSizeSecondary +
                     " mNumRows " + mNumRows);
         }
-
         leaveContext();
     }
 
     private void measureChild(View child) {
+        if (TRACE) TraceHelper.beginSection("measureChild");
         final ViewGroup.LayoutParams lp = child.getLayoutParams();
         final int secondarySpec = (mRowSizeSecondaryRequested == ViewGroup.LayoutParams.WRAP_CONTENT) ?
                 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) :
@@ -1109,18 +1262,17 @@
                     0, lp.height);
             widthSpec = ViewGroup.getChildMeasureSpec(secondarySpec, 0, lp.width);
         }
-
         child.measure(widthSpec, heightSpec);
-
         if (DEBUG) Log.v(getTag(), "measureChild secondarySpec " + Integer.toHexString(secondarySpec) +
                 " widthSpec " + Integer.toHexString(widthSpec) +
                 " heightSpec " + Integer.toHexString(heightSpec) +
                 " measuredWidth " + child.getMeasuredWidth() +
                 " measuredHeight " + child.getMeasuredHeight());
         if (DEBUG) Log.v(getTag(), "child lp width " + lp.width + " height " + lp.height);
+        if (TRACE) TraceHelper.endSection();
     }
 
-    private StaggeredGrid.Provider mGridProvider = new StaggeredGrid.Provider() {
+    private Grid.Provider mGridProvider = new Grid.Provider() {
 
         @Override
         public int getCount() {
@@ -1128,112 +1280,138 @@
         }
 
         @Override
-        public void createItem(int index, int rowIndex, boolean append) {
+        public int createItem(int index, boolean append, Object[] item) {
+            if (TRACE) TraceHelper.beginSection("createItem");
+            if (TRACE) TraceHelper.beginSection("getview");
             View v = getViewForPosition(index);
-            if (mFirstVisiblePos >= 0) {
-                // when StaggeredGrid append or prepend item, we must guarantee
-                // that sibling item has created views already.
-                if (append && index != mLastVisiblePos + 1) {
-                    throw new RuntimeException();
-                } else if (!append && index != mFirstVisiblePos - 1) {
-                    throw new RuntimeException();
-                }
-            }
-
+            if (TRACE) TraceHelper.endSection();
             // See recyclerView docs:  we don't need re-add scraped view if it was removed.
             if (!((RecyclerView.LayoutParams) v.getLayoutParams()).isItemRemoved()) {
+                if (TRACE) TraceHelper.beginSection("addView");
                 if (append) {
                     addView(v);
                 } else {
                     addView(v, 0);
                 }
+                if (TRACE) TraceHelper.endSection();
                 if (mChildVisibility != -1) {
                     v.setVisibility(mChildVisibility);
                 }
 
-                // View is added first or it won't be found by dispatchChildSelected.
-                if (mInLayout && index == mFocusPosition) {
-                    dispatchChildSelected();
+                if (mPendingMoveSmoothScroller != null) {
+                    mPendingMoveSmoothScroller.consumePendingMovesBeforeLayout();
                 }
-
+                if (!mInLayout) {
+                    // when we are appending item during scroll pass and the item's position
+                    // matches the mFocusPosition,  we should signal a childSelected event.
+                    // However if we are still running PendingMoveSmoothScroller,  we defer and
+                    // signal the event in PendingMoveSmoothScroller.onStop().  This can
+                    // avoid lots of childSelected events during a long smooth scrolling and
+                    // increase performance.
+                    if (index == mFocusPosition && (mPendingMoveSmoothScroller == null
+                            || mPendingMoveSmoothScroller.mPendingMoves == 0)) {
+                        dispatchChildSelected();
+                    }
+                } else if (!mInFastRelayout) {
+                    // fastRelayout will dispatch event at end of onLayoutChildren().
+                    // For full layout, two situations here:
+                    // 1. mInLayoutSearchFocus is false, dispatchChildSelected() at mFocusPosition.
+                    // 2. mInLayoutSearchFocus is true:  dispatchChildSelected() on first child
+                    //    equal to or after mFocusPosition that can take focus.
+                    if (!mInLayoutSearchFocus && index == mFocusPosition) {
+                        dispatchChildSelected();
+                    } else if (mInLayoutSearchFocus && index >= mFocusPosition
+                            && v.hasFocusable()) {
+                        mFocusPosition = index;
+                        mInLayoutSearchFocus = false;
+                        dispatchChildSelected();
+                    }
+                }
                 measureChild(v);
             }
+            item[0] = v;
+            return mOrientation == HORIZONTAL ? v.getMeasuredWidth() : v.getMeasuredHeight();
+        }
 
-            int length = mOrientation == HORIZONTAL ? v.getMeasuredWidth() : v.getMeasuredHeight();
+        @Override
+        public void addItem(Object item, int index, int length, int rowIndex, int edge) {
+            View v = (View) item;
             int start, end;
-            final boolean rowIsEmpty = mRows[rowIndex].high == mRows[rowIndex].low;
-            boolean addHigh = (!mReverseFlowPrimary) ? append : !append;
-            int lowVisiblePos = (!mReverseFlowPrimary) ? mFirstVisiblePos : mLastVisiblePos;
-            int highVisiblePos = (!mReverseFlowPrimary) ? mLastVisiblePos : mFirstVisiblePos;
-            if (addHigh) {
-                if (!rowIsEmpty) {
-                    // if there are existing item in the row,  add margin between
-                    start = mRows[rowIndex].high + mMarginPrimary;
-                } else {
-                    if (lowVisiblePos >= 0) {
-                        int rowOfLowPos = mGrid.getLocation(lowVisiblePos).row;
-                        // If row is after row of lowest position,
-                        // start a new column after the first row.
-                        if (rowIndex < rowOfLowPos) {
-                            mRows[rowIndex].low = mRows[rowOfLowPos].high + mMarginPrimary;
-                        }
-                    }
-                    start = mRows[rowIndex].low;
-                }
-                end = start + length;
-                mRows[rowIndex].high = end;
-            } else {
-                if (!rowIsEmpty) {
-                    // if there are existing item in the row,  add margin between
-                    end = mRows[rowIndex].low - mMarginPrimary;
-                } else {
-                    if (highVisiblePos >= 0) {
-                        int rowOfHighPos = mGrid.getLocation(highVisiblePos).row;
-                        if (mOrientation == HORIZONTAL ?
-                                rowIndex < rowOfHighPos : rowIndex > rowOfHighPos) {
-                            mRows[rowIndex].high = mRows[rowOfHighPos].low - mMarginPrimary;
-                        }
-                    }
-                    end = mRows[rowIndex].high;
-                }
-                start = end - length;
-                mRows[rowIndex].low = start;
+            if (edge == Integer.MIN_VALUE || edge == Integer.MAX_VALUE) {
+                edge = !mGrid.isReversedFlow() ? mWindowAlignment.mainAxis().getPaddingLow()
+                        : mWindowAlignment.mainAxis().getSize()
+                                - mWindowAlignment.mainAxis().getPaddingHigh();
             }
-            if (mFirstVisiblePos < 0) {
-                mFirstVisiblePos = mLastVisiblePos = index;
+            boolean edgeIsMin = !mGrid.isReversedFlow();
+            if (edgeIsMin) {
+                start = edge;
+                end = edge + length;
             } else {
-                if (append) {
-                    mLastVisiblePos++;
-                } else {
-                    mFirstVisiblePos--;
-                }
+                start = edge - length;
+                end = edge;
             }
-            if (DEBUG) Log.v(getTag(), "start " + start + " end " + end);
             int startSecondary = getRowStartSecondary(rowIndex) - mScrollOffsetSecondary;
             mChildrenStates.loadView(v, index);
-            layoutChild(rowIndex, v, start - mScrollOffsetPrimary, end - mScrollOffsetPrimary,
-                    startSecondary);
+            layoutChild(rowIndex, v, start, end, startSecondary);
             if (DEBUG) {
                 Log.d(getTag(), "addView " + index + " " + v);
             }
-            if (index == mFirstVisiblePos) {
-                if (!mReverseFlowPrimary) {
+            if (TRACE) TraceHelper.endSection();
+
+            if (index == mGrid.getFirstVisibleIndex()) {
+                if (!mGrid.isReversedFlow()) {
                     updateScrollMin();
                 } else {
                     updateScrollMax();
                 }
             }
-            if (index == mLastVisiblePos) {
-                if (!mReverseFlowPrimary) {
+            if (index == mGrid.getLastVisibleIndex()) {
+                if (!mGrid.isReversedFlow()) {
                     updateScrollMax();
                 } else {
                     updateScrollMin();
                 }
             }
+            if (!mInLayout && mPendingMoveSmoothScroller != null) {
+                mPendingMoveSmoothScroller.consumePendingMovesAfterLayout();
+            }
+            if (mChildLaidOutListener != null) {
+                RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(v);
+                mChildLaidOutListener.onChildLaidOut(mBaseGridView, v, index,
+                        vh == null ? NO_ID : vh.getItemId());
+            }
+        }
+
+        @Override
+        public void removeItem(int index) {
+            if (TRACE) TraceHelper.beginSection("removeItem");
+            View v = findViewByPosition(index);
+            if (mInLayout) {
+                detachAndScrapView(v, mRecycler);
+            } else {
+                removeAndRecycleView(v, mRecycler);
+            }
+            if (TRACE) TraceHelper.endSection();
+        }
+
+        @Override
+        public int getEdge(int index) {
+            if (mReverseFlowPrimary) {
+                return getViewMax(findViewByPosition(index));
+            } else {
+                return getViewMin(findViewByPosition(index));
+            }
+        }
+
+        @Override
+        public int getSize(int index) {
+            final View v = findViewByPosition(index);
+            return mOrientation == HORIZONTAL ? v.getMeasuredWidth() : v.getMeasuredHeight();
         }
     };
 
     private void layoutChild(int rowIndex, View v, int start, int end, int startSecondary) {
+        if (TRACE) TraceHelper.beginSection("layoutChild");
         int sizeSecondary = mOrientation == HORIZONTAL ? v.getMeasuredHeight()
                 : v.getMeasuredWidth();
         if (mFixedRowSizeSecondary > 0) {
@@ -1268,6 +1446,7 @@
         v.layout(left, top, right, bottom);
         updateChildOpticalInsets(v, left, top, right, bottom);
         updateChildAlignments(v);
+        if (TRACE) TraceHelper.endSection();
     }
 
     private void updateChildOpticalInsets(View v, int left, int top, int right, int bottom) {
@@ -1288,277 +1467,119 @@
         }
     }
 
-    private boolean needsAppendVisibleItem() {
-        if (mReverseFlowPrimary) {
-            for (int i = 0; i < mNumRows; i++) {
-                if (mRows[i].low == mRows[i].high) {
-                    if (mRows[i].low > mScrollOffsetPrimary) {
-                        return true;
-                    }
-                } else if (mRows[i].low - mMarginPrimary > mScrollOffsetPrimary) {
-                    return true;
-                }
-            }
-        } else {
-            int right = mScrollOffsetPrimary + mSizePrimary;
-            for (int i = 0; i < mNumRows; i++) {
-                if (mRows[i].low == mRows[i].high) {
-                    if (mRows[i].high < right) {
-                        return true;
-                    }
-                } else if (mRows[i].high < right - mMarginPrimary) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private boolean needsPrependVisibleItem() {
-        if (mReverseFlowPrimary) {
-            int right = mScrollOffsetPrimary + mSizePrimary;
-            for (int i = 0; i < mNumRows; i++) {
-                if (mRows[i].low == mRows[i].high) {
-                    if (mRows[i].high < right) {
-                        return true;
-                    }
-                } else if (mRows[i].high < right - mMarginPrimary) {
-                    return true;
-                }
-            }
-        } else {
-            for (int i = 0; i < mNumRows; i++) {
-                if (mRows[i].low == mRows[i].high) {
-                    if (mRows[i].low > mScrollOffsetPrimary) {
-                        return true;
-                    }
-                } else if (mRows[i].low - mMarginPrimary > mScrollOffsetPrimary) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    // Append one column if possible and return true if reach end.
-    private boolean appendOneVisibleItem() {
-        while (true) {
-            if (mLastVisiblePos != NO_POSITION && mLastVisiblePos < mState.getItemCount() -1 &&
-                    mLastVisiblePos < mGrid.getLastIndex()) {
-                // append invisible view of saved location till last row
-                final int index = mLastVisiblePos + 1;
-                final int row = mGrid.getLocation(index).row;
-                mGridProvider.createItem(index, row, true);
-                if (row == mNumRows - 1) {
-                    return false;
-                }
-            } else if ((mLastVisiblePos == NO_POSITION && mState.getItemCount() > 0) ||
-                    (mLastVisiblePos != NO_POSITION &&
-                            mLastVisiblePos < mState.getItemCount() - 1)) {
-                if (mReverseFlowPrimary) {
-                    mGrid.appendItems(mScrollOffsetPrimary);
-                } else {
-                    mGrid.appendItems(mScrollOffsetPrimary + mSizePrimary);
-                }
-                return false;
-            } else {
-                return true;
-            }
-        }
-    }
-
-    private void appendVisibleItems() {
-        while (needsAppendVisibleItem()) {
-            if (appendOneVisibleItem()) {
-                break;
-            }
-        }
-    }
-
-    // Prepend one column if possible and return true if reach end.
-    private boolean prependOneVisibleItem() {
-        while (true) {
-            if (mFirstVisiblePos > 0) {
-                if (mFirstVisiblePos > mGrid.getFirstIndex()) {
-                    // prepend invisible view of saved location till first row
-                    final int index = mFirstVisiblePos - 1;
-                    final int row = mGrid.getLocation(index).row;
-                    mGridProvider.createItem(index, row, false);
-                    if (row == 0) {
-                        return false;
-                    }
-                } else {
-                    if (mReverseFlowPrimary) {
-                        mGrid.prependItems(mScrollOffsetPrimary + mSizePrimary);
-                    } else {
-                        mGrid.prependItems(mScrollOffsetPrimary);
-                    }
-                    return false;
-                }
-            } else {
-                return true;
-            }
-        }
-    }
-
-    private void prependVisibleItems() {
-        while (needsPrependVisibleItem()) {
-            if (prependOneVisibleItem()) {
-                break;
-            }
-        }
-    }
-
-    private void removeChildAt(int position) {
-        View v = findViewByPosition(position);
-        if (v != null) {
-            if (DEBUG) {
-                Log.d(getTag(), "removeAndRecycleViewAt " + position + " " + v);
-            }
-            mChildrenStates.saveOffscreenView(v, position);
-            removeAndRecycleView(v, mRecycler);
-        }
-    }
-
     private void removeInvisibleViewsAtEnd() {
-        if (!mPruneChild) {
-            return;
-        }
-        boolean update = false;
-        while(mLastVisiblePos > mFirstVisiblePos && mLastVisiblePos > mFocusPosition) {
-            View view = findViewByPosition(mLastVisiblePos);
-            boolean offEnd = (!mReverseFlowPrimary) ? getViewMin(view) > mSizePrimary :
-                getViewMax(view) < 0;
-            if (offEnd) {
-                removeChildAt(mLastVisiblePos);
-                mLastVisiblePos--;
-                update = true;
-            } else {
-                break;
-            }
-        }
-        if (update) {
-            updateRowsMinMax();
+        if (mPruneChild) {
+            mGrid.removeInvisibleItemsAtEnd(mFocusPosition,
+                    mReverseFlowPrimary ? 0 : mSizePrimary);
         }
     }
 
     private void removeInvisibleViewsAtFront() {
-        if (!mPruneChild) {
-            return;
+        if (mPruneChild) {
+            mGrid.removeInvisibleItemsAtFront(mFocusPosition,
+                    mReverseFlowPrimary ? mSizePrimary : 0);
         }
-        boolean update = false;
-        while(mLastVisiblePos > mFirstVisiblePos && mFirstVisiblePos < mFocusPosition) {
-            View view = findViewByPosition(mFirstVisiblePos);
-            boolean offFront = (!mReverseFlowPrimary) ? getViewMax(view) < 0:
-                getViewMin(view) > mSizePrimary;
-            if (offFront) {
-                removeChildAt(mFirstVisiblePos);
-                mFirstVisiblePos++;
-                update = true;
+    }
+
+    private boolean appendOneColumnVisibleItems() {
+        return mGrid.appendOneColumnVisibleItems();
+    }
+
+    private boolean prependOneColumnVisibleItems() {
+        return mGrid.prependOneColumnVisibleItems();
+    }
+
+    private void appendVisibleItems() {
+        mGrid.appendVisibleItems(mReverseFlowPrimary ? 0 : mSizePrimary);
+    }
+
+    private void prependVisibleItems() {
+        mGrid.prependVisibleItems(mReverseFlowPrimary ? mSizePrimary : 0);
+    }
+
+    /**
+     * Fast layout when there is no structure change, adapter change, etc.
+     * It will layout all views was layout requested or updated, until hit a view
+     * with different size,  then it break and detachAndScrap all views after that. 
+     */
+    private void fastRelayout() {
+        boolean invalidateAfter = false;
+        final int childCount = getChildCount();
+        int position = -1;
+        for (int index = 0; index < childCount; index++) {
+            View view = getChildAt(index);
+            position = getPositionByIndex(index);
+            Grid.Location location = mGrid.getLocation(position);
+            if (location == null) {
+                if (DEBUG) Log.w(getTag(), "fastRelayout(): no Location at " + position);
+                invalidateAfter = true;
+                break;
+            }
+
+            int startSecondary = getRowStartSecondary(location.row) - mScrollOffsetSecondary;
+            int primarySize, end;
+            int start = getViewMin(view);
+            int oldPrimarySize = (mOrientation == HORIZONTAL) ?
+                    view.getMeasuredWidth() :
+                    view.getMeasuredHeight();
+
+            LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            if (lp.viewNeedsUpdate()) {
+                int viewIndex = mBaseGridView.indexOfChild(view);
+                detachAndScrapView(view, mRecycler);
+                view = getViewForPosition(position);
+                addView(view, viewIndex);
+            }
+
+            if (view.isLayoutRequested()) {
+                measureChild(view);
+            }
+            if (mOrientation == HORIZONTAL) {
+                primarySize = view.getMeasuredWidth();
+                end = start + primarySize;
             } else {
+                primarySize = view.getMeasuredHeight();
+                end = start + primarySize;
+            }
+            layoutChild(location.row, view, start, end, startSecondary);
+            if (oldPrimarySize != primarySize) {
+                // size changed invalidate remaining Locations
+                if (DEBUG) Log.d(getTag(), "fastRelayout: view size changed at " + position);
+                invalidateAfter = true;
                 break;
             }
         }
-        if (update) {
-            updateRowsMinMax();
-        }
-    }
-
-    private void updateRowsMinMax() {
-        if (mFirstVisiblePos < 0) {
-            return;
-        }
-        for (int i = 0; i < mNumRows; i++) {
-            mRows[i].low = Integer.MAX_VALUE;
-            mRows[i].high = Integer.MIN_VALUE;
-        }
-        for (int i = mFirstVisiblePos; i <= mLastVisiblePos; i++) {
-            View view = findViewByPosition(i);
-            int row = mGrid.getLocation(i).row;
-            int low = getViewMin(view) + mScrollOffsetPrimary;
-            if (low < mRows[row].low) {
-                mRows[row].low = low;
-            }
-            int high = getViewMax(view) + mScrollOffsetPrimary;
-            if (high > mRows[row].high) {
-                mRows[row].high = high;
-            }
-        }
-    }
-
-    // Fast layout when there is no structure change, adapter change, etc.
-    protected void fastRelayout(boolean scrollToFocus) {
-        updateScrollController();
-
-        List<Integer>[] rows = mGrid.getItemPositionsInRows(mFirstVisiblePos, mLastVisiblePos);
-
-        // relayout and repositioning views on each row
-        for (int i = 0; i < mNumRows; i++) {
-            List<Integer> row = rows[i];
-            final int startSecondary = getRowStartSecondary(i) - mScrollOffsetSecondary;
-            for (int j = 0, size = row.size(); j < size; j++) {
-                final int position = row.get(j);
-                View view = findViewByPosition(position);
-                int primaryDelta, end;
-
-                int start = getViewMin(view);
-                int oldPrimarySize = (mOrientation == HORIZONTAL) ?
-                        view.getMeasuredWidth() :
-                        view.getMeasuredHeight();
-
-                LayoutParams lp = (LayoutParams) view.getLayoutParams();
-                if (lp.viewNeedsUpdate()) {
-                    int index = mBaseGridView.indexOfChild(view);
-                    detachAndScrapView(view, mRecycler);
-                    view = getViewForPosition(position);
-                    addView(view, index);
-                }
-
-                if (view.isLayoutRequested()) {
-                    measureChild(view);
-                }
-
-                if (mOrientation == HORIZONTAL) {
-                    end = start + view.getMeasuredWidth();
-                    primaryDelta = view.getMeasuredWidth() - oldPrimarySize;
-                    if (primaryDelta != 0) {
-                        for (int k = j + 1; k < size; k++) {
-                            findViewByPosition(row.get(k)).offsetLeftAndRight(primaryDelta);
-                        }
-                    }
-                } else {
-                    end = start + view.getMeasuredHeight();
-                    primaryDelta = view.getMeasuredHeight() - oldPrimarySize;
-                    if (primaryDelta != 0) {
-                        for (int k = j + 1; k < size; k++) {
-                            findViewByPosition(row.get(k)).offsetTopAndBottom(primaryDelta);
-                        }
+        if (invalidateAfter) {
+            final int savedLastPos = mGrid.getLastVisibleIndex();
+            mGrid.invalidateItemsAfter(position);
+            if (mPruneChild) {
+                // in regular prune child mode, we just append items up to edge limit
+                appendVisibleItems();
+                if (mFocusPosition >= 0 && mFocusPosition <= savedLastPos) {
+                    // make sure add focus view back:  the view might be outside edge limit
+                    // when there is delta in onLayoutChildren().
+                    while (mGrid.getLastVisibleIndex() < mFocusPosition) {
+                        mGrid.appendOneColumnVisibleItems();
                     }
                 }
-                layoutChild(i, view, start, end, startSecondary);
+            } else {
+                // prune disabled(e.g. in RowsFragment transition): append all removed items
+                while (mGrid.appendOneColumnVisibleItems()
+                        && mGrid.getLastVisibleIndex() < savedLastPos);
             }
         }
-
-        updateRowsMinMax();
-        appendVisibleItems();
-        prependVisibleItems();
-
-        updateRowsMinMax();
         updateScrollMin();
         updateScrollMax();
         updateScrollSecondAxis();
-
-        if (scrollToFocus) {
-            View focusView = findViewByPosition(mFocusPosition == NO_POSITION ? 0 : mFocusPosition);
-            scrollToView(focusView, false);
-        }
     }
 
     public void removeAndRecycleAllViews(RecyclerView.Recycler recycler) {
+        if (TRACE) TraceHelper.beginSection("removeAndRecycleAllViews");
         if (DEBUG) Log.v(TAG, "removeAndRecycleAllViews " + getChildCount());
         for (int i = getChildCount() - 1; i >= 0; i--) {
             removeAndRecycleViewAt(i, recycler);
         }
+        if (TRACE) TraceHelper.endSection();
     }
 
     // Lays out items based on the current scroll position
@@ -1615,52 +1636,30 @@
             }
         }
 
-        final boolean hasDoneFirstLayout = hasDoneFirstLayout();
+        boolean hadFocus = mBaseGridView.hasFocus();
         int savedFocusPos = mFocusPosition;
-        boolean fastRelayout = false;
-        if (!mState.didStructureChange() && !mForceFullLayout && hasDoneFirstLayout) {
-            fastRelayout = true;
-            fastRelayout(scrollToFocus);
-        } else {
-            boolean hadFocus = mBaseGridView.hasFocus();
-
-            mFocusPosition = init(mFocusPosition);
-            if (mFocusPosition != savedFocusPos) {
-                if (DEBUG) Log.v(getTag(), "savedFocusPos " + savedFocusPos +
-                        " mFocusPosition " + mFocusPosition);
-            }
-            if (mFocusPosition == NO_POSITION) {
-                mBaseGridView.clearFocus();
-            }
-
-            mWindowAlignment.mainAxis().invalidateScrollMin();
-            mWindowAlignment.mainAxis().invalidateScrollMax();
-            // depending on result of init(), either recreating everything
-            // or try to reuse the row start positions near mFocusPosition
-            if (mGrid.getSize() == 0) {
-                // this is a fresh creating all items, starting from
-                // mFocusPosition with a estimated row index.
-                mGrid.setStart(mFocusPosition, StaggeredGrid.START_DEFAULT);
-
-                // Can't track the old focus view
-                delta = deltaSecondary = 0;
-
-            } else {
-                // mGrid remembers Locations for the column that
-                // contains mFocusePosition and also mRows remembers start
-                // positions of each row.
-                // Manually re-create child views for that column
-                int firstIndex = mGrid.getFirstIndex();
-                int lastIndex = mGrid.getLastIndex();
-                for (int i = firstIndex; i <= lastIndex; i++) {
-                    mGridProvider.createItem(i, mGrid.getLocation(i).row, true);
+        if (mInFastRelayout = layoutInit()) {
+            fastRelayout();
+            // appends items till focus position.
+            if (mFocusPosition != NO_POSITION) {
+                View focusView;
+                while ((focusView = findViewByPosition(mFocusPosition)) == null) {
+                    appendOneColumnVisibleItems();
+                }
+                if (scrollToFocus) {
+                    scrollToView(focusView, false);
+                }
+                if (hadFocus) {
+                    focusView.requestFocus();
                 }
             }
-
-            // add visible views at end until reach the end of window
-            appendVisibleItems();
-            // add visible views at front until reach the start of window
-            prependVisibleItems();
+        } else {
+            mInLayoutSearchFocus = hadFocus;
+            if (mFocusPosition != NO_POSITION) {
+                // appends items till focus position.
+                while (appendOneColumnVisibleItems()
+                        && findViewByPosition(mFocusPosition) == null) ;
+            }
             // multiple rounds: scrollToView of first round may drag first/last child into
             // "visible window" and we update scrollMin/scrollMax then run second scrollToView
             int oldFirstVisible;
@@ -1668,8 +1667,8 @@
             do {
                 updateScrollMin();
                 updateScrollMax();
-                oldFirstVisible = mFirstVisiblePos;
-                oldLastVisible = mLastVisiblePos;
+                oldFirstVisible = mGrid.getFirstVisibleIndex();
+                oldLastVisible = mGrid.getLastVisibleIndex();
                 View focusView = findViewByPosition(mFocusPosition);
                 // we need force to initialize the child view's position
                 scrollToView(focusView, false);
@@ -1680,9 +1679,9 @@
                 prependVisibleItems();
                 removeInvisibleViewsAtFront();
                 removeInvisibleViewsAtEnd();
-            } while (mFirstVisiblePos != oldFirstVisible || mLastVisiblePos != oldLastVisible);
+            } while (mGrid.getFirstVisibleIndex() != oldFirstVisible ||
+                    mGrid.getLastVisibleIndex() != oldLastVisible);
         }
-        mForceFullLayout = false;
 
         if (scrollToFocus) {
             scrollDirectionPrimary(-delta);
@@ -1706,7 +1705,12 @@
             updateRowSecondarySizeRefresh();
         }
 
-        if (fastRelayout && mFocusPosition != savedFocusPos) {
+        // For fastRelayout, only dispatch event when focus position changes.
+        if (mInFastRelayout && mFocusPosition != savedFocusPos) {
+            dispatchChildSelected();
+        } else if (!mInFastRelayout && mInLayoutSearchFocus) {
+            // For full layout we dispatchChildSelected() in createItem() unless searched all
+            // children and found none is focusable then dispatchChildSelected() here.
             dispatchChildSelected();
         }
 
@@ -1777,6 +1781,7 @@
 
     // scroll in main direction may add/prune views
     private int scrollDirectionPrimary(int da) {
+        if (TRACE) TraceHelper.beginSection("scrollPrimary");
         boolean isMaxUnknown = false, isMinUnknown = false;
         int minScroll = 0, maxScroll = 0;
         if (da > 0) {
@@ -1797,53 +1802,41 @@
             }
         }
         if (da == 0) {
+            if (TRACE) TraceHelper.endSection();
             return 0;
         }
         offsetChildrenPrimary(-da);
         mScrollOffsetPrimary += da;
         if (mInLayout) {
+            if (TRACE) TraceHelper.endSection();
             return da;
         }
 
         int childCount = getChildCount();
         boolean updated;
 
-        if (da > 0) {
-            if (mReverseFlowPrimary) {
-                prependVisibleItems();
-            } else {
-                appendVisibleItems();
-            }
-        } else if (da < 0) {
-            if (mReverseFlowPrimary) {
-                appendVisibleItems();
-            } else {
-                prependVisibleItems();
-            }
+        if (mReverseFlowPrimary ? da > 0 : da < 0) {
+            prependVisibleItems();
+        } else {
+            appendVisibleItems();
         }
         updated = getChildCount() > childCount;
         childCount = getChildCount();
 
-        if (da > 0) {
-            if (mReverseFlowPrimary) {
-                removeInvisibleViewsAtEnd();
-            } else {
-                removeInvisibleViewsAtFront();
-            }
-        } else if (da < 0) {
-            if (mReverseFlowPrimary) {
-                removeInvisibleViewsAtFront();
-            } else {
-                removeInvisibleViewsAtEnd();
-            }
+        if (TRACE) TraceHelper.beginSection("remove");
+        if (mReverseFlowPrimary ? da > 0 : da < 0) {
+            removeInvisibleViewsAtEnd();
+        } else {
+            removeInvisibleViewsAtFront();
         }
+        if (TRACE) TraceHelper.endSection();
         updated |= getChildCount() < childCount;
-
         if (updated) {
             updateRowSecondarySizeRefresh();
         }
 
         mBaseGridView.invalidate();
+        if (TRACE) TraceHelper.endSection();
         return da;
     }
 
@@ -1859,7 +1852,8 @@
     }
 
     private void updateScrollMax() {
-        int highVisiblePos = (!mReverseFlowPrimary) ? mLastVisiblePos : mFirstVisiblePos;
+        int highVisiblePos = (!mReverseFlowPrimary) ? mGrid.getLastVisibleIndex()
+                : mGrid.getFirstVisibleIndex();
         int highMaxPos = (!mReverseFlowPrimary) ? mState.getItemCount() - 1 : 0;
         if (highVisiblePos < 0) {
             return;
@@ -1869,44 +1863,29 @@
         if (!highAvailable && maxUnknown) {
             return;
         }
-        int maxEdge = Integer.MIN_VALUE;
-        int rowIndex = -1;
-        for (int i = 0; i < mRows.length; i++) {
-            if (mRows[i].high > maxEdge) {
-                maxEdge = mRows[i].high;
-                rowIndex = i;
-            }
-        }
-        int maxScroll = Integer.MAX_VALUE;
-        for (int i = mFirstVisiblePos; i <= mLastVisiblePos; i++) {
-            int pos = mReverseFlowPrimary ? i : mLastVisiblePos-i+mFirstVisiblePos;
-            StaggeredGrid.Location location = mGrid.getLocation(pos);
-            if (location != null && location.row == rowIndex) {
-                int savedMaxEdge = mWindowAlignment.mainAxis().getMaxEdge();
-                mWindowAlignment.mainAxis().setMaxEdge(maxEdge);
-                maxScroll = getPrimarySystemScrollPosition(findViewByPosition(pos));
-                mWindowAlignment.mainAxis().setMaxEdge(savedMaxEdge);
-                break;
-            }
-        }
+        int maxEdge = mGrid.findRowMax(true, sTwoInts) + mScrollOffsetPrimary;
+        int rowIndex = sTwoInts[0];
+        int pos = sTwoInts[1];
+        int savedMaxEdge = mWindowAlignment.mainAxis().getMaxEdge();
+        mWindowAlignment.mainAxis().setMaxEdge(maxEdge);
+        int maxScroll = getPrimarySystemScrollPosition(findViewByPosition(pos));
+        mWindowAlignment.mainAxis().setMaxEdge(savedMaxEdge);
+
         if (highAvailable) {
             mWindowAlignment.mainAxis().setMaxEdge(maxEdge);
             mWindowAlignment.mainAxis().setMaxScroll(maxScroll);
             if (DEBUG) Log.v(getTag(), "updating scroll maxEdge to " + maxEdge +
                     " scrollMax to " + maxScroll);
         } else {
-            // the maxScroll for currently last visible item is larger,
-            // so we must invalidate the max scroll value.
-            if (maxScroll > mWindowAlignment.mainAxis().getMaxScroll()) {
-                mWindowAlignment.mainAxis().invalidateScrollMax();
-                if (DEBUG) Log.v(getTag(), "Invalidate scrollMax since it should be "
-                        + "greater than " + maxScroll);
-            }
+            mWindowAlignment.mainAxis().invalidateScrollMax();
+            if (DEBUG) Log.v(getTag(), "Invalidate scrollMax since it should be "
+                    + "greater than " + maxScroll);
         }
     }
 
     private void updateScrollMin() {
-        int lowVisiblePos = (!mReverseFlowPrimary) ? mFirstVisiblePos : mLastVisiblePos;
+        int lowVisiblePos = (!mReverseFlowPrimary) ? mGrid.getFirstVisibleIndex()
+                : mGrid.getLastVisibleIndex();
         int lowMinPos = (!mReverseFlowPrimary) ? 0 : mState.getItemCount() - 1;
         if (lowVisiblePos < 0) {
             return;
@@ -1916,39 +1895,23 @@
         if (!lowAvailable && minUnknown) {
             return;
         }
-        int minEdge = Integer.MAX_VALUE;
-        int rowIndex = -1;
-        for (int i = 0; i < mRows.length; i++) {
-            if (mRows[i].low < minEdge) {
-                minEdge = mRows[i].low;
-                rowIndex = i;
-            }
-        }
-        int minScroll = Integer.MIN_VALUE;
-        for (int i = mFirstVisiblePos; i <= mLastVisiblePos; i++) {
-            int pos = mReverseFlowPrimary ? mLastVisiblePos-i+mFirstVisiblePos : i;
-            StaggeredGrid.Location location = mGrid.getLocation(pos);
-            if (location != null && location.row == rowIndex) {
-                int savedMinEdge = mWindowAlignment.mainAxis().getMinEdge();
-                mWindowAlignment.mainAxis().setMinEdge(minEdge);
-                minScroll = getPrimarySystemScrollPosition(findViewByPosition(pos));
-                mWindowAlignment.mainAxis().setMinEdge(savedMinEdge);
-                break;
-            }
-        }
+        int minEdge = mGrid.findRowMin(false, sTwoInts) + mScrollOffsetPrimary;
+        int rowIndex = sTwoInts[0];
+        int pos = sTwoInts[1];
+        int savedMinEdge = mWindowAlignment.mainAxis().getMinEdge();
+        mWindowAlignment.mainAxis().setMinEdge(minEdge);
+        int minScroll = getPrimarySystemScrollPosition(findViewByPosition(pos));
+        mWindowAlignment.mainAxis().setMinEdge(savedMinEdge);
+
         if (lowAvailable) {
             mWindowAlignment.mainAxis().setMinEdge(minEdge);
             mWindowAlignment.mainAxis().setMinScroll(minScroll);
             if (DEBUG) Log.v(getTag(), "updating scroll minEdge to " + minEdge +
                     " scrollMin to " + minScroll);
         } else {
-            // the minScroll for currently first visible item is smaller,
-            // so we must invalidate the min scroll value.
-            if (minScroll < mWindowAlignment.mainAxis().getMinScroll()) {
-                mWindowAlignment.mainAxis().invalidateScrollMin();
-                if (DEBUG) Log.v(getTag(), "Invalidate scrollMin, since it should be "
-                        + "less than " + minScroll);
-            }
+            mWindowAlignment.mainAxis().invalidateScrollMin();
+            if (DEBUG) Log.v(getTag(), "Invalidate scrollMin, since it should be "
+                    + "less than " + minScroll);
         }
     }
 
@@ -1977,9 +1940,9 @@
     private void updateScrollController() {
         // mScrollOffsetPrimary and mScrollOffsetSecondary includes the padding.
         // e.g. when topPadding is 16 for horizontal grid view,  the initial
-        // mScrollOffsetSecondary is -16.  fastLayout() put views based on offsets(not padding),
+        // mScrollOffsetSecondary is -16.  fastRelayout() put views based on offsets(not padding),
         // when padding changes to 20,  we also need update mScrollOffsetSecondary to -20 before
-        // fastLayout() is performed
+        // fastRelayout() is performed
         int paddingPrimaryDiff, paddingSecondaryDiff;
         if (mOrientation == HORIZONTAL) {
             paddingPrimaryDiff = getPaddingLeft() - mWindowAlignment.horizontal.getPaddingLow();
@@ -2004,25 +1967,31 @@
         }
     }
 
-    public void setSelection(RecyclerView parent, int position) {
-        setSelection(parent, position, false);
+    public void setSelection(RecyclerView parent, int position,
+            int primaryScrollExtra) {
+        setSelection(parent, position, false, primaryScrollExtra);
     }
 
     public void setSelectionSmooth(RecyclerView parent, int position) {
-        setSelection(parent, position, true);
+        setSelection(parent, position, true, 0);
     }
 
     public int getSelection() {
         return mFocusPosition;
     }
 
-    public void setSelection(RecyclerView parent, int position, boolean smooth) {
-        if (mFocusPosition != position && position != NO_POSITION) {
-            scrollToSelection(parent, position, smooth);
+    public void setSelection(RecyclerView parent, int position, boolean smooth,
+            int primaryScrollExtra) {
+        if (mFocusPosition != position && position != NO_POSITION
+                || primaryScrollExtra != mPrimaryScrollExtra) {
+            scrollToSelection(parent, position, smooth, primaryScrollExtra);
         }
     }
 
-    private void scrollToSelection(RecyclerView parent, int position, boolean smooth) {
+    private void scrollToSelection(RecyclerView parent, int position, boolean smooth,
+            int primaryScrollExtra) {
+        if (TRACE) TraceHelper.beginSection("scrollToSelection");
+        mPrimaryScrollExtra = primaryScrollExtra;
         View view = findViewByPosition(position);
         if (view != null) {
             mInSelection = true;
@@ -2040,61 +2009,68 @@
                             "not be called before first layout pass");
                     return;
                 }
-                LinearSmoothScroller linearSmoothScroller =
-                        new LinearSmoothScroller(parent.getContext()) {
-                    @Override
-                    public PointF computeScrollVectorForPosition(int targetPosition) {
-                        if (getChildCount() == 0) {
-                            return null;
-                        }
-                        final int firstChildPos = getPosition(getChildAt(0));
-                        // TODO We should be able to deduce direction from bounds of current and target focus,
-                        // rather than making assumptions about positions and directionality
-                        final boolean isStart = mReverseFlowPrimary ? targetPosition > firstChildPos : targetPosition < firstChildPos;
-                        final int direction = isStart ? -1 : 1;
-                        if (mOrientation == HORIZONTAL) {
-                            return new PointF(direction, 0);
-                        } else {
-                            return new PointF(0, direction);
-                        }
-                    }
-                    @Override
-                    protected void onTargetFound(View targetView,
-                            RecyclerView.State state, Action action) {
-                        if (hasFocus()) {
-                            targetView.requestFocus();
-                        }
-                        dispatchChildSelected();
-                        if (getScrollPosition(targetView, mTempDeltas)) {
-                            int dx, dy;
-                            if (mOrientation == HORIZONTAL) {
-                                dx = mTempDeltas[0];
-                                dy = mTempDeltas[1];
-                            } else {
-                                dx = mTempDeltas[1];
-                                dy = mTempDeltas[0];
-                            }
-                            final int distance = (int) Math.sqrt(dx * dx + dy * dy);
-                            final int time = calculateTimeForDeceleration(distance);
-                            action.update(dx, dy, time, mDecelerateInterpolator);
-                        }
-                    }
-                };
-                linearSmoothScroller.setTargetPosition(position);
-                startSmoothScroll(linearSmoothScroller);
+                startPositionSmoothScroller(position);
             } else {
                 mForceFullLayout = true;
                 parent.requestLayout();
             }
         }
+        if (TRACE) TraceHelper.endSection();
+    }
+
+    void startPositionSmoothScroller(int position) {
+        LinearSmoothScroller linearSmoothScroller = new GridLinearSmoothScroller() {
+            @Override
+            public PointF computeScrollVectorForPosition(int targetPosition) {
+                if (getChildCount() == 0) {
+                    return null;
+                }
+                final int firstChildPos = getPosition(getChildAt(0));
+                // TODO We should be able to deduce direction from bounds of current and target
+                // focus, rather than making assumptions about positions and directionality
+                final boolean isStart = mReverseFlowPrimary ? targetPosition > firstChildPos
+                        : targetPosition < firstChildPos;
+                final int direction = isStart ? -1 : 1;
+                if (mOrientation == HORIZONTAL) {
+                    return new PointF(direction, 0);
+                } else {
+                    return new PointF(0, direction);
+                }
+            }
+
+        };
+        linearSmoothScroller.setTargetPosition(position);
+        startSmoothScroll(linearSmoothScroller);
+    }
+
+    private void processPendingMovement(boolean forward) {
+        if (forward ? hasCreatedLastItem() : hasCreatedFirstItem()) {
+            return;
+        }
+        if (mPendingMoveSmoothScroller == null) {
+            // Stop existing scroller and create a new PendingMoveSmoothScroller.
+            mBaseGridView.stopScroll();
+            PendingMoveSmoothScroller linearSmoothScroller = new PendingMoveSmoothScroller(
+                    forward ? 1 : -1, mNumRows > 1);
+            mFocusPositionOffset = 0;
+            startSmoothScroll(linearSmoothScroller);
+            if (linearSmoothScroller.isRunning()) {
+                mPendingMoveSmoothScroller = linearSmoothScroller;
+            }
+        } else {
+            if (forward) {
+                mPendingMoveSmoothScroller.increasePendingMoves();
+            } else {
+                mPendingMoveSmoothScroller.decreasePendingMoves();
+            }
+        }
     }
 
     @Override
     public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
         if (DEBUG) Log.v(getTag(), "onItemsAdded positionStart "
                 + positionStart + " itemCount " + itemCount);
-        if (mFocusPosition != NO_POSITION && mFocusPositionOffset != Integer.MIN_VALUE
-                && getChildAt(mFocusPosition) != null) {
+        if (mFocusPosition != NO_POSITION && mFocusPositionOffset != Integer.MIN_VALUE) {
             int pos = mFocusPosition + mFocusPositionOffset;
             if (positionStart <= pos) {
                 mFocusPositionOffset += itemCount;
@@ -2114,8 +2090,7 @@
     public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
         if (DEBUG) Log.v(getTag(), "onItemsRemoved positionStart "
                 + positionStart + " itemCount " + itemCount);
-        if (mFocusPosition != NO_POSITION && mFocusPositionOffset != Integer.MIN_VALUE
-                && getChildAt(mFocusPosition) != null) {
+        if (mFocusPosition != NO_POSITION && mFocusPositionOffset != Integer.MIN_VALUE) {
             int pos = mFocusPosition + mFocusPositionOffset;
             if (positionStart <= pos) {
                 if (positionStart + itemCount > pos) {
@@ -2134,8 +2109,7 @@
             int itemCount) {
         if (DEBUG) Log.v(getTag(), "onItemsMoved fromPosition "
                 + fromPosition + " toPosition " + toPosition);
-        if (mFocusPosition != NO_POSITION && mFocusPositionOffset != Integer.MIN_VALUE
-                && getChildAt(mFocusPosition) != null) {
+        if (mFocusPosition != NO_POSITION && mFocusPositionOffset != Integer.MIN_VALUE) {
             int pos = mFocusPosition + mFocusPositionOffset;
             if (fromPosition <= pos && pos < fromPosition + itemCount) {
                 // moved items include focused position
@@ -2208,12 +2182,12 @@
         // TODO: change to use State object in onRequestChildFocus()
         boolean isMin, isMax;
         if (!mReverseFlowPrimary) {
-            isMin = mFirstVisiblePos == 0;
-            isMax = mLastVisiblePos == (mState == null ?
+            isMin = mGrid.getFirstVisibleIndex() == 0;
+            isMax = mGrid.getLastVisibleIndex() == (mState == null ?
                     getItemCount() : mState.getItemCount()) - 1;
         } else {
-            isMax = mFirstVisiblePos == 0;
-            isMin = mLastVisiblePos == (mState == null ?
+            isMax = mGrid.getFirstVisibleIndex() == 0;
+            isMin = mGrid.getLastVisibleIndex() == (mState == null ?
                     getItemCount() : mState.getItemCount()) - 1;
         }
         for (int i = getChildCount() - 1; (isMin || isMax) && i >= 0; i--) {
@@ -2234,7 +2208,7 @@
     private int getSecondarySystemScrollPosition(View view) {
         int viewCenterSecondary = mScrollOffsetSecondary + getViewCenterSecondary(view);
         int pos = getPositionByView(view);
-        StaggeredGrid.Location location = mGrid.getLocation(pos);
+        Grid.Location location = mGrid.getLocation(pos);
         final int row = location.row;
         final boolean isMin, isMax;
         if (!mReverseFlowSecondary) {
@@ -2258,9 +2232,9 @@
             if (!mInLayout) {
                 dispatchChildSelected();
             }
-        }
-        if (mBaseGridView.isChildrenDrawingOrderEnabledInternal()) {
-            mBaseGridView.invalidate();
+            if (mBaseGridView.isChildrenDrawingOrderEnabledInternal()) {
+                mBaseGridView.invalidate();
+            }
         }
         if (view == null) {
             return;
@@ -2270,11 +2244,11 @@
             // by setSelection())
             view.requestFocus();
         }
-        if (!mScrollEnabled) {
+        if (!mScrollEnabled && smooth) {
             return;
         }
-        if (getScrollPosition(view, mTempDeltas)) {
-            scrollGrid(mTempDeltas[0], mTempDeltas[1], smooth);
+        if (getScrollPosition(view, sTwoInts)) {
+            scrollGrid(sTwoInts[0], sTwoInts[1], smooth);
         }
     }
 
@@ -2299,20 +2273,20 @@
         View lastView = null;
         int paddingLow = mWindowAlignment.mainAxis().getPaddingLow();
         int clientSize = mWindowAlignment.mainAxis().getClientSize();
-        final int row = mGrid.getLocation(pos).row;
+        final int row = mGrid.getRowIndex(pos);
         if (viewMin < paddingLow) {
             // view enters low padding area:
             firstView = view;
             if (mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_PAGE) {
                 // scroll one "page" left/top,
                 // align first visible item of the "page" at the low padding edge.
-                while (!prependOneVisibleItem()) {
-                    List<Integer> positions =
-                            mGrid.getItemPositionsInRows(mFirstVisiblePos, pos)[row];
+                while (prependOneColumnVisibleItems()) {
+                    CircularIntArray positions =
+                            mGrid.getItemPositionsInRows(mGrid.getFirstVisibleIndex(), pos)[row];
                     firstView = findViewByPosition(positions.get(0));
                     if (viewMax - getViewMin(firstView) > clientSize) {
-                        if (positions.size() > 1) {
-                            firstView = findViewByPosition(positions.get(1));
+                        if (positions.size() > 2) {
+                            firstView = findViewByPosition(positions.get(2));
                         }
                         break;
                     }
@@ -2324,14 +2298,14 @@
                 // scroll whole one page right/bottom, align view at the low padding edge.
                 firstView = view;
                 do {
-                    List<Integer> positions =
-                            mGrid.getItemPositionsInRows(pos, mLastVisiblePos)[row];
+                    CircularIntArray positions =
+                            mGrid.getItemPositionsInRows(pos, mGrid.getLastVisibleIndex())[row];
                     lastView = findViewByPosition(positions.get(positions.size() - 1));
                     if (getViewMax(lastView) - viewMin > clientSize) {
                         lastView = null;
                         break;
                     }
-                } while (!appendOneVisibleItem());
+                } while (appendOneColumnVisibleItems());
                 if (lastView != null) {
                     // however if we reached end,  we should align last view.
                     firstView = null;
@@ -2370,11 +2344,12 @@
         int scrollSecondary = getSecondarySystemScrollPosition(view);
         if (DEBUG) {
             Log.v(getTag(), "getAlignedPosition " + scrollPrimary + " " + scrollSecondary
-                    + " " + mWindowAlignment);
+                    + " " + mPrimaryScrollExtra + " " + mWindowAlignment);
             Log.v(getTag(), "getAlignedPosition " + mScrollOffsetPrimary + " " + mScrollOffsetSecondary);
         }
         scrollPrimary -= mScrollOffsetPrimary;
         scrollSecondary -= mScrollOffsetSecondary;
+        scrollPrimary += mPrimaryScrollExtra;
         if (scrollPrimary != 0 || scrollSecondary != 0) {
             deltas[0] = scrollPrimary;
             deltas[1] = scrollSecondary;
@@ -2423,7 +2398,7 @@
             mScrollEnabled = scrollEnabled;
             if (mScrollEnabled && mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_ALIGNED
                     && mFocusPosition != NO_POSITION) {
-                scrollToSelection(mBaseGridView, mFocusPosition, true);
+                scrollToSelection(mBaseGridView, mFocusPosition, true, mPrimaryScrollExtra);
             }
         }
     }
@@ -2460,16 +2435,16 @@
     }
 
     boolean hasPreviousViewInSameRow(int pos) {
-        if (mGrid == null || pos == NO_POSITION) {
+        if (mGrid == null || pos == NO_POSITION || mGrid.getFirstVisibleIndex() < 0) {
             return false;
         }
-        if (mFirstVisiblePos > 0) {
+        if (mGrid.getFirstVisibleIndex() > 0) {
             return true;
         }
         final int focusedRow = mGrid.getLocation(pos).row;
         for (int i = getChildCount() - 1; i >= 0; i--) {
             int position = getPositionByIndex(i);
-            StaggeredGrid.Location loc = mGrid.getLocation(position);
+            Grid.Location loc = mGrid.getLocation(position);
             if (loc != null && loc.row == focusedRow) {
                 if (position < pos) {
                     return true;
@@ -2498,6 +2473,10 @@
                 // Move on secondary direction uses default addFocusables().
                 return false;
             }
+            if (mPendingMoveSmoothScroller != null) {
+                // don't find next focusable if has pending movement.
+                return true;
+            }
             final View focused = recyclerView.findFocus();
             final int focusedPos = getPositionByIndex(findImmediateChildIndex(focused));
             // Add focusables of focused item.
@@ -2512,11 +2491,11 @@
                 for (int i = 0, count = getChildCount(); i < count; i++) {
                     int index = movement == NEXT_ITEM ? i : count - 1 - i;
                     final View child = getChildAt(index);
-                    if (child.getVisibility() != View.VISIBLE) {
+                    if (child.getVisibility() != View.VISIBLE || !child.hasFocusable()) {
                         continue;
                     }
                     int position = getPositionByIndex(index);
-                    StaggeredGrid.Location loc = mGrid.getLocation(position);
+                    Grid.Location loc = mGrid.getLocation(position);
                     if (focusedRow == NO_POSITION || (loc != null && loc.row == focusedRow)) {
                         if (focusedPos == NO_POSITION ||
                                 (movement == NEXT_ITEM && position > focusedPos)
@@ -2566,6 +2545,20 @@
         return true;
     }
 
+    private boolean hasCreatedLastItem() {
+        int count = mState.getItemCount();
+        return count == 0 || findViewByPosition(count - 1) != null;
+    }
+
+    private boolean hasCreatedFirstItem() {
+        int count = mState.getItemCount();
+        return count == 0 || findViewByPosition(0) != null;
+    }
+
+    boolean canScrollTo(View view) {
+        return view.getVisibility() == View.VISIBLE && (!hasFocus() || view.hasFocusable());
+    }
+
     @Override
     public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
             RecyclerView.State state) {
@@ -2574,55 +2567,26 @@
         View view = null;
         int movement = getMovement(direction);
         final boolean isScroll = mBaseGridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE;
-        if (mNumRows == 1) {
-            // for simple row, use LinearSmoothScroller to smooth animation.
-            // It will stay at a fixed cap speed in continuous scroll.
-            if (movement == NEXT_ITEM) {
-                int newPos = mFocusPosition + mNumRows;
-                if (newPos < getItemCount() && mScrollEnabled) {
-                    setSelectionSmooth(mBaseGridView, newPos);
-                    view = focused;
-                } else {
-                    if (isScroll || !mFocusOutEnd) {
-                        view = focused;
-                    }
-                }
-            } else if (movement == PREV_ITEM){
-                int newPos = mFocusPosition - mNumRows;
-                if (newPos >= 0 && mScrollEnabled) {
-                    setSelectionSmooth(mBaseGridView, newPos);
-                    view = focused;
-                } else {
-                    if (isScroll || !mFocusOutFront) {
-                        view = focused;
-                    }
-                }
+        saveContext(recycler, state);
+        if (movement == NEXT_ITEM) {
+            if (isScroll || !mFocusOutEnd) {
+                view = focused;
             }
-        } else if (mNumRows > 1) {
-            // for possible staggered grid,  we need guarantee focus to same row/column.
-            // TODO: we may also use LinearSmoothScroller.
-            saveContext(recycler, state);
-            final FocusFinder ff = FocusFinder.getInstance();
-            if (movement == NEXT_ITEM) {
-                while (view == null && !appendOneVisibleItem()) {
-                    view = ff.findNextFocus(mBaseGridView, focused, direction);
-                }
-            } else if (movement == PREV_ITEM){
-                while (view == null && !prependOneVisibleItem()) {
-                    view = ff.findNextFocus(mBaseGridView, focused, direction);
-                }
+            if (mScrollEnabled && !hasCreatedLastItem()) {
+                processPendingMovement(true);
+                view = focused;
             }
-            if (view == null) {
-                // returning the same view to prevent focus lost when scrolling past the end of the list
-                if (movement == PREV_ITEM) {
-                    view = mFocusOutFront && !isScroll ? null : focused;
-                } else if (movement == NEXT_ITEM){
-                    view = mFocusOutEnd && !isScroll ? null : focused;
-                }
+        } else if (movement == PREV_ITEM) {
+            if (isScroll || !mFocusOutFront) {
+                view = focused;
             }
-            leaveContext();
+            if (mScrollEnabled && !hasCreatedFirstItem()) {
+                processPendingMovement(false);
+                view = focused;
+            }
         }
-        if (DEBUG) Log.v(getTag(), "returning view " + view);
+        leaveContext();
+        if (DEBUG) Log.v(getTag(), "onFocusSearchFailed returning view " + view);
         return view;
     }
 
@@ -2759,10 +2723,7 @@
 
     private void discardLayoutInfo() {
         mGrid = null;
-        mRows = null;
         mRowSizeSecondary = null;
-        mFirstVisiblePos = -1;
-        mLastVisiblePos = -1;
         mRowSecondarySizeRefresh = false;
     }
 
@@ -2838,6 +2799,13 @@
         return ss;
     }
 
+    void onChildRecycled(RecyclerView.ViewHolder holder) {
+        final int position = holder.getAdapterPosition();
+        if (position != NO_POSITION) {
+            mChildrenStates.saveOffscreenView(holder.itemView, position);
+        }
+    }
+
     @Override
     public void onRestoreInstanceState(Parcelable state) {
         if (!(state instanceof SavedState)) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java
index 1150805..321fa67 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java
@@ -77,13 +77,10 @@
 
     void setRowHeight(TypedArray array) {
         TypedValue typedValue = array.peekValue(R.styleable.lbHorizontalGridView_rowHeight);
-        int size;
-        if (typedValue != null && typedValue.type == TypedValue.TYPE_DIMENSION) {
-            size = array.getDimensionPixelSize(R.styleable.lbHorizontalGridView_rowHeight, 0);
-        } else {
-            size = array.getInt(R.styleable.lbHorizontalGridView_rowHeight, 0);
+        if (typedValue != null) {
+            int size = array.getLayoutDimension(R.styleable.lbHorizontalGridView_rowHeight, 0);
+            setRowHeight(size);
         }
-        setRowHeight(size);
     }
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java b/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java
index d1545dd..d423d4e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java
@@ -16,6 +16,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -151,7 +152,7 @@
         }
     }
 
-    public void setInfoAreaBackgroundColor(int color) {
+    public void setInfoAreaBackgroundColor(@ColorInt int color) {
         if (mInfoArea != null) {
             mInfoArea.setBackgroundColor(color);
             if (mBadgeImage != null) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java
index 97f9091..dae01fc 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java
@@ -221,6 +221,7 @@
             mPresenters.add(presenter);
             type = mPresenters.indexOf(presenter);
             if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type);
+            onAddPresenter(presenter, type);
             if (mAdapterListener != null) {
                 mAdapterListener.onAddPresenter(presenter, type);
             }
@@ -229,12 +230,48 @@
     }
 
     /**
+     * Called when presenter is added to Adapter.
+     */
+    protected void onAddPresenter(Presenter presenter, int type) {
+    }
+
+    /**
+     * Called when ViewHolder is created.
+     */
+    protected void onCreate(ViewHolder viewHolder) {
+    }
+
+    /**
+     * Called when ViewHolder has been bound to data.
+     */
+    protected void onBind(ViewHolder viewHolder) {
+    }
+
+    /**
+     * Called when ViewHolder has been unbound from data.
+     */
+    protected void onUnbind(ViewHolder viewHolder) {
+    }
+
+    /**
+     * Called when ViewHolder has been attached to window.
+     */
+    protected void onAttachedToWindow(ViewHolder viewHolder) {
+    }
+
+    /**
+     * Called when ViewHolder has been detached from window.
+     */
+    protected void onDetachedFromWindow(ViewHolder viewHolder) {
+    }
+
+    /**
      * {@link View.OnFocusChangeListener} that assigned in
      * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change
      * {@link View.OnFocusChangeListener} after that.
      */
     @Override
-    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType);
         Presenter presenter = mPresenters.get(viewType);
         Presenter.ViewHolder presenterVh;
@@ -248,6 +285,7 @@
             view = presenterVh.view;
         }
         ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh);
+        onCreate(viewHolder);
         if (mAdapterListener != null) {
             mAdapterListener.onCreate(viewHolder);
         }
@@ -267,33 +305,34 @@
     }
 
     @Override
-    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
         if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
         ViewHolder viewHolder = (ViewHolder) holder;
         viewHolder.mItem = mAdapter.get(position);
 
         viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem);
 
+        onBind(viewHolder);
         if (mAdapterListener != null) {
             mAdapterListener.onBind(viewHolder);
         }
     }
 
     @Override
-    public void onViewRecycled(RecyclerView.ViewHolder holder) {
+    public final void onViewRecycled(RecyclerView.ViewHolder holder) {
         ViewHolder viewHolder = (ViewHolder) holder;
         viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder);
-
-        viewHolder.mItem = null;
-
+        onUnbind(viewHolder);
         if (mAdapterListener != null) {
             mAdapterListener.onUnbind(viewHolder);
         }
+        viewHolder.mItem = null;
     }
 
     @Override
-    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
+    public final void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
         ViewHolder viewHolder = (ViewHolder) holder;
+        onAttachedToWindow(viewHolder);
         if (mAdapterListener != null) {
             mAdapterListener.onAttachedToWindow(viewHolder);
         }
@@ -301,9 +340,10 @@
     }
 
     @Override
-    public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
+    public final void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
         ViewHolder viewHolder = (ViewHolder) holder;
         viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder);
+        onDetachedFromWindow(viewHolder);
         if (mAdapterListener != null) {
             mAdapterListener.onDetachedFromWindow(viewHolder);
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
index fc0237b..c324844 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
@@ -16,9 +16,8 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.support.v17.leanback.R;
-import android.support.v17.leanback.graphics.ColorOverlayDimmer;
-import android.support.v17.leanback.widget.RowPresenter.ViewHolder;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
@@ -55,7 +54,7 @@
     public static class ViewHolder extends RowPresenter.ViewHolder {
         final ListRowPresenter mListRowPresenter;
         final HorizontalGridView mGridView;
-        final ItemBridgeAdapter mItemBridgeAdapter = new ItemBridgeAdapter();
+        ItemBridgeAdapter mItemBridgeAdapter;
         final HorizontalHoverCardSwitcher mHoverCardViewSwitcher = new HorizontalHoverCardSwitcher();
         final int mPaddingTop;
         final int mPaddingBottom;
@@ -85,10 +84,59 @@
         }
     }
 
+    class ListRowPresenterItemBridgeAdapter extends ItemBridgeAdapter {
+        ListRowPresenter.ViewHolder mRowViewHolder;
+
+        ListRowPresenterItemBridgeAdapter(ListRowPresenter.ViewHolder rowViewHolder) {
+            mRowViewHolder = rowViewHolder;
+        }
+
+        @Override
+        public void onBind(final ItemBridgeAdapter.ViewHolder viewHolder) {
+            // Only when having an OnItemClickListner, we will attach the OnClickListener.
+            if (mRowViewHolder.getOnItemViewClickedListener() != null) {
+                viewHolder.mHolder.view.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
+                                mRowViewHolder.mGridView.getChildViewHolder(viewHolder.itemView);
+                        if (mRowViewHolder.getOnItemViewClickedListener() != null) {
+                            mRowViewHolder.getOnItemViewClickedListener().onItemClicked(viewHolder.mHolder,
+                                    ibh.mItem, mRowViewHolder, (ListRow) mRowViewHolder.mRow);
+                        }
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
+            if (mRowViewHolder.getOnItemViewClickedListener() != null) {
+                viewHolder.mHolder.view.setOnClickListener(null);
+            }
+        }
+
+        @Override
+        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
+            if (needsDefaultListSelectEffect()) {
+                int dimmedColor = mRowViewHolder.mColorDimmer.getPaint().getColor();
+                ((ShadowOverlayContainer) viewHolder.itemView).setOverlayColor(dimmedColor);
+            }
+            mRowViewHolder.syncActivatedStatus(viewHolder.itemView);
+        }
+
+        @Override
+        public void onAddPresenter(Presenter presenter, int type) {
+            mRowViewHolder.getGridView().getRecycledViewPool().setMaxRecycledViews(
+                    type, getRecycledPoolSize(presenter));
+        }
+    }
+
     private int mRowHeight;
     private int mExpandedRowHeight;
     private PresenterSelector mHoverCardPresenterSelector;
-    private int mZoomFactor;
+    private int mFocusZoomFactor;
+    private boolean mUseFocusDimmer;
     private boolean mShadowEnabled = true;
     private int mBrowseRowsFadingEdgeLength = -1;
     private boolean mRoundedCornersEnabled = true;
@@ -100,7 +148,8 @@
 
     /**
      * Constructs a ListRowPresenter with defaults.
-     * Uses {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} for focus zooming.
+     * Uses {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} for focus zooming and
+     * disabled dimming on focus.
      */
     public ListRowPresenter() {
         this(FocusHighlight.ZOOM_FACTOR_MEDIUM);
@@ -109,14 +158,35 @@
     /**
      * Constructs a ListRowPresenter with the given parameters.
      *
-     * @param zoomFactor Controls the zoom factor used when an item view is focused. One of
+     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
      *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
      *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
+     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
      *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
      *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
+     * Dimming on focus defaults to disabled.
      */
-    public ListRowPresenter(int zoomFactor) {
-        mZoomFactor = zoomFactor;
+    public ListRowPresenter(int focusZoomFactor) {
+        this(focusZoomFactor, false);
+    }
+
+    /**
+     * Constructs a ListRowPresenter with the given parameters.
+     *
+     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
+     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
+     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
+     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
+     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
+     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
+     * @param useFocusDimmer determines if the FocusHighlighter will use the dimmer
+     */
+    public ListRowPresenter(int focusZoomFactor, boolean useFocusDimmer) {
+        if (!FocusHighlightHelper.isValidZoomIndex(focusZoomFactor)) {
+            throw new IllegalArgumentException("Unhandled zoom factor");
+        }
+        mFocusZoomFactor = focusZoomFactor;
+        mUseFocusDimmer = useFocusDimmer;
     }
 
     /**
@@ -159,8 +229,22 @@
     /**
      * Returns the zoom factor used for focus highlighting.
      */
-    public final int getZoomFactor() {
-        return mZoomFactor;
+    public final int getFocusZoomFactor() {
+        return mFocusZoomFactor;
+    }
+    /**
+     * Returns the zoom factor used for focus highlighting.
+     * @deprecated use {@link #getFocusZoomFactor} instead.
+     */
+    @Deprecated public final int getZoomFactor() {
+        return mFocusZoomFactor;
+    }
+
+    /**
+     * Returns true if the focus dimmer is used for focus highlighting; false otherwise.
+     */
+    public final boolean isFocusDimmerUsed() {
+        return mUseFocusDimmer;
     }
 
     private ItemBridgeAdapter.Wrapper mCardWrapper = new ItemBridgeAdapter.Wrapper() {
@@ -184,15 +268,16 @@
     protected void initializeRowViewHolder(RowPresenter.ViewHolder holder) {
         super.initializeRowViewHolder(holder);
         final ViewHolder rowViewHolder = (ViewHolder) holder;
+        rowViewHolder.mItemBridgeAdapter = new ListRowPresenterItemBridgeAdapter(rowViewHolder);
         if (needsDefaultListSelectEffect() || needsDefaultShadow()
                 || areChildRoundedCornersEnabled()) {
             rowViewHolder.mItemBridgeAdapter.setWrapper(mCardWrapper);
         }
-        if (needsDefaultListSelectEffect()) {
+        if (needsDefaultShadow()) {
             ShadowOverlayContainer.prepareParentForShadow(rowViewHolder.mGridView);
         }
         FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter,
-                mZoomFactor, false);
+                mFocusZoomFactor, mUseFocusDimmer);
         rowViewHolder.mGridView.setFocusDrawingOrderEnabled(!isUsingZOrder());
         rowViewHolder.mGridView.setOnChildSelectedListener(
                 new OnChildSelectedListener() {
@@ -201,51 +286,16 @@
                 selectChildView(rowViewHolder, view);
             }
         });
-        rowViewHolder.mItemBridgeAdapter.setAdapterListener(
-                new ItemBridgeAdapter.AdapterListener() {
+        rowViewHolder.mGridView.setOnUnhandledKeyListener(
+                new BaseGridView.OnUnhandledKeyListener() {
             @Override
-            public void onBind(final ItemBridgeAdapter.ViewHolder viewHolder) {
-                // Only when having an OnItemClickListner, we will attach the OnClickListener.
-                if (getOnItemClickedListener() != null || getOnItemViewClickedListener() != null) {
-                    viewHolder.mHolder.view.setOnClickListener(new View.OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
-                                    rowViewHolder.mGridView
-                                            .getChildViewHolder(viewHolder.itemView);
-                            if (getOnItemClickedListener() != null) {
-                                getOnItemClickedListener().onItemClicked(ibh.mItem,
-                                        (ListRow) rowViewHolder.mRow);
-                            }
-                            if (getOnItemViewClickedListener() != null) {
-                                getOnItemViewClickedListener().onItemClicked(viewHolder.mHolder,
-                                        ibh.mItem, rowViewHolder, (ListRow) rowViewHolder.mRow);
-                            }
-                        }
-                    });
+            public boolean onUnhandledKey(KeyEvent event) {
+                if (rowViewHolder.getOnKeyListener() != null &&
+                        rowViewHolder.getOnKeyListener().onKey(
+                                rowViewHolder.view, event.getKeyCode(), event)) {
+                    return true;
                 }
-            }
-
-            @Override
-            public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
-                if (getOnItemClickedListener() != null || getOnItemViewClickedListener() != null) {
-                    viewHolder.mHolder.view.setOnClickListener(null);
-                }
-            }
-
-            @Override
-            public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-                if (viewHolder.itemView instanceof ShadowOverlayContainer) {
-                    int dimmedColor = rowViewHolder.mColorDimmer.getPaint().getColor();
-                    ((ShadowOverlayContainer) viewHolder.itemView).setOverlayColor(dimmedColor);
-                }
-                viewHolder.itemView.setActivated(rowViewHolder.mExpanded);
-            }
-
-            @Override
-            public void onAddPresenter(Presenter presenter, int type) {
-                rowViewHolder.getGridView().getRecycledViewPool().setMaxRecycledViews(
-                        type, getRecycledPoolSize(presenter));
+                return false;
             }
         });
     }
@@ -287,33 +337,27 @@
      * Perform operations when a child of horizontal grid view is selected.
      */
     private void selectChildView(ViewHolder rowViewHolder, View view) {
-        ItemBridgeAdapter.ViewHolder ibh = null;
         if (view != null) {
-            ibh = (ItemBridgeAdapter.ViewHolder)
-                    rowViewHolder.mGridView.getChildViewHolder(view);
-        }
-        if (view == null) {
+            if (rowViewHolder.mExpanded && rowViewHolder.mSelected) {
+                ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
+                        rowViewHolder.mGridView.getChildViewHolder(view);
+
+                if (mHoverCardPresenterSelector != null) {
+                    rowViewHolder.mHoverCardViewSwitcher.select(
+                            rowViewHolder.mGridView, view, ibh.mItem);
+                }
+                if (rowViewHolder.getOnItemViewSelectedListener() != null) {
+                    rowViewHolder.getOnItemViewSelectedListener().onItemSelected(
+                            ibh.mHolder, ibh.mItem, rowViewHolder, rowViewHolder.mRow);
+                }
+            }
+        } else {
             if (mHoverCardPresenterSelector != null) {
                 rowViewHolder.mHoverCardViewSwitcher.unselect();
             }
-            if (getOnItemViewSelectedListener() != null) {
-                getOnItemViewSelectedListener().onItemSelected(null, null,
-                        rowViewHolder, rowViewHolder.mRow);
-            }
-            if (getOnItemSelectedListener() != null) {
-                getOnItemSelectedListener().onItemSelected(null, rowViewHolder.mRow);
-            }
-        } else if (rowViewHolder.mExpanded && rowViewHolder.mSelected) {
-            if (mHoverCardPresenterSelector != null) {
-                rowViewHolder.mHoverCardViewSwitcher.select(rowViewHolder.mGridView, view,
-                        ibh.mItem);
-            }
-            if (getOnItemViewSelectedListener() != null) {
-                getOnItemViewSelectedListener().onItemSelected(ibh.mHolder, ibh.mItem,
-                        rowViewHolder, rowViewHolder.mRow);
-            }
-            if (getOnItemSelectedListener() != null) {
-                getOnItemSelectedListener().onItemSelected(ibh.mItem, rowViewHolder.mRow);
+            if (rowViewHolder.getOnItemViewSelectedListener() != null) {
+                rowViewHolder.getOnItemViewSelectedListener().onItemSelected(
+                        null, null, rowViewHolder, rowViewHolder.mRow);
             }
         }
     }
@@ -372,6 +416,28 @@
         return new ViewHolder(rowView, rowView.getGridView(), this);
     }
 
+    /**
+     * Dispatch item selected event using current selected item in the {@link HorizontalGridView}.
+     * The method should only be called from onRowViewSelected().
+     */
+    @Override
+    protected void dispatchItemSelectedListener(RowPresenter.ViewHolder holder, boolean selected) {
+        ViewHolder vh = (ViewHolder)holder;
+        ItemBridgeAdapter.ViewHolder itemViewHolder = (ItemBridgeAdapter.ViewHolder)
+                vh.mGridView.findViewHolderForPosition(vh.mGridView.getSelectedPosition());
+        if (itemViewHolder == null) {
+            super.dispatchItemSelectedListener(holder, selected);
+            return;
+        }
+
+        if (selected) {
+            if (holder.getOnItemViewSelectedListener() != null) {
+                holder.getOnItemViewSelectedListener().onItemSelected(
+                        itemViewHolder.getViewHolder(), itemViewHolder.mItem, vh, vh.getRow());
+            }
+        }
+    }
+
     @Override
     protected void onRowViewSelected(RowPresenter.ViewHolder holder, boolean selected) {
         super.onRowViewSelected(holder, selected);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingView.java b/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingView.java
new file mode 100644
index 0000000..2566e58
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingView.java
@@ -0,0 +1,43 @@
+/*
+ * 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.util.AttributeSet;
+import android.view.View;
+
+public class NonOverlappingView extends View {
+    public NonOverlappingView(Context context) {
+        this(context, null);
+    }
+
+    public NonOverlappingView(Context context, AttributeSet attrs) {
+        super(context, attrs, 0);
+    }
+
+    public NonOverlappingView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    /**
+     * Avoid creating hardware layer when Transition is animating alpha.
+     */
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/OnChildLaidOutListener.java b/v17/leanback/src/android/support/v17/leanback/widget/OnChildLaidOutListener.java
new file mode 100644
index 0000000..158e719
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/OnChildLaidOutListener.java
@@ -0,0 +1,35 @@
+/*
+ * 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.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Interface definition for a callback to be invoked when a child of this
+ * ViewGroup has been laid out.
+ */
+public interface OnChildLaidOutListener {
+    /**
+     * Callback method to be invoked when a child of this ViewGroup has been
+     * added to view hierarchy laid out.
+     *
+     * @param parent The ViewGroup where the layout happened.
+     * @param view The view within the ViewGroup that is selected, or null if no
+     *        view is selected.
+     * @param position The position of the view in the adapter.
+     * @param id The id of the child.
+     */
+    void onChildLaidOut(ViewGroup parent, View view, int position, long id);
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/OnItemClickedListener.java b/v17/leanback/src/android/support/v17/leanback/widget/OnItemClickedListener.java
deleted file mode 100644
index c6b33b5..0000000
--- a/v17/leanback/src/android/support/v17/leanback/widget/OnItemClickedListener.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2014 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.view.View;
-
-/**
- * Interface for receiving notification when a item is clicked.
- *
- * @deprecated Uses {@link OnItemViewClickedListener}
- */
-public interface OnItemClickedListener {
-
-    public void onItemClicked(Object item, Row row);
-
-}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/OnItemSelectedListener.java b/v17/leanback/src/android/support/v17/leanback/widget/OnItemSelectedListener.java
deleted file mode 100644
index 7d46e8f..0000000
--- a/v17/leanback/src/android/support/v17/leanback/widget/OnItemSelectedListener.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2014 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;
-
-/**
- * Interface for receiving notification when a row or item becomes selected.
- *
- * @deprecated Use {@link OnItemViewSelectedListener}
- */
-public interface OnItemSelectedListener {
-    /**
-     * Called when the a row or a new item becomes selected.  The concept of current selection
-     * is different than focus.  Row or item can be selected even they don't have focus.
-     * Having the concept of selection will allow developer to switch background to selected
-     * item or selected row when user selects rows outside row UI (e.g. headers left of
-     * rows).
-     * <p>
-     * For a none {@link ListRow} case,  parameter item is always null.  Event is fired when
-     * selection changes between rows, regardless if row view has focus or not.
-     * <p>
-     * For a {@link ListRow} case, parameter item can be null if the list row is empty.
-     * </p>
-     * <p>
-     * In the case of a grid, the row parameter is always null.
-     * </p>
-     * <li>
-     * Row has focus: event is fired when focus changes between child of the row.
-     * </li>
-     * <li>
-     * None of the row has focus: the event is fired with the current selected row and last
-     * focused item in the row.
-     * </li>
-     *
-     * @param item The item that is currently selected.
-     * @param row The row that is currently selected.
-     */
-    public void onItemSelected(Object item, Row row);
-}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
index 9027e6e..cde4a7a 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
@@ -20,6 +20,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -225,7 +226,7 @@
         return mMoreActionsEnabled;
     }
 
-    public void setProgressColor(ViewHolder vh, int color) {
+    public void setProgressColor(ViewHolder vh, @ColorInt int color) {
         Drawable drawable = new ClipDrawable(new ColorDrawable(color),
                 Gravity.LEFT, ClipDrawable.HORIZONTAL);
         ((LayerDrawable) vh.mProgressBar.getProgressDrawable())
@@ -300,8 +301,10 @@
     public void onUnbindViewHolder(Presenter.ViewHolder holder) {
         super.onUnbindViewHolder(holder);
         ViewHolder vh = (ViewHolder) holder;
-        vh.mMoreActionsAdapter.unregisterObserver(vh.mMoreActionsObserver);
-        vh.mMoreActionsAdapter = null;
+        if (vh.mMoreActionsAdapter != null) {
+            vh.mMoreActionsAdapter.unregisterObserver(vh.mMoreActionsObserver);
+            vh.mMoreActionsAdapter = null;
+        }
     }
 
     int getChildMarginBigger(Context context) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
index ddac678..aa10dc3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
@@ -26,6 +26,7 @@
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.view.KeyEvent;
 
 /**
  * A row of playback controls to be displayed by a {@link PlaybackControlsRowPresenter}.
@@ -183,6 +184,9 @@
             labels[PLAY] = context.getString(R.string.lb_playback_controls_play);
             labels[PAUSE] = context.getString(R.string.lb_playback_controls_pause);
             setLabels(labels);
+            addKeyCode(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+            addKeyCode(KeyEvent.KEYCODE_MEDIA_PLAY);
+            addKeyCode(KeyEvent.KEYCODE_MEDIA_PAUSE);
         }
     }
 
@@ -229,6 +233,7 @@
             }
             setLabels(labels);
             setSecondaryLabels(labels2);
+            addKeyCode(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
         }
     }
 
@@ -275,6 +280,7 @@
             }
             setLabels(labels);
             setSecondaryLabels(labels2);
+            addKeyCode(KeyEvent.KEYCODE_MEDIA_REWIND);
         }
     }
 
@@ -291,6 +297,7 @@
             setIcon(getStyledDrawable(context,
                     R.styleable.lbPlaybackControlsActionIcons_skip_next));
             setLabel1(context.getString(R.string.lb_playback_controls_skip_next));
+            addKeyCode(KeyEvent.KEYCODE_MEDIA_NEXT);
         }
     }
 
@@ -307,6 +314,7 @@
             setIcon(getStyledDrawable(context,
                     R.styleable.lbPlaybackControlsActionIcons_skip_previous));
             setLabel1(context.getString(R.string.lb_playback_controls_skip_previous));
+            addKeyCode(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
         }
     }
 
@@ -406,8 +414,7 @@
          * @param context Context used for loading resources.
          */
         public RepeatAction(Context context) {
-            this(context, getColorFromTheme(context,
-                    R.attr.playbackControlsIconHighlightColor));
+            this(context, getIconHighlightColor(context));
         }
 
         /**
@@ -433,10 +440,12 @@
             BitmapDrawable repeatOneDrawable = (BitmapDrawable) getStyledDrawable(context,
                     R.styleable.lbPlaybackControlsActionIcons_repeat_one);
             drawables[NONE] = repeatDrawable;
-            drawables[ALL] = new BitmapDrawable(context.getResources(),
-                    createBitmap(repeatDrawable.getBitmap(), repeatAllColor));
-            drawables[ONE] = new BitmapDrawable(context.getResources(),
-                    createBitmap(repeatOneDrawable.getBitmap(), repeatOneColor));
+            drawables[ALL] = repeatDrawable == null ? null
+                    : new BitmapDrawable(context.getResources(),
+                            createBitmap(repeatDrawable.getBitmap(), repeatAllColor));
+            drawables[ONE] = repeatOneDrawable == null ? null
+                    : new BitmapDrawable(context.getResources(),
+                            createBitmap(repeatOneDrawable.getBitmap(), repeatOneColor));
             setDrawables(drawables);
 
             String[] labels = new String[drawables.length];
@@ -460,8 +469,7 @@
          * @param context Context used for loading resources.
          */
         public ShuffleAction(Context context) {
-            this(context, getColorFromTheme(context,
-                    R.attr.playbackControlsIconHighlightColor));
+            this(context, getIconHighlightColor(context));
         }
 
         /**
@@ -498,8 +506,7 @@
          * @param context Context used for loading resources.
          */
         public HighQualityAction(Context context) {
-            this(context, getColorFromTheme(context,
-                    R.attr.playbackControlsIconHighlightColor));
+            this(context, getIconHighlightColor(context));
         }
 
         /**
@@ -536,8 +543,7 @@
          * @param context Context used for loading resources.
          */
         public ClosedCaptioningAction(Context context) {
-            this(context, getColorFromTheme(context,
-                    R.attr.playbackControlsIconHighlightColor));
+            this(context, getIconHighlightColor(context));
         }
 
         /**
@@ -571,16 +577,21 @@
         return dst;
     }
 
-    private static int getColorFromTheme(Context context, int attributeResId) {
+    private static int getIconHighlightColor(Context context) {
         TypedValue outValue = new TypedValue();
-        context.getTheme().resolveAttribute(attributeResId, outValue, true);
-        return outValue.data;
+        if (context.getTheme().resolveAttribute(R.attr.playbackControlsIconHighlightColor,
+                outValue, true)) {
+            return outValue.data;
+        }
+        return context.getResources().getColor(R.color.lb_playback_icon_highlight_no_theme);
     }
 
     private static Drawable getStyledDrawable(Context context, int index) {
         TypedValue outValue = new TypedValue();
-        context.getTheme().resolveAttribute(
-                R.attr.playbackControlsActionIcons, outValue, false);
+        if (!context.getTheme().resolveAttribute(
+                R.attr.playbackControlsActionIcons, outValue, false)) {
+            return null;
+        }
         TypedArray array = context.getTheme().obtainStyledAttributes(outValue.data,
                 R.styleable.lbPlaybackControlsActionIcons);
         Drawable drawable = array.getDrawable(index);
@@ -729,6 +740,34 @@
         return mBufferedProgressMs;
     }
 
+    /**
+     * Returns the Action associated with the given keycode, or null if no associated action exists.
+     * Searches the primary adapter first, then the secondary adapter.
+     */
+    public Action getActionForKeyCode(int keyCode) {
+        Action action = getActionForKeyCode(getPrimaryActionsAdapter(), keyCode);
+        if (action != null) {
+            return action;
+        }
+        return getActionForKeyCode(getSecondaryActionsAdapter(), keyCode);
+    }
+
+    /**
+     * Returns the Action associated with the given keycode, or null if no associated action exists.
+     */
+    public Action getActionForKeyCode(ObjectAdapter adapter, int keyCode) {
+        if (adapter != mPrimaryActionsAdapter && adapter != mSecondaryActionsAdapter) {
+            throw new IllegalArgumentException("Invalid adapter");
+        }
+        for (int i = 0; i < adapter.size(); i++) {
+            Action action = (Action) adapter.get(i);
+            if (action.respondsToKeyCode(keyCode)) {
+                return action;
+            }
+        }
+        return null;
+    }
+
     interface OnPlaybackStateChangedListener {
         public void onCurrentTimeChanged(int currentTimeMs);
         public void onBufferedProgressChanged(int bufferedProgressMs);
@@ -737,14 +776,14 @@
     /**
      * Sets a listener to be called when the playback state changes.
      */
-    public void setOnPlaybackStateChangedListener(OnPlaybackStateChangedListener listener) {
+    void setOnPlaybackStateChangedListener(OnPlaybackStateChangedListener listener) {
         mListener = listener;
     }
 
     /**
      * Returns the playback state listener.
      */
-    public OnPlaybackStateChangedListener getOnPlaybackStateChangedListener() {
+    OnPlaybackStateChangedListener getOnPlaybackStateChangedListener() {
         return mListener;
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
index 9c62a1b..5d0ad33 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
@@ -13,18 +13,22 @@
  */
 package android.support.v17.leanback.widget;
 
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.widget.ControlBarPresenter.OnControlClickedListener;
 import android.support.v17.leanback.widget.ControlBarPresenter.OnControlSelectedListener;
 import android.content.Context;
 import android.graphics.Color;
 import android.util.TypedValue;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 
 /**
  * A PlaybackControlsRowPresenter renders a {@link PlaybackControlsRow} to display a
@@ -44,6 +48,7 @@
     public class ViewHolder extends RowPresenter.ViewHolder {
         public final Presenter.ViewHolder mDescriptionViewHolder;
         final ViewGroup mCard;
+        final ViewGroup mCardRightPanel;
         final ImageView mImageView;
         final ViewGroup mDescriptionDock;
         final ViewGroup mControlsDock;
@@ -51,7 +56,6 @@
         final View mSpacer;
         final View mBottomSpacer;
         View mBgView;
-        int mCardHeight;
         int mControlsDockMarginStart;
         int mControlsDockMarginEnd;
         PlaybackControlsPresenter.ViewHolder mControlsVh;
@@ -75,6 +79,7 @@
         ViewHolder(View rootView, Presenter descriptionPresenter) {
             super(rootView);
             mCard = (ViewGroup) rootView.findViewById(R.id.controls_card);
+            mCardRightPanel = (ViewGroup) rootView.findViewById(R.id.controls_card_right_panel);
             mImageView = (ImageView) rootView.findViewById(R.id.image);
             mDescriptionDock = (ViewGroup) rootView.findViewById(R.id.description_dock);
             mControlsDock = (ViewGroup) rootView.findViewById(R.id.controls_dock);
@@ -94,17 +99,11 @@
                 return;
             }
             if (mSelectedViewHolder == null) {
-                if (getOnItemSelectedListener() != null) {
-                    getOnItemSelectedListener().onItemSelected(null, getRow());
-                }
                 if (getOnItemViewSelectedListener() != null) {
                     getOnItemViewSelectedListener().onItemSelected(null, null,
                             ViewHolder.this, getRow());
                 }
             } else {
-                if (getOnItemSelectedListener() != null) {
-                    getOnItemSelectedListener().onItemSelected(mSelectedItem, getRow());
-                }
                 if (getOnItemViewSelectedListener() != null) {
                     getOnItemViewSelectedListener().onItemSelected(mSelectedViewHolder, mSelectedItem,
                             ViewHolder.this, getRow());
@@ -176,11 +175,8 @@
         public void onControlClicked(Presenter.ViewHolder itemViewHolder, Object item,
                 ControlBarPresenter.BoundData data) {
             ViewHolder vh = ((BoundData) data).mRowViewHolder;
-            if (getOnItemClickedListener() != null) {
-                getOnItemClickedListener().onItemClicked(item, vh.getRow());
-            }
-            if (getOnItemViewClickedListener() != null) {
-                getOnItemViewClickedListener().onItemClicked(itemViewHolder, item,
+            if (vh.getOnItemViewClickedListener() != null) {
+                vh.getOnItemViewClickedListener().onItemClicked(itemViewHolder, item,
                         vh, vh.getRow());
             }
             if (mOnActionClickedListener != null && item instanceof Action) {
@@ -232,7 +228,7 @@
     /**
      * Sets the background color.  If not set, a default from the theme will be used.
      */
-    public void setBackgroundColor(int color) {
+    public void setBackgroundColor(@ColorInt int color) {
         mBackgroundColor = color;
         mBackgroundColorSet = true;
     }
@@ -241,6 +237,7 @@
      * Returns the background color.  If no background color was set, transparent
      * is returned.
      */
+    @ColorInt
     public int getBackgroundColor() {
         return mBackgroundColor;
     }
@@ -249,7 +246,7 @@
      * Sets the primary color for the progress bar.  If not set, a default from
      * the theme will be used.
      */
-    public void setProgressColor(int color) {
+    public void setProgressColor(@ColorInt int color) {
         mProgressColor = color;
         mProgressColorSet = true;
     }
@@ -258,6 +255,7 @@
      * Returns the primary color for the progress bar.  If no color was set, transparent
      * is returned.
      */
+    @ColorInt
     public int getProgressColor() {
         return mProgressColor;
     }
@@ -296,14 +294,19 @@
 
     private int getDefaultBackgroundColor(Context context) {
         TypedValue outValue = new TypedValue();
-        context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true);
-        return context.getResources().getColor(outValue.resourceId);
+        if (context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) {
+            return context.getResources().getColor(outValue.resourceId);
+        }
+        return context.getResources().getColor(R.color.lb_default_brand_color);
     }
 
     private int getDefaultProgressColor(Context context) {
         TypedValue outValue = new TypedValue();
-        context.getTheme().resolveAttribute(R.attr.playbackProgressPrimaryColor, outValue, true);
-        return context.getResources().getColor(outValue.resourceId);
+        if (context.getTheme()
+                .resolveAttribute(R.attr.playbackProgressPrimaryColor, outValue, true)) {
+            return context.getResources().getColor(outValue.resourceId);
+        }
+        return context.getResources().getColor(R.color.lb_playback_progress_color_no_theme);
     }
 
     @Override
@@ -315,9 +318,7 @@
         return vh;
     }
 
-    private void initRow(ViewHolder vh) {
-        vh.mCardHeight = vh.mCard.getLayoutParams().height;
-
+    private void initRow(final ViewHolder vh) {
         MarginLayoutParams lp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
         vh.mControlsDockMarginStart = lp.getMarginStart();
         vh.mControlsDockMarginEnd = lp.getMarginEnd();
@@ -334,6 +335,18 @@
         if (!mSecondaryActionsHidden) {
             vh.mSecondaryControlsDock.addView(vh.mSecondaryControlsVh.view);
         }
+        ((PlaybackControlsRowView) vh.view).setOnUnhandledKeyListener(
+                new PlaybackControlsRowView.OnUnhandledKeyListener() {
+            @Override
+            public boolean onUnhandledKey(KeyEvent event) {
+                if (vh.getOnKeyListener() != null) {
+                    if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        });
     }
 
     @Override
@@ -346,15 +359,9 @@
         mPlaybackControlsPresenter.enableSecondaryActions(mSecondaryActionsHidden);
 
         if (row.getItem() == null) {
-            LayoutParams lp = vh.mCard.getLayoutParams();
-            lp.height = LayoutParams.WRAP_CONTENT;
-            vh.mCard.setLayoutParams(lp);
             vh.mDescriptionDock.setVisibility(View.GONE);
             vh.mSpacer.setVisibility(View.GONE);
         } else {
-            LayoutParams lp = vh.mCard.getLayoutParams();
-            lp.height = vh.mCardHeight;
-            vh.mCard.setLayoutParams(lp);
             vh.mDescriptionDock.setVisibility(View.VISIBLE);
             if (vh.mDescriptionViewHolder != null) {
                 mDescriptionPresenter.onBindViewHolder(vh.mDescriptionViewHolder, row.getItem());
@@ -362,21 +369,13 @@
             vh.mSpacer.setVisibility(View.VISIBLE);
         }
 
-        MarginLayoutParams lp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
         if (row.getImageDrawable() == null || row.getItem() == null) {
             vh.mImageView.setImageDrawable(null);
-            vh.setBackground(vh.mControlsDock);
-            lp.setMarginStart(0);
-            lp.setMarginEnd(0);
-            mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, true);
+            updateCardLayout(vh, LayoutParams.WRAP_CONTENT);
         } else {
             vh.mImageView.setImageDrawable(row.getImageDrawable());
-            vh.setBackground(vh.mCard);
-            lp.setMarginStart(vh.mControlsDockMarginStart);
-            lp.setMarginEnd(vh.mControlsDockMarginEnd);
-            mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, false);
+            updateCardLayout(vh, vh.mImageView.getLayoutParams().height);
         }
-        vh.mControlsDock.setLayoutParams(lp);
 
         vh.mControlsBoundData.adapter = row.getPrimaryActionsAdapter();
         vh.mControlsBoundData.secondaryActionsAdapter = row.getSecondaryActionsAdapter();
@@ -396,6 +395,33 @@
         row.setOnPlaybackStateChangedListener(vh.mListener);
     }
 
+    private void updateCardLayout(ViewHolder vh, int height) {
+        LayoutParams lp = vh.mCardRightPanel.getLayoutParams();
+        lp.height = height;
+        vh.mCardRightPanel.setLayoutParams(lp);
+
+        MarginLayoutParams mlp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
+        LinearLayout.LayoutParams llp =
+                (LinearLayout.LayoutParams) vh.mDescriptionDock.getLayoutParams();
+
+        if (height == LayoutParams.WRAP_CONTENT) {
+            llp.height = LayoutParams.WRAP_CONTENT;
+            mlp.setMarginStart(0);
+            mlp.setMarginEnd(0);
+            vh.setBackground(vh.mControlsDock);
+            mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, true);
+        } else {
+            llp.height = 0;
+            llp.weight = 1;
+            mlp.setMarginStart(vh.mControlsDockMarginStart);
+            mlp.setMarginEnd(vh.mControlsDockMarginEnd);
+            vh.setBackground(vh.mCard);
+            mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, false);
+        }
+        vh.mDescriptionDock.setLayoutParams(llp);
+        vh.mControlsDock.setLayoutParams(mlp);
+    }
+
     @Override
     protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
         ViewHolder vh = (ViewHolder) holder;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowView.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowView.java
new file mode 100644
index 0000000..0732d40
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowView.java
@@ -0,0 +1,75 @@
+/*
+ * 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.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * A LinearLayout that preserves the focused child view.
+ * @hide
+ */
+class PlaybackControlsRowView extends LinearLayout {
+    public interface OnUnhandledKeyListener {
+        /**
+         * Returns true if the key event should be consumed.
+         */
+        public boolean onUnhandledKey(KeyEvent event);
+    }
+
+    private OnUnhandledKeyListener mOnUnhandledKeyListener;
+
+    public PlaybackControlsRowView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public PlaybackControlsRowView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public void setOnUnhandledKeyListener(OnUnhandledKeyListener listener) {
+         mOnUnhandledKeyListener = listener;
+    }
+
+    public OnUnhandledKeyListener getOnUnhandledKeyListener() {
+        return mOnUnhandledKeyListener;
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (super.dispatchKeyEvent(event)) {
+            return true;
+        }
+        if (mOnUnhandledKeyListener != null && mOnUnhandledKeyListener.onUnhandledKey(event)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+        final View focused = findFocus();
+        if (focused != null && focused.requestFocus(direction, previouslyFocusedRect)) {
+            return true;
+        }
+        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java b/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java
index 4de58ea..a8ea24c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java
@@ -17,6 +17,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -77,7 +78,7 @@
         invalidate();
     }
 
-    public void setForegroundColor(int color) {
+    public void setForegroundColor(@ColorInt int color) {
         if (mForeground instanceof ColorDrawable) {
             ((ColorDrawable) mForeground.mutate()).setColor(color);
             invalidate();
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
index 886bf89..a56aea6 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
@@ -29,6 +29,7 @@
 
     private final int mLayoutResourceId;
     private final Paint mFontMeasurePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private boolean mNullItemVisibilityGone;
 
     public RowHeaderPresenter() {
         this(R.layout.lb_row_header);
@@ -41,6 +42,20 @@
         mLayoutResourceId = layoutResourceId;
     }
 
+    /**
+     * Optionally sets the view visibility to {@link View#GONE} when bound to null.
+     */
+    public void setNullItemVisibilityGone(boolean nullItemVisibilityGone) {
+        mNullItemVisibilityGone = nullItemVisibilityGone;
+    }
+
+    /**
+     * Returns true if the view visibility is set to {@link View#GONE} when bound to null.
+     */
+    public boolean isNullItemVisibilityGone() {
+        return mNullItemVisibilityGone;
+    }
+
     public static class ViewHolder extends Presenter.ViewHolder {
         float mSelectLevel;
         int mOriginalTextColor;
@@ -69,18 +84,21 @@
     @Override
     public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
         setSelectLevel((ViewHolder) viewHolder, 0);
-        Row rowItem = (Row) item;
-        if (rowItem != null) {
-            HeaderItem headerItem = rowItem.getHeaderItem();
-            if (headerItem != null) {
-                String text = headerItem.getName();
-                ((RowHeaderView) viewHolder.view).setText(text);
+        HeaderItem headerItem = item == null ? null : ((Row) item).getHeaderItem();
+        if (headerItem == null) {
+            ((RowHeaderView) viewHolder.view).setText(null);
+            if (mNullItemVisibilityGone) {
+                viewHolder.view.setVisibility(View.GONE);
             }
+        } else {
+            viewHolder.view.setVisibility(View.VISIBLE);
+            ((RowHeaderView) viewHolder.view).setText(headerItem.getName());
         }
     }
 
     @Override
     public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
+        ((RowHeaderView) viewHolder.view).setText(null);
     }
 
     public final void setSelectLevel(ViewHolder holder, float selectLevel) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
index aa774db..30e1b33 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
@@ -35,20 +35,42 @@
  * RowPresenter receives calls from its parent (typically a Fragment) when:
  * <ul>
  * <li>
- * A Row is selected via {@link #setRowViewSelected(Presenter.ViewHolder, boolean)}.  The event
+ * A row is selected via {@link #setRowViewSelected(Presenter.ViewHolder, boolean)}.  The event
  * is triggered immediately when there is a row selection change before the selection
- * animation is started.
+ * animation is started.  Selected status may control activated status of the row (see
+ * "Activated status" below).
  * Subclasses of RowPresenter may override {@link #onRowViewSelected(ViewHolder, boolean)}.
  * </li>
  * <li>
- * A Row is expanded to full width via {@link #setRowViewExpanded(Presenter.ViewHolder, boolean)}.
+ * A row is expanded to full height via {@link #setRowViewExpanded(Presenter.ViewHolder, boolean)}
+ * when BrowseFragment hides fast lane on the left.
  * The event is triggered immediately before the expand animation is started.
+ * Row title is shown when row is expanded.  Expanded status may control activated status
+ * of the row (see "Activated status" below).
  * Subclasses of RowPresenter may override {@link #onRowViewExpanded(ViewHolder, boolean)}.
  * </li>
  * </ul>
  *
+ * <h3>Activated status</h3>
+ * The activated status of a row is applied to the row view and it's children via
+ * {@link View#setActivated(boolean)}.
+ * The activated status is typically used to control {@link BaseCardView} info region visibility.
+ * The row's activated status can be controlled by selected status and/or expanded status.
+ * Call {@link #setSyncActivatePolicy(int)} and choose one of the four policies:
+ * <ul>
+ * <li>{@link #SYNC_ACTIVATED_TO_EXPANDED} Activated status is synced with row expanded status</li>
+ * <li>{@link #SYNC_ACTIVATED_TO_SELECTED} Activated status is synced with row selected status</li>
+ * <li>{@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED} Activated status is set to true
+ *     when both expanded and selected status are true</li>
+ * <li>{@link #SYNC_ACTIVATED_CUSTOM} Activated status is not controlled by selected status
+ *     or expanded status, application can control activated status by its own.
+ *     Application should call {@link RowPresenter.ViewHolder#setActivated(boolean)} to change
+ *     activated status of row view.
+ * </li>
+ * </ul>
+ *
  * <h3>User events</h3>
- * RowPresenter provides {@link OnItemSelectedListener} and {@link OnItemClickedListener}.
+ * RowPresenter provides {@link OnItemViewSelectedListener} and {@link OnItemViewClickedListener}.
  * If a subclass wants to add its own {@link View.OnFocusChangeListener} or
  * {@link View.OnClickListener}, it must do that in {@link #createRowViewHolder(ViewGroup)}
  * to be properly chained by the library.  Adding View listeners after
@@ -72,6 +94,27 @@
  */
 public abstract class RowPresenter extends Presenter {
 
+    /**
+     * Don't synchronize row view activated status with selected status or expanded status,
+     * application will do its own through {@link RowPresenter.ViewHolder#setActivated(boolean)}.
+     */
+    public static final int SYNC_ACTIVATED_CUSTOM = 0;
+
+    /**
+     * Synchronizes row view's activated status to expand status of the row view holder.
+     */
+    public static final int SYNC_ACTIVATED_TO_EXPANDED = 1;
+
+    /**
+     * Synchronizes row view's activated status to selected status of the row view holder.
+     */
+    public static final int SYNC_ACTIVATED_TO_SELECTED = 2;
+
+    /**
+     * Sets the row view's activated status to true when both expand and selected are true.
+     */
+    public static final int SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED = 3;
+
     static class ContainerViewHolder extends Presenter.ViewHolder {
         /**
          * wrapped row view holder
@@ -93,14 +136,22 @@
      * A view holder for a {@link Row}.
      */
     public static class ViewHolder extends Presenter.ViewHolder {
+        private static final int ACTIVATED_NOT_ASSIGNED = 0;
+        private static final int ACTIVATED = 1;
+        private static final int NOT_ACTIVATED = 2;
+
         ContainerViewHolder mContainerViewHolder;
         RowHeaderPresenter.ViewHolder mHeaderViewHolder;
         Row mRow;
+        int mActivated = ACTIVATED_NOT_ASSIGNED;
         boolean mSelected;
         boolean mExpanded;
         boolean mInitialzed;
         float mSelectLevel = 0f; // initially unselected
         protected final ColorOverlayDimmer mColorDimmer;
+        private View.OnKeyListener mOnKeyListener;
+        private OnItemViewSelectedListener mOnItemViewSelectedListener;
+        private OnItemViewClickedListener mOnItemViewClickedListener;
 
         /**
          * Constructor for ViewHolder.
@@ -150,15 +201,95 @@
         public final RowHeaderPresenter.ViewHolder getHeaderViewHolder() {
             return mHeaderViewHolder;
         }
+
+        /**
+         * Sets the row view's activated status.  The status will be applied to children through
+         * {@link #syncActivatedStatus(View)}.  Application should only call this function
+         * when {@link RowPresenter#getSyncActivatePolicy()} is
+         * {@link RowPresenter#SYNC_ACTIVATED_CUSTOM}; otherwise the value will
+         * be overwritten when expanded or selected status changes.
+         */
+        public final void setActivated(boolean activated) {
+            mActivated = activated ? ACTIVATED : NOT_ACTIVATED;
+        }
+
+        /**
+         * Synchronizes the activated status of view to the last value passed through
+         * {@link RowPresenter.ViewHolder#setActivated(boolean)}. No operation if
+         * {@link RowPresenter.ViewHolder#setActivated(boolean)} is never called.  Normally
+         * application does not need to call this method,  {@link ListRowPresenter} automatically
+         * calls this method when a child is attached to list row.   However if
+         * application writes its own custom RowPresenter, it should call this method
+         * when attaches a child to the row view.
+         */
+        public final void syncActivatedStatus(View view) {
+            if (mActivated == ACTIVATED) {
+                view.setActivated(true);
+            } else if (mActivated == NOT_ACTIVATED) {
+                view.setActivated(false);
+            }
+        }
+
+        /**
+         * Sets a key listener.
+         */
+        public void setOnKeyListener(View.OnKeyListener keyListener) {
+            mOnKeyListener = keyListener;
+        }
+
+        /**
+         * Returns the key listener.
+         */
+        public View.OnKeyListener getOnKeyListener() {
+            return mOnKeyListener;
+        }
+
+        /**
+         * Sets the listener for item or row selection.  RowPresenter fires row selection
+         * event with null item.  A subclass of RowPresenter e.g. {@link ListRowPresenter} may
+         * fire a selection event with selected item.
+         */
+        public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
+            mOnItemViewSelectedListener = listener;
+        }
+
+        /**
+         * Returns the listener for item or row selection.
+         */
+        public final OnItemViewSelectedListener getOnItemViewSelectedListener() {
+            return mOnItemViewSelectedListener;
+        }
+
+        /**
+         * Sets the listener for item click event.  RowPresenter does nothing but subclass of
+         * RowPresenter may fire item click event if it has the concept of item.
+         * OnItemViewClickedListener will override {@link View.OnClickListener} that
+         * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
+         */
+        public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
+            mOnItemViewClickedListener = listener;
+        }
+
+        /**
+         * Returns the listener for item click event.
+         */
+        public final OnItemViewClickedListener getOnItemViewClickedListener() {
+            return mOnItemViewClickedListener;
+        }
     }
 
     private RowHeaderPresenter mHeaderPresenter = new RowHeaderPresenter();
-    private OnItemSelectedListener mOnItemSelectedListener;
-    private OnItemClickedListener mOnItemClickedListener;
-    private OnItemViewSelectedListener mOnItemViewSelectedListener;
-    private OnItemViewClickedListener mOnItemViewClickedListener;
 
     boolean mSelectEffectEnabled = true;
+    int mSyncActivatePolicy = SYNC_ACTIVATED_TO_EXPANDED;
+
+
+    /**
+     * Constructs a RowPresenter.
+     */
+    public RowPresenter() {
+        mHeaderPresenter.setNullItemVisibilityGone(true);
+    }
 
     @Override
     public final Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
@@ -201,7 +332,9 @@
     protected void initializeRowViewHolder(ViewHolder vh) {
         vh.mInitialzed = true;
         // set clip children to false for slide transition
-        ((ViewGroup) vh.view).setClipChildren(false);
+        if (vh.view instanceof ViewGroup) {
+            ((ViewGroup) vh.view).setClipChildren(false);
+        }
         if (vh.mContainerViewHolder != null) {
             ((ViewGroup) vh.mContainerViewHolder.view).setClipChildren(false);
         }
@@ -267,7 +400,62 @@
      */
     protected void onRowViewExpanded(ViewHolder vh, boolean expanded) {
         updateHeaderViewVisibility(vh);
-        vh.view.setActivated(expanded);
+        updateActivateStatus(vh, vh.view);
+    }
+
+    /**
+     * Update view's activate status according to {@link #getSyncActivatePolicy()} and the
+     * selected status and expanded status of the RowPresenter ViewHolder.
+     */
+    private void updateActivateStatus(ViewHolder vh, View view) {
+        switch (mSyncActivatePolicy) {
+            case SYNC_ACTIVATED_TO_EXPANDED:
+                vh.setActivated(vh.isExpanded());
+                break;
+            case SYNC_ACTIVATED_TO_SELECTED:
+                vh.setActivated(vh.isSelected());
+                break;
+            case SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED:
+                vh.setActivated(vh.isExpanded() && vh.isSelected());
+                break;
+        }
+        vh.syncActivatedStatus(view);
+    }
+
+    /**
+     * Sets policy of updating row view activated status.  Can be one of:
+     * <li> Default value {@link #SYNC_ACTIVATED_TO_EXPANDED}
+     * <li> {@link #SYNC_ACTIVATED_TO_SELECTED}
+     * <li> {@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED}
+     * <li> {@link #SYNC_ACTIVATED_CUSTOM}
+     */
+    public final void setSyncActivatePolicy(int syncActivatePolicy) {
+        mSyncActivatePolicy = syncActivatePolicy;
+    }
+
+    /**
+     * Returns policy of updating row view activated status.  Can be one of:
+     * <li> Default value {@link #SYNC_ACTIVATED_TO_EXPANDED}
+     * <li> {@link #SYNC_ACTIVATED_TO_SELECTED}
+     * <li> {@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED}
+     * <li> {@link #SYNC_ACTIVATED_CUSTOM}
+     */
+    public final int getSyncActivatePolicy() {
+        return mSyncActivatePolicy;
+    }
+
+
+    /**
+     * The method is only called from onRowViewSelecetd().
+     * Default behavior is signaling row selected events with null item. Subclass of RowPresenter
+     * having child items should override this method and dispatch events with item information.
+     */
+    protected void dispatchItemSelectedListener(ViewHolder vh, boolean selected) {
+        if (selected) {
+            if (vh.mOnItemViewSelectedListener != null) {
+                vh.mOnItemViewSelectedListener.onItemSelected(null, null, vh, vh.getRow());
+            }
+        }
     }
 
     /**
@@ -276,15 +464,9 @@
      * animation on the Row view.
      */
     protected void onRowViewSelected(ViewHolder vh, boolean selected) {
-        if (selected) {
-            if (mOnItemViewSelectedListener != null) {
-                mOnItemViewSelectedListener.onItemSelected(null, null, vh, vh.getRow());
-            }
-            if (mOnItemSelectedListener != null) {
-                mOnItemSelectedListener.onItemSelected(null, vh.getRow());
-            }
-        }
+        dispatchItemSelectedListener(vh, selected);
         updateHeaderViewVisibility(vh);
+        updateActivateStatus(vh, vh.view);
     }
 
     private void updateHeaderViewVisibility(ViewHolder vh) {
@@ -424,76 +606,6 @@
     }
 
     /**
-     * Set the listener for item or row selection. A RowPresenter fires a row
-     * selection event with a null item. Subclasses (e.g. {@link ListRowPresenter})
-     * can fire a selection event with the selected item.
-     */
-    public final void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mOnItemSelectedListener = listener;
-    }
-
-    /**
-     * Get the listener for item or row selection.
-     */
-    public final OnItemSelectedListener getOnItemSelectedListener() {
-        return mOnItemSelectedListener;
-    }
-
-    /**
-     * Set the listener for item click events. A RowPresenter does not use this
-     * listener, but a subclass may fire an item click event if it has the concept
-     * of an item. The {@link OnItemClickedListener} will override any
-     * {@link View.OnClickListener} that an item's Presenter sets during
-     * {@link Presenter#onCreateViewHolder(ViewGroup)}. So in general, you
-     * should choose to use an OnItemClickedListener or a {@link
-     * View.OnClickListener}, but not both.
-     */
-    public final void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-    }
-
-    /**
-     * Get the listener for item click events.
-     */
-    public final OnItemClickedListener getOnItemClickedListener() {
-        return mOnItemClickedListener;
-    }
-
-    /**
-     * Set listener for item or row selection.  RowPresenter fires row selection
-     * event with null item, subclass of RowPresenter e.g. {@link ListRowPresenter} can
-     * fire a selection event with selected item.
-     */
-    public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-        mOnItemViewSelectedListener = listener;
-    }
-
-    /**
-     * Get listener for item or row selection.
-     */
-    public final OnItemViewSelectedListener getOnItemViewSelectedListener() {
-        return mOnItemViewSelectedListener;
-    }
-
-    /**
-     * Set listener for item click event.  RowPresenter does nothing but subclass of
-     * RowPresenter may fire item click event if it does have a concept of item.
-     * OnItemViewClickedListener will override {@link View.OnClickListener} that
-     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
-     * So in general,  developer should choose one of the listeners but not both.
-     */
-    public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        mOnItemViewClickedListener = listener;
-    }
-
-    /**
-     * Set listener for item click event.
-     */
-    public final OnItemViewClickedListener getOnItemViewClickedListener() {
-        return mOnItemViewClickedListener;
-    }
-
-    /**
      * Freeze/Unfreeze the row, typically used when transition starts/ends.
      * This method is called by fragment, app should not call it directly.
      */
@@ -507,7 +619,8 @@
      * app should not call it directly.
      */
     public void setEntranceTransitionState(ViewHolder holder, boolean afterTransition) {
-        if (holder.mHeaderViewHolder != null) {
+        if (holder.mHeaderViewHolder != null &&
+                holder.mHeaderViewHolder.view.getVisibility() != View.GONE) {
             holder.mHeaderViewHolder.view.setVisibility(afterTransition ?
                     View.VISIBLE : View.INVISIBLE);
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java b/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java
index c46ed5e..f4f4836 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java
@@ -98,7 +98,7 @@
             new AudioManager.OnAudioFocusChangeListener() {
                 @Override
                 public void onAudioFocusChange(int focusChange) {
-                    // Do nothing.
+                    stopRecognition();
                 }
             };
 
@@ -318,6 +318,7 @@
 
     @Override
     protected void onDetachedFromWindow() {
+        stopRecognition();
         if (DEBUG) Log.v(TAG, "Releasing SoundPool");
         mSoundPool.release();
         super.onDetachedFromWindow();
@@ -423,6 +424,7 @@
      * @param recognizer a SpeechRecognizer
      */
     public void setSpeechRecognizer(SpeechRecognizer recognizer) {
+        stopRecognition();
         if (null != mSpeechRecognizer) {
             mSpeechRecognizer.setRecognitionListener(null);
             if (mListening) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SearchOrbView.java b/v17/leanback/src/android/support/v17/leanback/widget/SearchOrbView.java
index 1e961f4..1dacf52 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SearchOrbView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SearchOrbView.java
@@ -25,6 +25,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -63,7 +64,7 @@
          *
          * @param color The main search orb color.
          */
-        public Colors(int color) {
+        public Colors(@ColorInt int color) {
             this(color, color);
         }
 
@@ -74,7 +75,7 @@
          * @param color The main search orb color.
          * @param brightColor A brighter version of the search orb used for animation.
          */
-        public Colors(int color, int brightColor) {
+        public Colors(@ColorInt int color, @ColorInt int brightColor) {
             this(color, brightColor, Color.TRANSPARENT);
         }
 
@@ -85,7 +86,7 @@
          * @param brightColor A brighter version of the search orb used for animation.
          * @param iconColor A color used to tint the search orb icon.
          */
-        public Colors(int color, int brightColor, int iconColor) {
+        public Colors(@ColorInt int color, @ColorInt int brightColor, @ColorInt int iconColor) {
             this.color = color;
             this.brightColor = brightColor == color ? getBrightColor(color) : brightColor;
             this.iconColor = iconColor;
@@ -94,16 +95,19 @@
         /**
          * The main color of the search orb.
          */
+        @ColorInt
         public int color;
 
         /**
          * A brighter version of the search orb used for animation.
          */
+        @ColorInt
         public int brightColor;
 
         /**
          * A color used to tint the search orb icon.
          */
+        @ColorInt
         public int iconColor;
 
         /**
@@ -294,7 +298,7 @@
      * @deprecated Use {@link #setOrbColors(Colors)} instead.
      */
     @Deprecated
-    public void setOrbColor(int color, int brightColor) {
+    public void setOrbColor(@ColorInt int color, @ColorInt int brightColor) {
         setOrbColors(new Colors(color, brightColor, Color.TRANSPARENT));
     }
 
@@ -302,6 +306,7 @@
      * Returns the orb color
      * @return the RGBA color
      */
+    @ColorInt
     public int getOrbColor() {
         return mColors.color;
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
index 49d227f..2b5da55 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
@@ -14,6 +14,7 @@
 package android.support.v17.leanback.widget;
 
 import android.content.Context;
+import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -127,7 +128,7 @@
     /**
      * Set color (with alpha) of the overlay.
      */
-    public void setOverlayColor(int overlayColor) {
+    public void setOverlayColor(@ColorInt int overlayColor) {
         if (mColorDimOverlay != null) {
             mColorDimOverlay.setBackgroundColor(overlayColor);
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
index 4b5abe8..2fef48e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
@@ -14,13 +14,14 @@
 package android.support.v17.leanback.widget;
 
 import android.support.v4.util.CircularArray;
+import android.support.v4.util.CircularIntArray;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * A dynamic data structure that maintains staggered grid position information
+ * A dynamic data structure that caches staggered grid position information
  * for each individual child. The algorithm ensures that each row will be kept
  * as balanced as possible when prepending and appending a child.
  *
@@ -32,146 +33,58 @@
  * scrolls back to 0 and we don't keep history location information, edges of
  * the very beginning of rows will not be aligned. It is recommended to keep a
  * list of tens of thousands of {@link StaggeredGrid.Location}s which will be
- * big enough to remember a typical user's scroll history. There are situations
- * where StaggeredGrid falls back to the simple case where we do not need save a
- * huge list of locations inside StaggeredGrid:
- * <ul>
- *   <li>Only one row (e.g., a single row listview)</li>
- *   <li> Each item has the same length (not staggered at all)</li>
- * </ul>
+ * big enough to remember a typical user's scroll history.
  *
  * <p>
  * This class is abstract and can be replaced with different implementations.
  */
-abstract class StaggeredGrid {
+abstract class StaggeredGrid extends Grid {
 
     /**
-     * TODO: document this
+     * Cached representation of Staggered item.
      */
-    public static interface Provider {
+    public static class Location extends Grid.Location {
         /**
-         * Return how many items are in the adapter.
+         * Offset to previous item location.
+         * min_edge(index) - min_edge(index - 1) for non reversed case
+         * max_edge(index) - max_edge(index - 1) for reversed case
          */
-        public abstract int getCount();
+        public int offset;
 
         /**
-         * Create the object at a given row.
+         * size of the item.
          */
-        public abstract void createItem(int index, int row, boolean append);
-    }
+        public int size;
 
-    /**
-     * Location of an item in the grid. For now it only saves row index but
-     * more information may be added in the future.
-     */
-    public final static class Location {
-        /**
-         * The index of the row for this Location.
-         */
-        public final int row;
-
-        /**
-         * Create a Location with the given row index.
-         */
-        public Location(int row) {
-            this.row = row;
+        public Location(int row, int offset, int size) {
+            super(row);
+            this.offset = offset;
+            this.size = size;
         }
     }
 
-    /**
-     * TODO: document this
-     */
-    public final static class Row {
-        /**
-         * first view start location
-         */
-        public int low;
-        /**
-         * last view end location
-         */
-        public int high;
-    }
-
-    protected Provider mProvider;
-    protected int mNumRows = 1; // mRows.length
-    protected Row[] mRows;
     protected CircularArray<Location> mLocations = new CircularArray<Location>(64);
-    private ArrayList<Integer>[] mTmpItemPositionsInRows;
-    protected boolean mReversedFlow;
 
-    /**
-     * A constant representing a default starting index, indicating that the
-     * developer did not provide a start index.
-     */
-    public static final int START_DEFAULT = -1;
-
-    // the first index that grid will layout
-    protected int mStartIndex = START_DEFAULT;
-    // the row to layout the first index
-    protected int mStartRow = START_DEFAULT;
-
+    // mFirstIndex <= mFirstVisibleIndex <= mLastVisibleIndex
+    //    <= mFirstIndex + mLocations.size() - 1
     protected int mFirstIndex = -1;
 
-    public void setReversedFlow(boolean reversedFlow) {
-        mReversedFlow = reversedFlow;
-    }
+    private Object[] mTmpItem = new Object[1];
+
+    protected Object mPendingItem;
+    protected int mPendingItemSize;
 
     /**
-     * Sets the {@link Provider} for this staggered grid.
-     *
-     * @param provider The provider for this staggered grid.
-     */
-    public void setProvider(Provider provider) {
-        mProvider = provider;
-    }
-
-    /**
-     * Sets the array of {@link Row}s to fill into. For views that represent a
-     * horizontal list, this will be the rows of the view. For views that
-     * represent a vertical list, this will be the columns.
-     *
-     * @param row The array of {@link Row}s to be filled.
-     */
-    public final void setRows(Row[] row) {
-        if (row == null || row.length == 0) {
-            throw new IllegalArgumentException();
-        }
-        mNumRows = row.length;
-        mRows = row;
-        mTmpItemPositionsInRows = new ArrayList[mNumRows];
-        for (int i = 0; i < mNumRows; i++) {
-            mTmpItemPositionsInRows[i] = new ArrayList(32);
-        }
-    }
-
-    /**
-     * Returns the number of rows in the staggered grid.
-     */
-    public final int getNumRows() {
-        return mNumRows;
-    }
-
-    /**
-     * Set the first item index and the row index to load when there are no
-     * items.
-     *
-     * @param startIndex the index of the first item
-     * @param startRow the index of the row
-     */
-    public final void setStart(int startIndex, int startRow) {
-        mStartIndex = startIndex;
-        mStartRow = startRow;
-    }
-
-    /**
-     * Returns the first index in the staggered grid.
+     * Returns index of first item (cached or visible) in the staggered grid.
+     * Returns negative value if no item.
      */
     public final int getFirstIndex() {
         return mFirstIndex;
     }
 
     /**
-     * Returns the last index in the staggered grid.
+     * Returns index of last item (cached or visible) in the staggered grid.
+     * Returns negative value if no item.
      */
     public final int getLastIndex() {
         return mFirstIndex + mLocations.size() - 1;
@@ -184,9 +97,7 @@
         return mLocations.size();
     }
 
-    /**
-     * Returns the {@link Location} at the given index.
-     */
+    @Override
     public final Location getLocation(int index) {
         if (mLocations.size() == 0) {
             return null;
@@ -194,21 +105,7 @@
         return mLocations.get(index - mFirstIndex);
     }
 
-    /**
-     * Removes the first element.
-     */
-    public final void removeFirst() {
-        mFirstIndex++;
-        mLocations.popFirst();
-    }
-
-    /**
-     * Removes the last element.
-     */
-    public final void removeLast() {
-        mLocations.popLast();
-    }
-
+    @Override
     public final void debugPrint(PrintWriter pw) {
         for (int i = 0, size = mLocations.size(); i < size; i++) {
             Location loc = mLocations.get(i);
@@ -218,97 +115,326 @@
         }
     }
 
-    protected final int getMaxHighRowIndex() {
-        int maxHighRowIndex = 0;
-        for (int i = 1; i < mNumRows; i++) {
-            if (mRows[i].high > mRows[maxHighRowIndex].high) {
-                maxHighRowIndex = i;
-            }
+    @Override
+    protected final boolean prependVisibleItems(int toLimit, boolean oneColumnMode) {
+        if (mProvider.getCount() == 0) {
+            return false;
         }
-        return maxHighRowIndex;
+        if (!oneColumnMode && checkPrependOverLimit(toLimit)) {
+            return false;
+        }
+        try {
+            if (prependVisbleItemsWithCache(toLimit, oneColumnMode)) {
+                return true;
+            }
+            return prependVisibleItemsWithoutCache(toLimit, oneColumnMode);
+        } finally {
+            mTmpItem[0] = null;
+            mPendingItem = null;
+        }
     }
 
-    protected final int getMinHighRowIndex() {
-        int minHighRowIndex = 0;
-        for (int i = 1; i < mNumRows; i++) {
-            if (mRows[i].high < mRows[minHighRowIndex].high) {
-                minHighRowIndex = i;
-            }
-        }
-        return minHighRowIndex;
-    }
-
-    protected final Location appendItemToRow(int itemIndex, int rowIndex) {
-        Location loc = new Location(rowIndex);
+    /**
+     * Prepends items using cached locations,  returning true if toLimit is reached.
+     * This method should only be called by prependVisibleItems().
+     */
+    protected final boolean prependVisbleItemsWithCache(int toLimit, boolean oneColumnMode) {
         if (mLocations.size() == 0) {
-            mFirstIndex = itemIndex;
+            return false;
         }
-        mLocations.addLast(loc);
-        mProvider.createItem(itemIndex, rowIndex, true);
-        return loc;
+        final int count = mProvider.getCount();
+        final int firstIndex = getFirstIndex();
+        int itemIndex;
+        int edge;
+        int offset;
+        if (mFirstVisibleIndex >= 0) {
+            // prepend visible items from first visible index
+            edge = mProvider.getEdge(mFirstVisibleIndex);
+            offset = getLocation(mFirstVisibleIndex).offset;
+            itemIndex = mFirstVisibleIndex - 1;
+        } else {
+            // prepend first visible item
+            edge = Integer.MAX_VALUE;
+            offset = 0;
+            itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
+            if (itemIndex > getLastIndex() || itemIndex < getFirstIndex() - 1) {
+                // if the item is not within or adjacent to cached items, clear cache.
+                mLocations.clear();
+                return false;
+            } else if (itemIndex < getFirstIndex()) {
+                // if the item is adjacent to first index, should prepend without cache.
+                return false;
+            }
+        }
+        for (; itemIndex >= mFirstIndex; itemIndex--) {
+            Location loc = getLocation(itemIndex);
+            int rowIndex = loc.row;
+            int size = mProvider.createItem(itemIndex, false, mTmpItem);
+            if (size != loc.size) {
+                mLocations.removeFromStart(itemIndex + 1 - mFirstIndex);
+                mFirstIndex = mFirstVisibleIndex;
+                // pending item will be added in prependVisibleItemsWithoutCache
+                mPendingItem = mTmpItem[0];
+                mPendingItemSize = size;
+                return false;
+            }
+            mFirstVisibleIndex = itemIndex;
+            if (mLastVisibleIndex < 0) {
+                mLastVisibleIndex = itemIndex;
+            }
+            mProvider.addItem(mTmpItem[0], itemIndex, size, rowIndex, edge - offset);
+            if (!oneColumnMode && checkPrependOverLimit(toLimit)) {
+                return true;
+            }
+            edge = mProvider.getEdge(itemIndex);
+            offset = loc.offset;
+            // Check limit after filled a full column
+            if (rowIndex == 0) {
+                if (oneColumnMode) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     /**
-     * Append items until the high edge reaches upTo.
+     * Calculate offset of item after last cached item.
      */
-    public abstract void appendItems(int toLimit);
-
-    protected final int getMaxLowRowIndex() {
-        int maxLowRowIndex = 0;
-        for (int i = 1; i < mNumRows; i++) {
-            if (mRows[i].low > mRows[maxLowRowIndex].low) {
-                maxLowRowIndex = i;
+    private int calculateOffsetAfterLastItem(int row) {
+        // Find a cached item in same row, if not found, just use last item.
+        int cachedIndex = getLastIndex();
+        boolean foundCachedItemInSameRow = false;
+        while (cachedIndex >= mFirstIndex) {
+            Location loc = getLocation(cachedIndex);
+            if (loc.row == row) {
+                foundCachedItemInSameRow = true;
+                break;
             }
+            cachedIndex--;
         }
-        return maxLowRowIndex;
+        if (!foundCachedItemInSameRow) {
+            cachedIndex = getLastIndex();
+        }
+        // Assuming the cachedIndex is next to item on the same row, so the
+        // sum of offset of [cachedIndex + 1, itemIndex] should be size of the
+        // cached item plus margin.
+        int offset = isReversedFlow() ?  -getLocation(cachedIndex).size - mMargin:
+                getLocation(cachedIndex).size + mMargin;
+        for (int i = cachedIndex + 1; i <= getLastIndex(); i++) {
+            offset -= getLocation(i).offset;
+        }
+        return offset;
     }
 
-    protected final int getMinLowRowIndex() {
-        int minLowRowIndex = 0;
-        for (int i = 1; i < mNumRows; i++) {
-            if (mRows[i].low < mRows[minLowRowIndex].low) {
-                minLowRowIndex = i;
+
+    /**
+     * This implements the algorithm of layout staggered grid, the method should only be called by
+     * prependVisibleItems().
+     */
+    protected abstract boolean prependVisibleItemsWithoutCache(int toLimit, boolean oneColumnMode);
+
+    /**
+     * Prepends one visible item with new Location info.  Only called from
+     * prependVisibleItemsWithoutCache().
+     */
+    protected final int prependVisibleItemToRow(int itemIndex, int rowIndex, int edge) {
+        int offset;
+        if (mFirstVisibleIndex >= 0) {
+            if (mFirstVisibleIndex != getFirstIndex() || mFirstVisibleIndex != itemIndex + 1) {
+                // should never hit this when we prepend a new item with a new Location object.
+                throw new IllegalStateException();
             }
         }
-        return minLowRowIndex;
-    }
-
-    protected final Location prependItemToRow(int itemIndex, int rowIndex) {
-        Location loc = new Location(rowIndex);
-        mFirstIndex = itemIndex;
+        Location oldFirstLoc = mFirstIndex >= 0 ? getLocation(mFirstIndex) : null;
+        int oldFirstEdge = mProvider.getEdge(mFirstIndex);
+        Location loc = new Location(rowIndex, 0, 0);
         mLocations.addFirst(loc);
-        mProvider.createItem(itemIndex, rowIndex, false);
-        return loc;
+        Object item;
+        if (mPendingItem != null) {
+            loc.size = mPendingItemSize;
+            item = mPendingItem;
+            mPendingItem = null;
+        } else {
+            loc.size = mProvider.createItem(itemIndex, false, mTmpItem);
+            item = mTmpItem[0];
+        }
+        mFirstIndex = mFirstVisibleIndex = itemIndex;
+        if (mLastVisibleIndex < 0) {
+            mLastVisibleIndex = itemIndex;
+        }
+        int thisEdge = !mReversedFlow ? edge - loc.size : edge + loc.size;
+        if (oldFirstLoc != null) {
+            oldFirstLoc.offset = oldFirstEdge - thisEdge;
+        }
+        mProvider.addItem(item, itemIndex, loc.size, rowIndex, thisEdge);
+        return loc.size;
+    }
+
+    @Override
+    protected final boolean appendVisibleItems(int toLimit, boolean oneColumnMode) {
+        if (mProvider.getCount() == 0) {
+            return false;
+        }
+        if (!oneColumnMode && checkAppendOverLimit(toLimit)) {
+            return false;
+        }
+        try {
+            if (appendVisbleItemsWithCache(toLimit, oneColumnMode)) {
+                return true;
+            }
+            return appendVisibleItemsWithoutCache(toLimit, oneColumnMode);
+        } finally {
+            mTmpItem[0] = null;
+            mPendingItem = null;
+        }
     }
 
     /**
-     * Return array of Lists for all rows, each List contains item positions
-     * on that row between startPos(included) and endPositions(included).
-     * Returned value is read only, do not change it.
+     * Appends items using cached locations,  returning true if at least one item is appended
+     * and (oneColumnMode is true or reach limit and aboveIndex).
+     * This method should only be called by appendVisibleItems()
      */
-    public final List<Integer>[] getItemPositionsInRows(int startPos, int endPos) {
+    protected final boolean appendVisbleItemsWithCache(int toLimit, boolean oneColumnMode) {
+        if (mLocations.size() == 0) {
+            return false;
+        }
+        final int count = mProvider.getCount();
+        int itemIndex;
+        int edge;
+        if (mLastVisibleIndex >= 0) {
+            // append visible items from last visible index
+            itemIndex = mLastVisibleIndex + 1;
+            edge = mProvider.getEdge(mLastVisibleIndex);
+        } else {
+            // append first visible item
+            edge = Integer.MAX_VALUE;
+            itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
+            if (itemIndex > getLastIndex() + 1 || itemIndex < getFirstIndex()) {
+                // if the item is not within or adjacent to cached items, clear cache.
+                mLocations.clear();
+                return false;
+            } else if (itemIndex > getLastIndex()) {
+                // if the item is adjacent to first index, should prepend without cache.
+                return false;
+            }
+        }
+        int lastIndex = getLastIndex();
+        for (; itemIndex < count && itemIndex <= lastIndex; itemIndex++) {
+            Location loc = getLocation(itemIndex);
+            if (edge != Integer.MAX_VALUE) {
+                edge = edge + loc.offset;
+            }
+            int rowIndex = loc.row;
+            int size = mProvider.createItem(itemIndex, true, mTmpItem);
+            if (size != loc.size) {
+                loc.size = size;
+                mLocations.removeFromEnd(lastIndex - itemIndex);
+                lastIndex = itemIndex;
+            }
+            mLastVisibleIndex = itemIndex;
+            if (mFirstVisibleIndex < 0) {
+                mFirstVisibleIndex = itemIndex;
+            }
+            mProvider.addItem(mTmpItem[0], itemIndex, size, rowIndex, edge);
+            if (!oneColumnMode && checkAppendOverLimit(toLimit)) {
+                return true;
+            }
+            if (edge == Integer.MAX_VALUE) {
+                edge = mProvider.getEdge(itemIndex);
+            }
+            // Check limit after filled a full column
+            if (rowIndex == mNumRows - 1) {
+                if (oneColumnMode) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * algorithm of layout staggered grid, this method should only be called by
+     * appendVisibleItems().
+     */
+    protected abstract boolean appendVisibleItemsWithoutCache(int toLimit, boolean oneColumnMode);
+
+    /**
+     * Appends one visible item with new Location info.  Only called from
+     * appendVisibleItemsWithoutCache().
+     */
+    protected final int appendVisibleItemToRow(int itemIndex, int rowIndex, int location) {
+        int offset;
+        if (mLastVisibleIndex >= 0) {
+            if (mLastVisibleIndex != getLastIndex() || mLastVisibleIndex != itemIndex - 1) {
+                // should never hit this when we append a new item with a new Location object.
+                throw new IllegalStateException();
+            }
+        }
+        if (mLastVisibleIndex < 0) {
+            // if we append first visible item after existing cached items,  we need update
+            // the offset later when prependVisbleItemsWithCache()
+            if (mLocations.size() > 0 && itemIndex == getLastIndex() + 1) {
+                offset = calculateOffsetAfterLastItem(rowIndex);
+            } else {
+                offset = 0;
+            }
+        } else {
+            offset = location - mProvider.getEdge(mLastVisibleIndex);
+        }
+        Location loc = new Location(rowIndex, offset, 0);
+        mLocations.addLast(loc);
+        Object item;
+        if (mPendingItem != null) {
+            loc.size = mPendingItemSize;
+            item = mPendingItem;
+            mPendingItem = null;
+        } else {
+            loc.size = mProvider.createItem(itemIndex, true, mTmpItem);
+            item = mTmpItem[0];
+        }
+        if (mLocations.size() == 1) {
+            mFirstIndex = mFirstVisibleIndex = mLastVisibleIndex = itemIndex;
+        } else {
+            if (mLastVisibleIndex < 0) {
+                mFirstVisibleIndex = mLastVisibleIndex = itemIndex;
+            } else {
+                mLastVisibleIndex++;
+            }
+        }
+        mProvider.addItem(item, itemIndex, loc.size, rowIndex, location);
+        return loc.size;
+    }
+
+    @Override
+    public final CircularIntArray[] getItemPositionsInRows(int startPos, int endPos) {
         for (int i = 0; i < mNumRows; i++) {
             mTmpItemPositionsInRows[i].clear();
         }
         if (startPos >= 0) {
             for (int i = startPos; i <= endPos; i++) {
-                mTmpItemPositionsInRows[getLocation(i).row].add(i);
+                CircularIntArray row = mTmpItemPositionsInRows[getLocation(i).row];
+                if (row.size() > 0 && row.getLast() == i - 1) {
+                    // update continuous range
+                    row.popLast();
+                    row.addLast(i);
+                } else {
+                    // add single position
+                    row.addLast(i);
+                    row.addLast(i);
+                }
             }
         }
         return mTmpItemPositionsInRows;
     }
 
-    /**
-     * Prepend items until the low edge reaches downTo.
-     */
-    public abstract void prependItems(int toLimit);
+    @Override
+    public void invalidateItemsAfter(int index) {
+        super.invalidateItemsAfter(index);
+        mLocations.removeFromEnd(getLastIndex() - index + 1);
+        if (mLocations.size() == 0) {
+            mFirstIndex = -1;
+        }
+    }
 
-    /**
-     * Strip items, keep a contiguous subset of items; the subset should include
-     * at least one item on every row that currently has at least one item.
-     *
-     * <p>
-     * TODO: document this better
-     */
-    public abstract void stripDownTo(int itemIndex);
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGridDefault.java b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGridDefault.java
index a8b855a..54e03e6 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGridDefault.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGridDefault.java
@@ -21,153 +21,410 @@
  */
 final class StaggeredGridDefault extends StaggeredGrid {
 
+    /**
+     * Returns the max edge value of item (visible or cached) in a row.  This
+     * will be the place to append or prepend item not in cache.
+     */
+    int getRowMax(int rowIndex) {
+        if (mFirstVisibleIndex < 0) {
+            return Integer.MIN_VALUE;
+        }
+        if (mReversedFlow) {
+            int edge = mProvider.getEdge(mFirstVisibleIndex);
+            if (getLocation(mFirstVisibleIndex).row == rowIndex) {
+                return edge;
+            }
+            for (int i = mFirstVisibleIndex + 1; i <= getLastIndex(); i++) {
+                Location loc = getLocation(i);
+                edge += loc.offset;
+                if (loc.row == rowIndex) {
+                    return edge;
+                }
+            }
+        } else {
+            int edge = mProvider.getEdge(mLastVisibleIndex);
+            Location loc = getLocation(mLastVisibleIndex);
+            if (loc.row == rowIndex) {
+                return edge + loc.size;
+            }
+            for (int i = mLastVisibleIndex - 1; i >= getFirstIndex(); i--) {
+                edge -= loc.offset;
+                loc = getLocation(i);
+                if (loc.row == rowIndex) {
+                    return edge + loc.size;
+                }
+            }
+        }
+        return Integer.MIN_VALUE;
+    }
+
+    /**
+     * Returns the min edge value of item (visible or cached) in a row.  This
+     * will be the place to prepend or append item not in cache.
+     */
+    int getRowMin(int rowIndex) {
+        if (mFirstVisibleIndex < 0) {
+            return Integer.MAX_VALUE;
+        }
+        if (mReversedFlow) {
+            int edge = mProvider.getEdge(mLastVisibleIndex);
+            Location loc = getLocation(mLastVisibleIndex);
+            if (loc.row == rowIndex) {
+                return edge - loc.size;
+            }
+            for (int i = mLastVisibleIndex - 1; i >= getFirstIndex(); i--) {
+                edge -= loc.offset;
+                loc = getLocation(i);
+                if (loc.row == rowIndex) {
+                    return edge - loc.size;
+                }
+            }
+        } else {
+            int edge = mProvider.getEdge(mFirstVisibleIndex);
+            if (getLocation(mFirstVisibleIndex).row == rowIndex) {
+                return edge;
+            }
+            for (int i = mFirstVisibleIndex + 1; i <= getLastIndex() ; i++) {
+                Location loc = getLocation(i);
+                edge += loc.offset;
+                if (loc.row == rowIndex) {
+                    return edge;
+                }
+            }
+        }
+        return Integer.MAX_VALUE;
+    }
+
+    /**
+     * Note this method has assumption that item is filled either in the same row
+     * next row of last item.  Search until row index wrapped.
+     */
     @Override
-    public void appendItems(int toLimit) {
-        int count = mProvider.getCount();
+    public int findRowMax(boolean findLarge, int indexLimit, int[] indices) {
+        int value;
+        int edge = mProvider.getEdge(indexLimit);
+        Location loc = getLocation(indexLimit);
+        int row = loc.row;
+        int index = indexLimit;
+        int visitedRows = 1;
+        int visitRow = row;
+        if (mReversedFlow) {
+            value = edge;
+            for (int i = indexLimit + 1; visitedRows < mNumRows && i <= mLastVisibleIndex; i++) {
+                loc = getLocation(i);
+                edge += loc.offset;
+                if (loc.row != visitRow) {
+                    visitRow = loc.row;
+                    visitedRows++;
+                    if (findLarge ? edge > value : edge < value) {
+                        row = visitRow;
+                        value = edge;
+                        index = i;
+                    }
+                }
+            }
+        } else {
+            value = edge + mProvider.getSize(indexLimit);
+            for (int i = indexLimit - 1; visitedRows < mNumRows && i >= mFirstVisibleIndex; i--) {
+                edge -= loc.offset;
+                loc = getLocation(i);
+                if (loc.row != visitRow) {
+                    visitRow = loc.row;
+                    visitedRows++;
+                    int newValue = edge + mProvider.getSize(i);
+                    if (findLarge ? newValue > value : newValue < value) {
+                        row = visitRow;
+                        value = newValue;
+                        index = i;
+                    }
+                }
+            }
+        }
+        if (indices != null) {
+            indices[0] = row;
+            indices[1] = index;
+        }
+        return value;
+    }
+
+    /**
+     * Note this method has assumption that item is filled either in the same row
+     * next row of last item.  Search until row index wrapped.
+     */
+    @Override
+    public int findRowMin(boolean findLarge, int indexLimit, int[] indices) {
+        int value;
+        int edge = mProvider.getEdge(indexLimit);
+        Location loc = getLocation(indexLimit);
+        int row = loc.row;
+        int index = indexLimit;
+        int visitedRows = 1;
+        int visitRow = row;
+        if (mReversedFlow) {
+            value = edge - mProvider.getSize(indexLimit);
+            for (int i = indexLimit - 1; visitedRows < mNumRows && i >= mFirstVisibleIndex; i--) {
+                edge -= loc.offset;
+                loc = getLocation(i);
+                if (loc.row != visitRow) {
+                    visitRow = loc.row;
+                    visitedRows++;
+                    int newValue = edge - mProvider.getSize(i);
+                    if (findLarge ? newValue > value : newValue < value) {
+                        value = newValue;
+                        row = visitRow;
+                        index = i;
+                    }
+                }
+            }
+        } else {
+            value = edge;
+            for (int i = indexLimit + 1; visitedRows < mNumRows && i <= mLastVisibleIndex; i++) {
+                loc = getLocation(i);
+                edge += loc.offset;
+                if (loc.row != visitRow) {
+                    visitRow = loc.row;
+                    visitedRows++;
+                    if (findLarge ? edge > value : edge < value) {
+                        value = edge;
+                        row = visitRow;
+                        index = i;
+                    }
+                }
+            }
+        }
+        if (indices != null) {
+            indices[0] = row;
+            indices[1] = index;
+        }
+        return value;
+    }
+
+    private int findRowEdgeLimitSearchIndex(boolean append) {
+        boolean wrapped = false;
+        if (append) {
+            for (int index = mLastVisibleIndex; index >= mFirstVisibleIndex; index--) {
+                int row = getLocation(index).row;
+                if (row == 0) {
+                    wrapped = true;
+                } else if (wrapped && row == mNumRows - 1) {
+                    return index;
+                }
+            }
+        } else {
+            for (int index = mFirstVisibleIndex; index <= mLastVisibleIndex; index++) {
+                int row = getLocation(index).row;
+                if (row == mNumRows - 1) {
+                    wrapped = true;
+                } else if (wrapped && row == 0) {
+                    return index;
+                }
+            }
+        }
+        return -1;
+    }
+
+    @Override
+    protected boolean appendVisibleItemsWithoutCache(int toLimit, boolean oneColumnMode) {
+        final int count = mProvider.getCount();
         int itemIndex;
         int rowIndex;
-        if (mLocations.size() > 0) {
-            itemIndex = getLastIndex() + 1;
-            rowIndex = (mLocations.getLast().row + 1) % mNumRows;
+        int edgeLimit;
+        boolean edgeLimitIsValid;
+        if (mLastVisibleIndex >= 0) {
+            if (mLastVisibleIndex < getLastIndex()) {
+                // should fill using cache instead
+                return false;
+            }
+            itemIndex = mLastVisibleIndex + 1;
+            rowIndex = getLocation(mLastVisibleIndex).row;
+            // find start item index of "previous column"
+            int edgeLimitSearchIndex = findRowEdgeLimitSearchIndex(true);
+            if (edgeLimitSearchIndex < 0) {
+                // if "previous colummn" is not found, using edgeLimit of
+                // first row currently in grid
+                edgeLimit = Integer.MIN_VALUE;
+                for (int i = 0; i < mNumRows; i++) {
+                    edgeLimit = mReversedFlow ? getRowMin(i) : getRowMax(i);
+                    if (edgeLimit != Integer.MIN_VALUE) {
+                        break;
+                    }
+                }
+            } else {
+                edgeLimit = mReversedFlow ? findRowMin(false, edgeLimitSearchIndex, null) :
+                        findRowMax(true, edgeLimitSearchIndex, null);
+            }
+            if (mReversedFlow ? getRowMin(rowIndex) <= edgeLimit
+                    : getRowMax(rowIndex) >= edgeLimit) {
+                // if current row exceeds previous column, fill from next row
+                rowIndex = rowIndex + 1;
+                if (rowIndex == mNumRows) {
+                    // start a new column and using edge limit of current column
+                    rowIndex = 0;
+                    edgeLimit = mReversedFlow ? findRowMin(false, null) : findRowMax(true, null);
+                }
+            }
+            edgeLimitIsValid = true;
         } else {
             itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
-            rowIndex = mStartRow != START_DEFAULT ? mStartRow : itemIndex % mNumRows;
+            // if there are cached items,  put on next row of last cached item.
+            rowIndex = (mLocations.size() > 0 ? getLocation(getLastIndex()).row + 1 : itemIndex)
+                    % mNumRows;
+            edgeLimit = 0;
+            edgeLimitIsValid = false;
         }
 
-    top_loop:
+        boolean filledOne = false;
         while (true) {
-            // find endmost row edge (.high is biggest, or .low is smallest in reversed flow)
-            int edgeRowIndex = mReversedFlow ?
-                    (mLocations.size() > 0 ? getMinLowRowIndex() : -1) :
-                    (mLocations.size() > 0 ? getMaxHighRowIndex() : -1);
-            int edge = mReversedFlow ?
-                    (edgeRowIndex != -1 ? mRows[edgeRowIndex].low : Integer.MAX_VALUE) :
-                    (edgeRowIndex != -1 ? mRows[edgeRowIndex].high : Integer.MIN_VALUE);
+            // find end-most row edge (.high is biggest, or .low is smallest in reversed flow)
             // fill from current row till last row so that each row will grow longer than
             // the previous highest row.
             for (; rowIndex < mNumRows; rowIndex++) {
                 // fill one item to a row
-                if (itemIndex == count) {
-                    break top_loop;
+                if (itemIndex == count || (!oneColumnMode && checkAppendOverLimit(toLimit))) {
+                    return filledOne;
                 }
-                appendItemToRow(itemIndex++, rowIndex);
+                int location = mReversedFlow ? getRowMin(rowIndex) : getRowMax(rowIndex);
+                if (location == Integer.MAX_VALUE || location == Integer.MIN_VALUE) {
+                    // nothing on the row
+                    if (rowIndex == 0) {
+                        location = mReversedFlow ? getRowMin(mNumRows - 1) : getRowMax(mNumRows - 1);
+                        if (location != Integer.MAX_VALUE && location != Integer.MIN_VALUE) {
+                            location = location + (mReversedFlow ? -mMargin : mMargin);
+                        }
+                    } else {
+                        location = mReversedFlow ? getRowMax(rowIndex - 1) : getRowMin(rowIndex - 1);
+                    }
+                } else {
+                    location = location + (mReversedFlow ? -mMargin : mMargin);
+                }
+                int size = appendVisibleItemToRow(itemIndex++, rowIndex, location);
+                filledOne = true;
                 // fill more item to the row to make sure this row is longer than
                 // the previous highest row.
-                if (edgeRowIndex == -1) {
-                    edgeRowIndex = mReversedFlow ? getMinLowRowIndex() : getMaxHighRowIndex();
-                    edge = mReversedFlow ?
-                            mRows[edgeRowIndex].low :
-                            mRows[edgeRowIndex].high;
-                } else  if (rowIndex != edgeRowIndex) {
-                    while (mReversedFlow ?
-                            mRows[rowIndex].low > edge :
-                            mRows[rowIndex].high < edge) {
-                        if (itemIndex == count) {
-                            break top_loop;
+                if (edgeLimitIsValid) {
+                    while (mReversedFlow ? location - size > edgeLimit :
+                            location + size < edgeLimit) {
+                        if (itemIndex == count || (!oneColumnMode && checkAppendOverLimit(toLimit))) {
+                            return filledOne;
                         }
-                        appendItemToRow(itemIndex++, rowIndex);
+                        location = location + (mReversedFlow ? - size - mMargin : size + mMargin);
+                        size = appendVisibleItemToRow(itemIndex++, rowIndex, location);
                     }
+                } else {
+                    edgeLimitIsValid = true;
+                    edgeLimit = mReversedFlow ? getRowMin(rowIndex) : getRowMax(rowIndex);
                 }
             }
-            if (mReversedFlow ?
-                    mRows[getMaxLowRowIndex()].low <= toLimit :
-                    mRows[getMinHighRowIndex()].high >= toLimit) {
-                break;
+            if (oneColumnMode) {
+                return filledOne;
             }
+            edgeLimit = mReversedFlow ? findRowMin(false, null) : findRowMax(true, null);
             // start fill from row 0 again
             rowIndex = 0;
         }
     }
 
     @Override
-    public void prependItems(int toLimit) {
-        if (mProvider.getCount() <= 0) return;
+    protected boolean prependVisibleItemsWithoutCache(int toLimit, boolean oneColumnMode) {
         int itemIndex;
         int rowIndex;
-        if (mLocations.size() > 0) {
-            itemIndex = getFirstIndex() - 1;
-            rowIndex = mLocations.getFirst().row;
-            if (rowIndex == 0) {
-                rowIndex = mNumRows - 1;
-            } else {
-                rowIndex--;
+        int edgeLimit;
+        boolean edgeLimitIsValid;
+        if (mFirstVisibleIndex >= 0) {
+            if (mFirstVisibleIndex > getFirstIndex()) {
+                // should fill using cache instead
+                return false;
             }
-        } else {
-            itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
-            rowIndex = mStartRow != START_DEFAULT ? mStartRow : itemIndex % mNumRows;
-        }
-
-    top_loop:
-        while (true) {
-            // find startmost row edge (.low is smallest, or .high is biggest in reversed flow)
-            int edgeRowIndex = mReversedFlow ?
-                    (mLocations.size() > 0 ? getMaxHighRowIndex() : -1) :
-                    (mLocations.size() > 0 ? getMinLowRowIndex() : -1);
-            int edge = mReversedFlow ?
-                    (edgeRowIndex != -1 ? mRows[edgeRowIndex].high : Integer.MIN_VALUE) :
-                    (edgeRowIndex != -1 ? mRows[edgeRowIndex].low : Integer.MAX_VALUE);
-            for (; rowIndex >=0 ; rowIndex--) {
-                if (itemIndex < 0) {
-                    break top_loop;
-                }
-                prependItemToRow(itemIndex--, rowIndex);
-                if (edgeRowIndex == -1) {
-                    edgeRowIndex = mReversedFlow ? getMaxHighRowIndex() : getMinLowRowIndex();
-                    edge = mReversedFlow ?
-                            mRows[edgeRowIndex].high :
-                            mRows[edgeRowIndex].low;
-                } else if (rowIndex != edgeRowIndex) {
-                    while (mReversedFlow ?
-                            mRows[rowIndex].high < edge :
-                            mRows[rowIndex].low > edge) {
-                        if (itemIndex < 0) {
-                            break top_loop;
-                        }
-                        prependItemToRow(itemIndex--, rowIndex);
+            itemIndex = mFirstVisibleIndex - 1;
+            rowIndex = getLocation(mFirstVisibleIndex).row;
+            // find start item index of "previous column"
+            int edgeLimitSearchIndex = findRowEdgeLimitSearchIndex(false);
+            if (edgeLimitSearchIndex < 0) {
+                // if "previous colummn" is not found, using edgeLimit of
+                // last row currently in grid and fill from upper row
+                rowIndex = rowIndex - 1;
+                edgeLimit = Integer.MAX_VALUE;
+                for (int i = mNumRows - 1; i >= 0; i--) {
+                    edgeLimit = mReversedFlow ? getRowMax(i) : getRowMin(i);
+                    if (edgeLimit != Integer.MAX_VALUE) {
+                        break;
                     }
                 }
+            } else {
+                edgeLimit = mReversedFlow ? findRowMax(true, edgeLimitSearchIndex, null) :
+                        findRowMin(false, edgeLimitSearchIndex, null);
             }
-            if (mReversedFlow ?
-                    mRows[getMinHighRowIndex()].high >= toLimit :
-                    mRows[getMaxLowRowIndex()].low <= toLimit) {
-                break;
+            if (mReversedFlow ? getRowMax(rowIndex) >= edgeLimit
+                    : getRowMin(rowIndex) <= edgeLimit) {
+                // if current row exceeds previous column, fill from next row
+                rowIndex = rowIndex - 1;
+                if (rowIndex < 0) {
+                    // start a new column and using edge limit of current column
+                    rowIndex = mNumRows - 1;
+                    edgeLimit = mReversedFlow ? findRowMax(true, null) :
+                            findRowMin(false, null);
+                }
             }
+            edgeLimitIsValid = true;
+        } else {
+            itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
+            // if there are cached items,  put on previous row of first cached item.
+            rowIndex = (mLocations.size() >= 0 ? getLocation(getFirstIndex()).row + mNumRows - 1
+                    : itemIndex) % mNumRows;
+            edgeLimit = 0;
+            edgeLimitIsValid = false;
+        }
+        boolean filledOne = false;
+        while (true) {
+            // find start-most row edge (.low is smallest, or .high is largest in reversed flow)
+            // fill from current row till first row so that each row will grow longer than
+            // the previous lowest row.
+            for (; rowIndex >= 0; rowIndex--) {
+                // fill one item to a row
+                if (itemIndex < 0 || (!oneColumnMode && checkPrependOverLimit(toLimit))) {
+                    return filledOne;
+                }
+                int location = mReversedFlow ? getRowMax(rowIndex) : getRowMin(rowIndex);
+                if (location == Integer.MAX_VALUE || location == Integer.MIN_VALUE) {
+                    // nothing on the row
+                    if (rowIndex == mNumRows - 1) {
+                        location = mReversedFlow ? getRowMax(0) : getRowMin(0);
+                        if (location != Integer.MAX_VALUE && location != Integer.MIN_VALUE) {
+                            location = location + (mReversedFlow ? mMargin : -mMargin);
+                        }
+                    } else {
+                        location = mReversedFlow ? getRowMin(rowIndex + 1) : getRowMax(rowIndex + 1);
+                    }
+                } else {
+                    location = location + (mReversedFlow ? mMargin : -mMargin);
+                }
+                int size = prependVisibleItemToRow(itemIndex--, rowIndex, location);
+                filledOne = true;
+
+                // fill more item to the row to make sure this row is longer than
+                // the previous highest row.
+                if (edgeLimitIsValid) {
+                    while (mReversedFlow ? location + size < edgeLimit :
+                            location - size > edgeLimit) {
+                        if (itemIndex < 0 || (!oneColumnMode && checkPrependOverLimit(toLimit))) {
+                            return filledOne;
+                        }
+                        location = location + (mReversedFlow ? size + mMargin : -size - mMargin);
+                        size = prependVisibleItemToRow(itemIndex--, rowIndex, location);
+                    }
+                } else {
+                    edgeLimitIsValid = true;
+                    edgeLimit = mReversedFlow ? getRowMax(rowIndex) : getRowMin(rowIndex);
+                }
+            }
+            if (oneColumnMode) {
+                return filledOne;
+            }
+            edgeLimit = mReversedFlow ? findRowMax(true, null) : findRowMin(false, null);
+            // start fill from last row again
             rowIndex = mNumRows - 1;
         }
     }
 
-    @Override
-    public final void stripDownTo(int itemIndex) {
-        // because we layout the items in the order that next item is either same row
-        // or next row,  so we can easily find the row range by searching items forward and
-        // backward until we see the row is 0 or mNumRow - 1
-        Location loc = getLocation(itemIndex);
-        if (loc == null) {
-            return;
-        }
-        int firstIndex = getFirstIndex();
-        int lastIndex = getLastIndex();
-        int row = loc.row;
 
-        int endIndex = itemIndex;
-        int endRow = row;
-        while (endRow < mNumRows - 1 && endIndex < lastIndex) {
-            endIndex++;
-            endRow = getLocation(endIndex).row;
-        }
-
-        int startIndex = itemIndex;
-        int startRow = row;
-        while (startRow > 0 && startIndex > firstIndex) {
-            startIndex--;
-            startRow = getLocation(startIndex).row;
-        }
-        // trim information
-        for (int i = firstIndex; i < startIndex; i++) {
-            removeFirst();
-        }
-        for (int i = endIndex; i < lastIndex; i++) {
-            removeLast();
-        }
-    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
new file mode 100644
index 0000000..c61087b
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.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.v17.leanback.widget;
+
+import android.support.v17.leanback.transition.LeanbackTransitionHelper;
+import android.support.v17.leanback.transition.TransitionHelper;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Helper for managing {@link android.support.v17.leanback.widget.TitleView}, including
+ * transitions and focus movement.
+ * Assumes the TitleView is overlayed on the topmost portion of the scene root view.
+ */
+public class TitleHelper {
+
+    private ViewGroup mSceneRoot;
+    private TitleView mTitleView;
+    private Object mTitleUpTransition;
+    private Object mTitleDownTransition;
+    private Object mSceneWithTitle;
+    private Object mSceneWithoutTitle;
+
+    static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
+
+    // When moving focus off the TitleView, this focus search listener assumes that the view that
+    // should take focus comes before the TitleView in a focus search starting at the scene root.
+    private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
+            new BrowseFrameLayout.OnFocusSearchListener() {
+        @Override
+        public View onFocusSearch(View focused, int direction) {
+            if (focused != mTitleView && direction == View.FOCUS_UP) {
+                return mTitleView;
+            }
+            final boolean isRtl = ViewCompat.getLayoutDirection(focused) ==
+                    View.LAYOUT_DIRECTION_RTL;
+            final int forward = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
+            if (mTitleView.hasFocus() && direction == View.FOCUS_DOWN || direction == forward) {
+                return mSceneRoot;
+            }
+            return null;
+        }
+    };
+
+    public TitleHelper(ViewGroup sceneRoot, TitleView titleView) {
+        if (sceneRoot == null || titleView == null) {
+            throw new IllegalArgumentException("Views may not be null");
+        }
+        mSceneRoot = sceneRoot;
+        mTitleView = titleView;
+        createTransitions();
+    }
+
+    private void createTransitions() {
+        mTitleUpTransition = LeanbackTransitionHelper.loadTitleOutTransition(
+                mSceneRoot.getContext(), sTransitionHelper);
+        mTitleDownTransition = LeanbackTransitionHelper.loadTitleInTransition(
+                mSceneRoot.getContext(), sTransitionHelper);
+        mSceneWithTitle = sTransitionHelper.createScene(mSceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                mTitleView.setVisibility(View.VISIBLE);
+            }
+        });
+        mSceneWithoutTitle = sTransitionHelper.createScene(mSceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                mTitleView.setVisibility(View.INVISIBLE);
+            }
+        });
+    }
+
+    /**
+     * Shows the title.
+     */
+    public void showTitle(boolean show) {
+        if (show) {
+            sTransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition);
+        } else {
+            sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition);
+        }
+    }
+
+    /**
+     * Returns the scene root ViewGroup.
+     */
+    public ViewGroup getSceneRoot() {
+        return mSceneRoot;
+    }
+
+    /**
+     * Returns the {@link TitleView}
+     */
+    public TitleView getTitleView() {
+        return mTitleView;
+    }
+
+    /**
+     * Returns a
+     * {@link android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener} which
+     * may be used to manage focus switching between the title view and scene root.
+     */
+    public BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener() {
+        return mOnFocusSearchListener;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/TitleView.java b/v17/leanback/src/android/support/v17/leanback/widget/TitleView.java
index 9d73470..bc23de2 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/TitleView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/TitleView.java
@@ -25,7 +25,6 @@
 
 /**
  * Title view for a leanback fragment.
- * @hide
  */
 public class TitleView extends FrameLayout {
 
@@ -47,9 +46,9 @@
         LayoutInflater inflater = LayoutInflater.from(context);
         View rootView = inflater.inflate(R.layout.lb_title_view, this);
 
-        mBadgeView = (ImageView) rootView.findViewById(R.id.browse_badge);
-        mTextView = (TextView) rootView.findViewById(R.id.browse_title);
-        mSearchOrbView = (SearchOrbView) rootView.findViewById(R.id.browse_orb);
+        mBadgeView = (ImageView) rootView.findViewById(R.id.title_badge);
+        mTextView = (TextView) rootView.findViewById(R.id.title_text);
+        mSearchOrbView = (SearchOrbView) rootView.findViewById(R.id.title_orb);
 
         setClipToPadding(false);
         setClipChildren(false);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
index f36d1b6..44dbee4 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
@@ -28,8 +28,40 @@
     private static final String TAG = "GridPresenter";
     private static final boolean DEBUG = false;
 
+    class VerticalGridItemBridgeAdapter extends ItemBridgeAdapter {
+        @Override
+        public void onBind(final ItemBridgeAdapter.ViewHolder itemViewHolder) {
+            // Only when having an OnItemClickListner, we attach the OnClickListener.
+            if (getOnItemViewClickedListener() != null) {
+                final View itemView = itemViewHolder.mHolder.view;
+                itemView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View view) {
+                        if (getOnItemViewClickedListener() != null) {
+                            // Row is always null
+                            getOnItemViewClickedListener().onItemClicked(
+                                    itemViewHolder.mHolder, itemViewHolder.mItem, null, null);
+                        }
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
+            if (getOnItemViewClickedListener() != null) {
+                viewHolder.mHolder.view.setOnClickListener(null);
+            }
+        }
+
+        @Override
+        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
+            viewHolder.itemView.setActivated(true);
+        }
+    }
+
     public static class ViewHolder extends Presenter.ViewHolder {
-        final ItemBridgeAdapter mItemBridgeAdapter = new ItemBridgeAdapter();
+        ItemBridgeAdapter mItemBridgeAdapter;
         final VerticalGridView mGridView;
         boolean mInitialized;
 
@@ -44,20 +76,51 @@
     }
 
     private int mNumColumns = -1;
-    private int mZoomFactor;
+    private int mFocusZoomFactor;
+    private boolean mUseFocusDimmer;
     private boolean mShadowEnabled = true;
-    private OnItemClickedListener mOnItemClickedListener;
-    private OnItemSelectedListener mOnItemSelectedListener;
     private OnItemViewSelectedListener mOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
     private boolean mRoundedCornersEnabled = true;
 
+    /**
+     * Constructs a VerticalGridPresenter with defaults.
+     * Uses {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} for focus zooming and
+     * enabled dimming on focus.
+     */
     public VerticalGridPresenter() {
         this(FocusHighlight.ZOOM_FACTOR_LARGE);
     }
 
-    public VerticalGridPresenter(int zoomFactor) {
-        mZoomFactor = zoomFactor;
+    /**
+     * Constructs a VerticalGridPresenter with the given parameters.
+     *
+     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
+     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
+     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
+     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
+     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
+     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
+     * enabled dimming on focus.
+     */
+    public VerticalGridPresenter(int focusZoomFactor) {
+        this(focusZoomFactor, true);
+    }
+
+    /**
+     * Constructs a VerticalGridPresenter with the given parameters.
+     *
+     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
+     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
+     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
+     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
+     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
+     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
+     * @param useFocusDimmer determines if the FocusHighlighter will use the dimmer
+     */
+    public VerticalGridPresenter(int focusZoomFactor, boolean useFocusDimmer) {
+        mFocusZoomFactor = focusZoomFactor;
+        mUseFocusDimmer = useFocusDimmer;
     }
 
     /**
@@ -134,10 +197,26 @@
         return isUsingDefaultShadow() && getShadowEnabled();
     }
 
+    /**
+     * Returns the zoom factor used for focus highlighting.
+     */
+    public final int getFocusZoomFactor() {
+        return mFocusZoomFactor;
+    }
+
+    /**
+     * Returns true if the focus dimmer is used for focus highlighting; false otherwise.
+     */
+    public final boolean isFocusDimmerUsed() {
+        return mUseFocusDimmer;
+    }
+
+
     @Override
     public final ViewHolder onCreateViewHolder(ViewGroup parent) {
         ViewHolder vh = createGridViewHolder(parent);
         vh.mInitialized = false;
+        vh.mItemBridgeAdapter = new VerticalGridItemBridgeAdapter();
         initializeGridViewHolder(vh);
         if (!vh.mInitialized) {
             throw new RuntimeException("super.initializeGridViewHolder() must be called");
@@ -191,7 +270,7 @@
         }
         vh.getGridView().setFocusDrawingOrderEnabled(!isUsingZOrder());
         FocusHighlightHelper.setupBrowseItemFocusHighlight(vh.mItemBridgeAdapter,
-                mZoomFactor, true);
+                mFocusZoomFactor, mUseFocusDimmer);
 
         final ViewHolder gridViewHolder = vh;
         vh.getGridView().setOnChildSelectedListener(new OnChildSelectedListener() {
@@ -200,43 +279,6 @@
                 selectChildView(gridViewHolder, view);
             }
         });
-
-        vh.mItemBridgeAdapter.setAdapterListener(new ItemBridgeAdapter.AdapterListener() {
-            @Override
-            public void onBind(final ItemBridgeAdapter.ViewHolder itemViewHolder) {
-                // Only when having an OnItemClickListner, we attach the OnClickListener.
-                if (getOnItemClickedListener() != null || getOnItemViewClickedListener() != null) {
-                    final View itemView = itemViewHolder.mHolder.view;
-                    itemView.setOnClickListener(new View.OnClickListener() {
-                        @Override
-                        public void onClick(View view) {
-                            if (getOnItemClickedListener() != null) {
-                                // Row is always null
-                                getOnItemClickedListener().onItemClicked(itemViewHolder.mItem,
-                                        null);
-                            }
-                            if (getOnItemViewClickedListener() != null) {
-                                // Row is always null
-                                getOnItemViewClickedListener().onItemClicked(
-                                        itemViewHolder.mHolder, itemViewHolder.mItem, null, null);
-                            }
-                        }
-                    });
-                }
-            }
-
-            @Override
-            public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
-                if (getOnItemClickedListener() != null || getOnItemViewClickedListener() != null) {
-                    viewHolder.mHolder.view.setOnClickListener(null);
-                }
-            }
-
-            @Override
-            public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-                viewHolder.itemView.setActivated(true);
-            }
-        });
     }
 
     @Override
@@ -258,23 +300,6 @@
     /**
      * Sets the item selected listener.
      * Since this is a grid the row parameter is always null.
-     * @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
-     */
-    public final void setOnItemSelectedListener(OnItemSelectedListener listener) {
-        mOnItemSelectedListener = listener;
-    }
-
-    /**
-     * Returns the item selected listener.
-     * @deprecated Use {@link #getOnItemViewSelectedListener()}
-     */
-    public final OnItemSelectedListener getOnItemSelectedListener() {
-        return mOnItemSelectedListener;
-    }
-
-    /**
-     * Sets the item selected listener.
-     * Since this is a grid the row parameter is always null.
      */
     public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
         mOnItemViewSelectedListener = listener;
@@ -289,17 +314,6 @@
 
     /**
      * Sets the item clicked listener.
-     * OnItemClickedListener will override {@link View.OnClickListener} that
-     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
-     * So in general, developer should choose one of the listeners but not both.
-     * @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
-     */
-    public final void setOnItemClickedListener(OnItemClickedListener listener) {
-        mOnItemClickedListener = listener;
-    }
-
-    /**
-     * Sets the item clicked listener.
      * OnItemViewClickedListener will override {@link View.OnClickListener} that
      * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
      * So in general, developer should choose one of the listeners but not both.
@@ -310,29 +324,12 @@
 
     /**
      * Returns the item clicked listener.
-     * @deprecated Use {@link #getOnItemViewClickedListener()}
-     */
-    public final OnItemClickedListener getOnItemClickedListener() {
-        return mOnItemClickedListener;
-    }
-
-    /**
-     * Returns the item clicked listener.
      */
     public final OnItemViewClickedListener getOnItemViewClickedListener() {
         return mOnItemViewClickedListener;
     }
 
     private void selectChildView(ViewHolder vh, View view) {
-        if (getOnItemSelectedListener() != null) {
-            ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
-                    (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
-            if (ibh == null) {
-                getOnItemSelectedListener().onItemSelected(null, null);
-            } else {
-                getOnItemSelectedListener().onItemSelected(ibh.mItem, null);
-            }
-        }
         if (getOnItemViewSelectedListener() != null) {
             ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
                     (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridView.java b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridView.java
index e349354..662367d 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridView.java
@@ -50,13 +50,10 @@
 
     void setColumnWidth(TypedArray array) {
         TypedValue typedValue = array.peekValue(R.styleable.lbVerticalGridView_columnWidth);
-        int size;
-        if (typedValue != null && typedValue.type == TypedValue.TYPE_DIMENSION) {
-            size = array.getDimensionPixelSize(R.styleable.lbVerticalGridView_columnWidth, 0);
-        } else {
-            size = array.getInt(R.styleable.lbVerticalGridView_columnWidth, 0);
+        if (typedValue != null) {
+            int size = array.getLayoutDimension(R.styleable.lbVerticalGridView_columnWidth, 0);
+            setColumnWidth(size);
         }
-        setColumnWidth(size);
     }
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ViewsStateBundle.java b/v17/leanback/src/android/support/v17/leanback/widget/ViewsStateBundle.java
index b3a61d8..ce8a55e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ViewsStateBundle.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ViewsStateBundle.java
@@ -152,7 +152,9 @@
     public final void loadView(View view, int id) {
         if (mChildStates != null) {
             String key = getSaveStatesKey(id);
-            SparseArray<Parcelable> container = mChildStates.get(key);
+            // Once loaded the state, do not keep the state of child. The child state will
+            // be saved again either when child is offscreen or when the parent is saved.
+            SparseArray<Parcelable> container = mChildStates.remove(key);
             if (container != null) {
                 view.restoreHierarchyState(container);
             }
diff --git a/v17/tests/Android.mk b/v17/tests/Android.mk
new file mode 100644
index 0000000..35de495
--- /dev/null
+++ b/v17/tests/Android.mk
@@ -0,0 +1,40 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR = \
+        $(LOCAL_PATH)/res \
+        $(LOCAL_PATH)/../leanback/res
+LOCAL_AAPT_FLAGS := \
+        --auto-add-overlay \
+        --extra-packages android.support.v17.leanback
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+        android-support-v4 \
+        android-support-v7-recyclerview \
+        android-support-v17-leanback
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := AndroidLeanbackTests
+
+include $(BUILD_PACKAGE)
diff --git a/v17/tests/AndroidManifest.xml b/v17/tests/AndroidManifest.xml
new file mode 100644
index 0000000..364c225
--- /dev/null
+++ b/v17/tests/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.v17.leanback.tests">
+    <uses-sdk android:minSdkVersion="17"/>
+
+    <!--
+        This declares that this application uses the instrumentation test runner targeting
+        the package of android.support.v17.leanback.tests.  To run the tests use the command:
+        "adb shell am instrument -w android.support.v17.leanback.tests/android.test.InstrumentationTestRunner"
+    -->
+    <instrumentation
+        android:targetPackage="android.support.v17.leanback.tests"
+        android:name="android.test.InstrumentationTestRunner" />
+
+    <application
+        android:supportsRtl="true">
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="android.support.v17.leanback.widget.GridActivity"
+            android:exported="true" />
+
+    </application>
+
+</manifest>
diff --git a/v17/tests/README.txt b/v17/tests/README.txt
new file mode 100644
index 0000000..fd1fb56
--- /dev/null
+++ b/v17/tests/README.txt
@@ -0,0 +1,7 @@
+Test project for support leanback.
+
+INSTALLATION
+adb install -r AndroidLeanbackTests.apk
+
+RUN TESTS
+adb shell am instrument -w android.support.v17.leanback.tests/android.test.InstrumentationTestRunner
\ No newline at end of file
diff --git a/v17/tests/res/layout/horizontal_grid.xml b/v17/tests/res/layout/horizontal_grid.xml
new file mode 100644
index 0000000..5119f79
--- /dev/null
+++ b/v17/tests/res/layout/horizontal_grid.xml
@@ -0,0 +1,23 @@
+<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.support.v17.leanback.widget.HorizontalGridView
+      android:id="@+id/gridview"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:clipToPadding="false"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:background="#00ffff"
+      lb:horizontalMargin="12dip"
+      lb:verticalMargin="24dip"
+      lb:numberOfRows="3"
+      lb:rowHeight="150dip"
+      android:paddingBottom="12dip"
+      android:paddingLeft="12dip"
+      android:paddingRight="12dip"
+      android:paddingTop="12dip" />
+</RelativeLayout>
diff --git a/v17/tests/res/layout/horizontal_grid_testredundantappendremove2.xml b/v17/tests/res/layout/horizontal_grid_testredundantappendremove2.xml
new file mode 100644
index 0000000..b539f3c
--- /dev/null
+++ b/v17/tests/res/layout/horizontal_grid_testredundantappendremove2.xml
@@ -0,0 +1,23 @@
+<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.support.v17.leanback.widget.HorizontalGridView
+      android:id="@+id/gridview"
+      android:layout_width="960dp"
+      android:layout_height="492dp"
+      android:clipToPadding="false"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:background="#00ffff"
+      lb:horizontalMargin="12dip"
+      lb:verticalMargin="24dip"
+      lb:numberOfRows="3"
+      lb:rowHeight="150dip"
+      android:paddingBottom="12dip"
+      android:paddingLeft="12dip"
+      android:paddingRight="12dip"
+      android:paddingTop="12dip" />
+</RelativeLayout>
diff --git a/v17/tests/res/layout/vertical_grid.xml b/v17/tests/res/layout/vertical_grid.xml
new file mode 100644
index 0000000..85e1d46
--- /dev/null
+++ b/v17/tests/res/layout/vertical_grid.xml
@@ -0,0 +1,23 @@
+<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.support.v17.leanback.widget.VerticalGridView
+      android:id="@+id/gridview"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:clipToPadding="false"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:background="#00ffff"
+      lb:horizontalMargin="12dip"
+      lb:verticalMargin="24dip"
+      lb:numberOfColumns="3"
+      lb:columnWidth="150dip"
+      android:paddingBottom="12dip"
+      android:paddingLeft="12dip"
+      android:paddingRight="12dip"
+      android:paddingTop="12dip" />
+</RelativeLayout>
diff --git a/v17/tests/res/layout/vertical_grid_testredundantappendremove.xml b/v17/tests/res/layout/vertical_grid_testredundantappendremove.xml
new file mode 100644
index 0000000..4ce56c5
--- /dev/null
+++ b/v17/tests/res/layout/vertical_grid_testredundantappendremove.xml
@@ -0,0 +1,23 @@
+<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.support.v17.leanback.widget.VerticalGridView
+      android:id="@+id/gridview"
+      android:layout_width="960dp"
+      android:layout_height="492dp"
+      android:clipToPadding="false"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:background="#00ffff"
+      lb:horizontalMargin="12dip"
+      lb:verticalMargin="24dip"
+      lb:numberOfColumns="3"
+      lb:columnWidth="150dip"
+      android:paddingBottom="12dip"
+      android:paddingLeft="12dip"
+      android:paddingRight="12dip"
+      android:paddingTop="12dip" />
+</RelativeLayout>
diff --git a/v17/tests/res/layout/vertical_linear.xml b/v17/tests/res/layout/vertical_linear.xml
new file mode 100644
index 0000000..b2c74df
--- /dev/null
+++ b/v17/tests/res/layout/vertical_linear.xml
@@ -0,0 +1,23 @@
+<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.support.v17.leanback.widget.VerticalGridView
+      android:id="@+id/gridview"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:clipToPadding="false"
+      android:focusable="true"
+      android:focusableInTouchMode="true"
+      android:background="#00ffff"
+      lb:horizontalMargin="12dip"
+      lb:verticalMargin="24dip"
+      lb:numberOfColumns="1"
+      lb:columnWidth="150dip"
+      android:paddingBottom="12dip"
+      android:paddingLeft="12dip"
+      android:paddingRight="12dip"
+      android:paddingTop="12dip" />
+</RelativeLayout>
diff --git a/v17/tests/src/android/support/v17/leanback/widget/AssertHelper.java b/v17/tests/src/android/support/v17/leanback/widget/AssertHelper.java
new file mode 100644
index 0000000..7a61159
--- /dev/null
+++ b/v17/tests/src/android/support/v17/leanback/widget/AssertHelper.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+public class AssertHelper {
+
+    public static void assertGreaterThan(String msg, int a, int b) {
+        junit.framework.Assert.assertTrue(msg + ": " + a + " > " + b, a > b);
+    }
+}
diff --git a/v17/tests/src/android/support/v17/leanback/widget/GridActivity.java b/v17/tests/src/android/support/v17/leanback/widget/GridActivity.java
new file mode 100644
index 0000000..ba6cd39
--- /dev/null
+++ b/v17/tests/src/android/support/v17/leanback/widget/GridActivity.java
@@ -0,0 +1,252 @@
+/*
+ * 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.support.v17.leanback.tests.R;
+import android.support.v7.widget.RecyclerView;
+import android.support.v17.leanback.widget.BaseGridView;
+import android.support.v17.leanback.widget.OnChildSelectedListener;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * @hide from javadoc
+ */
+public class GridActivity extends Activity {
+    private static final String TAG = "GridActivity";
+
+    public static final String EXTRA_LAYOUT_RESOURCE_ID = "layoutResourceId";
+    public static final String EXTRA_NUM_ITEMS = "numItems";
+    public static final String EXTRA_ITEMS = "items";
+    public static final String EXTRA_ITEMS_FOCUSABLE = "itemsFocusable";
+    public static final String EXTRA_STAGGERED = "staggered";
+    public static final String EXTRA_REQUEST_LAYOUT_ONFOCUS = "requestLayoutOnFocus";
+    public static final String SELECT_ACTION = "android.test.leanback.widget.SELECT";
+
+    static final int DEFAULT_NUM_ITEMS = 100;
+    static final boolean DEFAULT_STAGGERED = true;
+    static final boolean DEFAULT_REQUEST_LAYOUT_ONFOCUS = false;
+
+    private static final boolean DEBUG = false;
+
+    int mLayoutId;
+    int mOrientation;
+    int mNumItems;
+    boolean mStaggered;
+    boolean mRequestLayoutOnFocus;
+
+    int[] mGridViewLayoutSize;
+    BaseGridView mGridView;
+    int[] mItemLengths;
+    boolean[] mItemFocusables;
+
+    private int mBoundCount;
+
+    private View createView() {
+
+        View view = getLayoutInflater().inflate(mLayoutId, null, false);
+        mGridView = (BaseGridView) view.findViewById(R.id.gridview);
+        mOrientation = mGridView instanceof HorizontalGridView ? BaseGridView.HORIZONTAL :
+                BaseGridView.VERTICAL;
+        mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_BOTH_EDGE);
+        mGridView.setWindowAlignmentOffsetPercent(35);
+        mGridView.setOnChildSelectedListener(new OnChildSelectedListener() {
+            @Override
+            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
+                if (DEBUG) Log.d(TAG, "onChildSelected position=" + position +  " id="+id);
+            }
+        });
+        return view;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Intent intent = getIntent();
+
+        mLayoutId = intent.getIntExtra(EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid);
+        mStaggered = intent.getBooleanExtra(EXTRA_STAGGERED, DEFAULT_STAGGERED);
+        mRequestLayoutOnFocus = intent.getBooleanExtra(EXTRA_REQUEST_LAYOUT_ONFOCUS,
+                DEFAULT_REQUEST_LAYOUT_ONFOCUS);
+        mItemLengths = intent.getIntArrayExtra(EXTRA_ITEMS);
+        mItemFocusables = intent.getBooleanArrayExtra(EXTRA_ITEMS_FOCUSABLE);
+
+        super.onCreate(savedInstanceState);
+
+        if (DEBUG) Log.v(TAG, "onCreate " + this);
+
+        RecyclerView.Adapter adapter = new MyAdapter();
+
+        View view = createView();
+        if (mItemLengths == null) {
+            mNumItems = intent.getIntExtra(EXTRA_NUM_ITEMS, DEFAULT_NUM_ITEMS);
+            mItemLengths = new int[mNumItems];
+            for (int i = 0; i < mItemLengths.length; i++) {
+                if (mOrientation == BaseGridView.HORIZONTAL) {
+                    mItemLengths[i] = mStaggered ? (int)(Math.random() * 180) + 180 : 240;
+                } else {
+                    mItemLengths[i] = mStaggered ? (int)(Math.random() * 120) + 120 : 160;
+                }
+            }
+        } else {
+            mNumItems = mItemLengths.length;
+        }
+
+        mGridView.setAdapter(new MyAdapter());
+        setContentView(view);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        if (DEBUG) Log.v(TAG, "onNewIntent " + intent+ " "+this);
+        if (intent.getAction().equals(SELECT_ACTION)) {
+            int position = intent.getIntExtra("SELECT_POSITION", -1);
+            if (position >= 0) {
+                mGridView.setSelectedPosition(position);
+            }
+        }
+        super.onNewIntent(intent);
+    }
+
+    private OnFocusChangeListener mItemFocusChangeListener = new OnFocusChangeListener() {
+
+        @Override
+        public void onFocusChange(View v, boolean hasFocus) {
+            if (hasFocus) {
+                v.setBackgroundColor(Color.YELLOW);
+            } else {
+                v.setBackgroundColor(Color.LTGRAY);
+            }
+            if (mRequestLayoutOnFocus) {
+                RecyclerView.ViewHolder vh = mGridView.getChildViewHolder(v);
+                int position = vh.getAdapterPosition();
+                updateSize(v, position);
+            }
+        }
+    };
+
+    void resetBoundCount() {
+        mBoundCount = 0;
+    }
+
+    int getBoundCount() {
+       return mBoundCount;
+    }
+
+    void swap(int index1, int index2) {
+        if (index1 == index2) {
+            return;
+        } else if (index1 > index2) {
+            int index = index1;
+            index1 = index2;
+            index2 = index;
+        }
+        int value = mItemLengths[index1];
+        mItemLengths[index1] = mItemLengths[index2];
+        mItemLengths[index2] = value;
+        mGridView.getAdapter().notifyItemMoved(index1, index2);
+        mGridView.getAdapter().notifyItemMoved(index2 - 1, index1);
+    }
+
+    void changeArraySize(int length) {
+        mNumItems = length;
+        mGridView.getAdapter().notifyDataSetChanged();
+    }
+
+    int[] removeItems(int index, int length) {
+        int[] removed = new int[length];
+        System.arraycopy(mItemLengths, index, removed, 0, length);
+        System.arraycopy(mItemLengths, index + length, mItemLengths, index,
+                mNumItems - index - length);
+        mNumItems -= length;
+        mGridView.getAdapter().notifyItemRangeRemoved(index, length);
+        return removed;
+    }
+
+    void addItems(int index, int[] items) {
+        int length = items.length;
+        System.arraycopy(mItemLengths, index, mItemLengths, index + length, mNumItems - index);
+        System.arraycopy(items, 0, mItemLengths, index, length);
+        mNumItems += length;
+        mGridView.getAdapter().notifyItemRangeInserted(index, length);
+    }
+
+    class MyAdapter extends RecyclerView.Adapter {
+
+        @Override
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            if (DEBUG) Log.v(TAG, "createViewHolder " + viewType);
+            TextView textView = new TextView(parent.getContext());
+            textView.setTextColor(Color.BLACK);
+            textView.setOnFocusChangeListener(mItemFocusChangeListener);
+            return new ViewHolder(textView);
+        }
+
+        @Override
+        public void onBindViewHolder(RecyclerView.ViewHolder baseHolder, int position) {
+            if (DEBUG) Log.v(TAG, "bindViewHolder " + position + " " + baseHolder);
+            mBoundCount++;
+            ViewHolder holder = (ViewHolder) baseHolder;
+            ((TextView) holder.itemView).setText("Item "+position);
+            boolean focusable = true;
+            if (mItemFocusables != null) {
+                focusable = mItemFocusables[position];
+            }
+            ((TextView) holder.itemView).setFocusable(focusable);
+            ((TextView) holder.itemView).setFocusableInTouchMode(focusable);
+            holder.itemView.setBackgroundColor(Color.LTGRAY);
+            updateSize(holder.itemView, position);
+        }
+
+        @Override
+        public int getItemCount() {
+            return mNumItems;
+        }
+    }
+
+    void updateSize(View view, int position) {
+        ViewGroup.LayoutParams p = view.getLayoutParams();
+        if (p == null) {
+            p = new ViewGroup.LayoutParams(0, 0);
+        }
+        if (mOrientation == BaseGridView.HORIZONTAL) {
+            p.width = mItemLengths[position] + (mRequestLayoutOnFocus && view.hasFocus() ? 1 : 0);
+            p.height = 80;
+        } else {
+            p.width = 240;
+            p.height = mItemLengths[position] + (mRequestLayoutOnFocus && view.hasFocus() ? 1 : 0);
+        }
+        view.setLayoutParams(p);
+    }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+
+        public ViewHolder(View v) {
+            super(v);
+        }
+    }
+}
diff --git a/v17/tests/src/android/support/v17/leanback/widget/GridTest.java b/v17/tests/src/android/support/v17/leanback/widget/GridTest.java
new file mode 100644
index 0000000..40deed3
--- /dev/null
+++ b/v17/tests/src/android/support/v17/leanback/widget/GridTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.test.AndroidTestCase;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Base class for testing Grid algorithm
+ * @hide
+ */
+public abstract class GridTest extends AndroidTestCase {
+
+    static class Provider implements Grid.Provider {
+
+        int[] mItems;
+        int mCount;
+        int[] mEdges;
+
+        Provider(int[] items) {
+            mItems = items;
+            mCount = items.length;
+            mEdges = new int[mCount];
+        }
+
+        @Override
+        public int getCount() {
+            return mCount;
+        }
+
+        @Override
+        public int createItem(int index, boolean append, Object[] item) {
+            return mItems[index];
+        }
+
+        @Override
+        public void addItem(Object item, int index, int length, int rowIndex, int edge) {
+            if (edge == Integer.MAX_VALUE || edge == Integer.MIN_VALUE) {
+                // initialize edge for first item added
+                edge = 0;
+            }
+            mEdges[index] = edge;
+        }
+
+        @Override
+        public void removeItem(int index) {
+        }
+
+        @Override
+        public int getEdge(int index) {
+            return mEdges[index];
+        }
+
+        @Override
+        public int getSize(int index) {
+            return mItems[index];
+        }
+
+        void scroll(int distance) {
+            for (int i= 0; i < mEdges.length; i++) {
+                mEdges[i] -= distance;
+            }
+        }
+    }
+
+    Provider mProvider;
+
+    static String dump(Grid grid) {
+        StringWriter w = new StringWriter();
+        grid.debugPrint(new PrintWriter(w));
+        return w.toString();
+    }
+}
diff --git a/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java b/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java
new file mode 100644
index 0000000..f19de7a
--- /dev/null
+++ b/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java
@@ -0,0 +1,1049 @@
+/*
+ * 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.support.v17.leanback.tests.R;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.app.Instrumentation;
+import android.content.Intent;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * @hide from javadoc
+ */
+public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivity> {
+
+    private static final boolean HUMAN_DELAY = false;
+
+    protected GridActivity mActivity;
+    protected Instrumentation mInstrumentation;
+    protected BaseGridView mGridView;
+    protected GridLayoutManager mLayoutManager;
+    protected int mOrientation;
+    protected int mNumRows;
+
+    private final Comparator<View> mRowSortComparator = new Comparator<View>() {
+        public int compare(View lhs, View rhs) {
+            if (mOrientation == BaseGridView.HORIZONTAL) {
+                return lhs.getLeft() - rhs.getLeft();
+            } else {
+                return lhs.getTop() - rhs.getTop();
+            }
+        };
+    };
+
+    /**
+     * Verify margins between items on same row are same.
+     */
+    private final Runnable mVerifyLayout = new Runnable() {
+        @Override
+        public void run() {
+            verifyMargin();
+        }
+    };
+
+    public GridWidgetTest() {
+        super("android.support.v17.leanback.tests", GridActivity.class);
+    }
+
+    private void humanDelay(int delay) throws InterruptedException {
+        if (HUMAN_DELAY) Thread.sleep(delay);
+    }
+    /**
+     * Change size of the Adapter and notifyDataSetChanged.
+     */
+    private void changeArraySize(final int size) throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mActivity.changeArraySize(size);
+            }
+        });
+        Thread.sleep(500);
+    }
+
+    /**
+     * Change selected position.
+     */
+    private void setSelectedPosition(final int position, final int scrollExtra) throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mGridView.setSelectedPosition(position, scrollExtra);
+            }
+        });
+        Thread.sleep(500);
+    }
+
+    /**
+     * Wait for grid view stop scroll and optionally verify state of grid view.
+     */
+    protected void waitForScrollIdle(Runnable verify) throws Throwable {
+        Thread.sleep(100);
+        while (mGridView.getLayoutManager().isSmoothScrolling() ||
+                mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE) {
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException ex) {
+                break;
+            }
+            if (verify != null) {
+                runTestOnUiThread(verify);
+            }
+        }
+    }
+
+    /**
+     * Wait for grid view stop animation and optionally verify state of grid view.
+     */
+    protected void waitForTransientStateGone(Runnable verify) throws Throwable {
+        do {
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException ex) {
+                break;
+            }
+            if (verify != null) {
+                runTestOnUiThread(verify);
+            }
+        } while (mGridView.hasTransientState());
+    }
+
+    /**
+     * Wait for grid view stop scroll.
+     */
+    protected void waitForScrollIdle() throws Throwable {
+        waitForScrollIdle(null);
+    }
+
+    /**
+     * Scrolls using given key.
+     */
+    protected void scroll(int key, Runnable verify) throws Throwable {
+        do {
+            if (verify != null) {
+                runTestOnUiThread(verify);
+            }
+            sendRepeatedKeys(10, key);
+            try {
+                Thread.sleep(300);
+            } catch (InterruptedException ex) {
+                break;
+            }
+        } while (mGridView.getLayoutManager().isSmoothScrolling() ||
+                mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE);
+    }
+
+    protected void scrollToBegin(Runnable verify) throws Throwable {
+        int key;
+        if (mOrientation == BaseGridView.HORIZONTAL) {
+            if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
+                key = KeyEvent.KEYCODE_DPAD_RIGHT;
+            } else {
+                key = KeyEvent.KEYCODE_DPAD_LEFT;
+            }
+        } else {
+            key = KeyEvent.KEYCODE_DPAD_UP;
+        }
+        scroll(key, verify);
+    }
+
+    protected void scrollToEnd(Runnable verify) throws Throwable {
+        int key;
+        if (mOrientation == BaseGridView.HORIZONTAL) {
+            if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
+                key = KeyEvent.KEYCODE_DPAD_LEFT;
+            } else {
+                key = KeyEvent.KEYCODE_DPAD_RIGHT;
+            }
+        } else {
+            key = KeyEvent.KEYCODE_DPAD_DOWN;
+        }
+        scroll(key, verify);
+    }
+
+    /**
+     * Group and sort children by their position on each row (HORIZONTAL) or column(VERTICAL).
+     */
+    protected View[][] sortByRows() {
+        final HashMap<Integer, ArrayList<View>> rows = new HashMap<Integer, ArrayList<View>>();
+        ArrayList<Integer> rowLocations = new ArrayList();
+        for (int i = 0; i < mGridView.getChildCount(); i++) {
+            View v = mGridView.getChildAt(i);
+            int rowLocation;
+            if (mOrientation == BaseGridView.HORIZONTAL) {
+                rowLocation = v.getTop();
+            } else {
+                rowLocation = mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL ?
+                    v.getRight() : v.getLeft();
+            }
+            ArrayList<View> views = rows.get(rowLocation);
+            if (views == null) {
+                views = new ArrayList<View>();
+                rows.put(rowLocation, views);
+                rowLocations.add(rowLocation);
+            }
+            views.add(v);
+        }
+        Object[] sortedLocations = rowLocations.toArray();
+        Arrays.sort(sortedLocations);
+        if (mNumRows != rows.size()) {
+            assertEquals("Dump Views by rows "+rows, mNumRows, rows.size());
+        }
+        View[][] sorted = new View[rows.size()][];
+        for (int i = 0; i < rowLocations.size(); i++) {
+            Integer rowLocation = rowLocations.get(i);
+            ArrayList<View> arr = rows.get(rowLocation);
+            View[] views = arr.toArray(new View[arr.size()]);
+            Arrays.sort(views, mRowSortComparator);
+            sorted[i] = views;
+        }
+        return sorted;
+    }
+
+    protected void verifyMargin() {
+        View[][] sorted = sortByRows();
+        for (int row = 0; row < sorted.length; row++) {
+            View[] views = sorted[row];
+            int margin = -1;
+            for (int i = 1; i < views.length; i++) {
+                if (mOrientation == BaseGridView.HORIZONTAL) {
+                    if (i == 1) {
+                        margin = views[i].getLeft() - views[i - 1].getRight();
+                    } else {
+                        assertEquals(margin, views[i].getLeft() - views[i - 1].getRight());
+                    }
+                } else {
+                    if (i == 1) {
+                        margin = views[i].getTop() - views[i - 1].getBottom();
+                    } else {
+                        assertEquals(margin, views[i].getTop() - views[i - 1].getBottom());
+                    }
+                }
+            }
+        }
+    }
+
+    protected void verifyBeginAligned() {
+        View[][] sorted = sortByRows();
+        int alignedLocation = 0;
+        if (mOrientation == BaseGridView.HORIZONTAL) {
+            if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
+                for (int i = 0; i < sorted.length; i++) {
+                    if (i == 0) {
+                        alignedLocation = sorted[i][sorted[i].length - 1].getRight();
+                    } else {
+                        assertEquals(alignedLocation, sorted[i][sorted[i].length - 1].getRight());
+                    }
+                }
+            } else {
+                for (int i = 0; i < sorted.length; i++) {
+                    if (i == 0) {
+                        alignedLocation = sorted[i][0].getLeft();
+                    } else {
+                        assertEquals(alignedLocation, sorted[i][0].getLeft());
+                    }
+                }
+            }
+        } else {
+            for (int i = 0; i < sorted.length; i++) {
+                if (i == 0) {
+                    alignedLocation = sorted[i][0].getTop();
+                } else {
+                    assertEquals(alignedLocation, sorted[i][0].getTop());
+                }
+            }
+        }
+    }
+
+    protected int[] getEndEdges() {
+        View[][] sorted = sortByRows();
+        int[] edges = new int[sorted.length];
+        if (mOrientation == BaseGridView.HORIZONTAL) {
+            if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
+                for (int i = 0; i < sorted.length; i++) {
+                    edges[i] = sorted[i][0].getLeft();
+                }
+            } else {
+                for (int i = 0; i < sorted.length; i++) {
+                    edges[i] = sorted[i][sorted[i].length - 1].getRight();
+                }
+            }
+        } else {
+            for (int i = 0; i < sorted.length; i++) {
+                edges[i] = sorted[i][sorted[i].length - 1].getBottom();
+            }
+        }
+        return edges;
+    }
+
+    protected void verifyEdgesSame(int[] edges, int[] edges2) {
+        assertEquals(edges.length, edges2.length);
+        for (int i = 0; i < edges.length; i++) {
+            assertEquals(edges[i], edges2[i]);
+        }
+    }
+
+    protected void verifyBoundCount(int count) {
+        if (mActivity.getBoundCount() != count) {
+            StringBuffer b = new StringBuffer();
+            b.append("ItemsLength: ");
+            for (int i = 0; i < mActivity.mItemLengths.length; i++) {
+                b.append(mActivity.mItemLengths[i]).append(",");
+            }
+            assertEquals("Bound count does not match, ItemsLengths: "+ b,
+                    count, mActivity.getBoundCount());
+        }
+    }
+
+    private static int getCenterY(View v) {
+        return (v.getTop() + v.getBottom())/2;
+    }
+
+    private static int getCenterX(View v) {
+        return (v.getLeft() + v.getRight())/2;
+    }
+
+    private void initActivity(Intent intent) {
+        setActivityIntent(intent);
+        mActivity = getActivity();
+        final String testName = getName();
+        try {
+            runTestOnUiThread(new Runnable() {
+                public void run() {
+                    mActivity.setTitle(testName);
+                }
+            });
+            Thread.sleep(1000);
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+        mGridView = mActivity.mGridView;
+    }
+
+    public void testThreeRowHorizontalBasic() throws Throwable {
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+
+        scrollToEnd(mVerifyLayout);
+        verifyBoundCount(100);
+
+        scrollToBegin(mVerifyLayout);
+
+        verifyBeginAligned();
+    }
+
+    public void testThreeColumnVerticalBasic() throws Throwable {
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 3;
+
+        scrollToEnd(mVerifyLayout);
+        verifyBoundCount(200);
+
+        scrollToBegin(mVerifyLayout);
+
+        verifyBeginAligned();
+    }
+
+    public void testRedundantAppendRemove() throws Throwable {
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_grid_testredundantappendremove);
+        intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{
+                149,177,128,234,227,187,163,223,146,210,228,148,227,193,182,197,177,142,225,207,
+                157,171,209,204,187,184,123,221,197,153,202,179,193,214,226,173,225,143,188,159,
+                139,193,233,143,227,203,222,124,228,223,164,131,228,126,211,160,165,152,235,184,
+                155,224,149,181,171,229,200,234,177,130,164,172,188,139,132,203,179,220,147,131,
+                226,127,230,239,183,203,206,227,123,170,239,234,200,149,237,204,160,133,202,234,
+                173,122,139,149,151,153,216,231,121,145,227,153,186,174,223,180,123,215,206,216,
+                239,222,219,207,193,218,140,133,171,153,183,132,233,138,159,174,189,171,143,128,
+                152,222,141,202,224,190,134,120,181,231,230,136,132,224,136,210,207,150,128,183,
+                221,194,179,220,126,221,137,205,223,193,172,132,226,209,133,191,227,127,159,171,
+                180,149,237,177,194,207,170,202,161,144,147,199,205,186,164,140,193,203,224,129});
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 3;
+
+        scrollToEnd(mVerifyLayout);
+
+        verifyBoundCount(200);
+
+        scrollToBegin(mVerifyLayout);
+
+        verifyBeginAligned();
+    }
+
+    public void testRedundantAppendRemove2() throws Throwable {
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid_testredundantappendremove2);
+        intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{
+                318,333,199,224,246,273,269,289,340,313,265,306,349,269,185,282,257,354,316,252,
+                237,290,283,343,196,313,290,343,191,262,342,228,343,349,251,203,226,305,265,213,
+                216,333,295,188,187,281,288,311,244,232,224,332,290,181,267,276,226,261,335,355,
+                225,217,219,183,234,285,257,304,182,250,244,223,257,219,342,185,347,205,302,315,
+                299,309,292,237,192,309,228,250,347,227,337,298,299,185,185,331,223,284,265,351});
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+        mLayoutManager = (GridLayoutManager) mGridView.getLayoutManager();
+
+        // test append without staggered result cache
+        scrollToEnd(mVerifyLayout);
+
+        verifyBoundCount(100);
+        int[] endEdges = getEndEdges();
+
+        scrollToBegin(mVerifyLayout);
+
+        verifyBeginAligned();
+
+        // now test append with staggered result cache
+        changeArraySize(3);
+        assertEquals("Staggerd cache should be kept as is when no item size change",
+                100, ((StaggeredGrid) mLayoutManager.mGrid).mLocations.size());
+
+        mActivity.resetBoundCount();
+        changeArraySize(100);
+
+        scrollToEnd(mVerifyLayout);
+        verifyBoundCount(100);
+
+        // we should get same aligned end edges
+        int[] endEdges2 = getEndEdges();
+        verifyEdgesSame(endEdges, endEdges2);
+    }
+
+    public void testItemMovedHorizontal() throws Throwable {
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+
+        mGridView.setSelectedPositionSmooth(150);
+        waitForScrollIdle(mVerifyLayout);
+        mActivity.swap(150, 152);
+        waitForTransientStateGone(null);
+
+        runTestOnUiThread(mVerifyLayout);
+
+        scrollToBegin(mVerifyLayout);
+
+        verifyBeginAligned();
+    }
+
+    public void testItemMovedVertical() throws Throwable {
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 3;
+
+        mGridView.setSelectedPositionSmooth(150);
+        waitForScrollIdle(mVerifyLayout);
+        mActivity.swap(150, 152);
+        waitForTransientStateGone(null);
+
+        runTestOnUiThread(mVerifyLayout);
+
+        scrollToEnd(mVerifyLayout);
+        scrollToBegin(mVerifyLayout);
+
+        verifyBeginAligned();
+    }
+
+    public void testItemAddRemoveHorizontal() throws Throwable {
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+
+        scrollToEnd(mVerifyLayout);
+        int[] endEdges = getEndEdges();
+
+        mGridView.setSelectedPositionSmooth(150);
+        waitForScrollIdle(mVerifyLayout);
+        int[] removedItems = mActivity.removeItems(151, 4);
+        waitForTransientStateGone(null);
+
+        scrollToEnd(mVerifyLayout);
+        mGridView.setSelectedPositionSmooth(150);
+        waitForScrollIdle(mVerifyLayout);
+
+        mActivity.addItems(151, removedItems);
+        waitForTransientStateGone(null);
+        scrollToEnd(mVerifyLayout);
+
+        // we should get same aligned end edges
+        int[] endEdges2 = getEndEdges();
+        verifyEdgesSame(endEdges, endEdges2);
+
+        scrollToBegin(mVerifyLayout);
+        verifyBeginAligned();
+    }
+
+    public void testFocusToFirstItem() throws Throwable {
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 3;
+
+        int[] removedItems = mActivity.removeItems(0, 200);
+
+        waitForTransientStateGone(null);
+        humanDelay(500);
+        mActivity.addItems(0, removedItems);
+
+        waitForTransientStateGone(null);
+        humanDelay(500);
+        assertTrue(mGridView.getLayoutManager().findViewByPosition(0).hasFocus());
+
+        changeArraySize(0);
+
+        changeArraySize(200);
+        assertTrue(mGridView.getLayoutManager().findViewByPosition(0).hasFocus());
+    }
+
+    public void testNonFocusableHorizontal() throws Throwable {
+        final int numItems = 200;
+        final int startPos = 45;
+        final int skips = 20;
+        final int numColumns = 3;
+        final int endPos = startPos + numColumns * (skips + 1);
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = numColumns;
+        boolean[] focusable = new boolean[numItems];
+        for (int i = 0; i < focusable.length; i++) {
+            focusable[i] = true;
+        }
+        for (int i = startPos + mNumRows, j = 0; j < skips; i += mNumRows, j++) {
+            focusable[i] = false;
+        }
+        intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
+        initActivity(intent);
+
+        mGridView.setSelectedPositionSmooth(startPos);
+        waitForScrollIdle(mVerifyLayout);
+
+        if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
+            sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
+        } else {
+            sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        }
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(endPos, mGridView.getSelectedPosition());
+
+        if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
+            sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        } else {
+            sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
+        }
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(startPos, mGridView.getSelectedPosition());
+
+    }
+
+
+    public void testNonFocusableVertical() throws Throwable {
+        final int numItems = 200;
+        final int startPos = 44;
+        final int skips = 20;
+        final int numColumns = 3;
+        final int endPos = startPos + numColumns * (skips + 1);
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = numColumns;
+        boolean[] focusable = new boolean[numItems];
+        for (int i = 0; i < focusable.length; i++) {
+            focusable[i] = true;
+        }
+        for (int i = startPos + mNumRows, j = 0; j < skips; i += mNumRows, j++) {
+            focusable[i] = false;
+        }
+        intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
+        initActivity(intent);
+
+        mGridView.setSelectedPositionSmooth(startPos);
+        waitForScrollIdle(mVerifyLayout);
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(endPos, mGridView.getSelectedPosition());
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(startPos, mGridView.getSelectedPosition());
+
+    }
+
+    public void testTransferFocusable() throws Throwable {
+        final int numItems = 200;
+        final int numColumns = 3;
+        final int startPos = 1;
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = numColumns;
+        boolean[] focusable = new boolean[numItems];
+        for (int i = 0; i < focusable.length; i++) {
+            focusable[i] = true;
+        }
+        for (int i = 0; i < startPos; i++) {
+            focusable[i] = false;
+        }
+        intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
+        initActivity(intent);
+
+        changeArraySize(0);
+        assertTrue(mGridView.isFocused());
+
+        changeArraySize(numItems);
+        assertTrue(mGridView.getLayoutManager().findViewByPosition(startPos).hasFocus());
+    }
+
+    public void testTransferFocusable2() throws Throwable {
+        final int numItems = 200;
+        final int numColumns = 3;
+        final int startPos = 10;
+
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, true);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = numColumns;
+        boolean[] focusable = new boolean[numItems];
+        for (int i = 0; i < focusable.length; i++) {
+            focusable[i] = true;
+        }
+        for (int i = 0; i < startPos; i++) {
+            focusable[i] = false;
+        }
+        intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
+        initActivity(intent);
+
+        changeArraySize(0);
+        assertTrue(mGridView.isFocused());
+
+        changeArraySize(numItems);
+        assertTrue(mGridView.getLayoutManager().findViewByPosition(startPos).hasFocus());
+    }
+
+    public void testNonFocusableLoseInFastLayout() throws Throwable {
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_linear);
+        int[] items = new int[300];
+        for (int i = 0; i < items.length; i++) {
+            items[i] = 480;
+        }
+        intent.putExtra(GridActivity.EXTRA_ITEMS, items);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+        int pressDown = 15;
+
+        initActivity(intent);
+
+        mGridView.setSelectedPositionSmooth(0);
+        waitForScrollIdle(mVerifyLayout);
+
+        for (int i = 0; i < pressDown; i++) {
+            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        }
+        waitForScrollIdle(mVerifyLayout);
+        assertFalse(mGridView.isFocused());
+
+    }
+
+    public void testSetSelectionWithDelta() throws Throwable {
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 300);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+
+        initActivity(intent);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mGridView.setSelectedPositionSmooth(3);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        int top1 = mGridView.getLayoutManager().findViewByPosition(3).getTop();
+
+        humanDelay(1000);
+
+        // scroll to position with delta
+        setSelectedPosition(3, 100);
+        int top2 = mGridView.getLayoutManager().findViewByPosition(3).getTop();
+        assertEquals(top1 - 100, top2);
+
+        // scroll to same position without delta, it will be reset
+        setSelectedPosition(3, 0);
+        int top3 = mGridView.getLayoutManager().findViewByPosition(3).getTop();
+        assertEquals(top1, top3);
+
+        // scroll invisible item after last visible item
+        final int lastVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
+                .mGrid.getLastVisibleIndex();
+        setSelectedPosition(lastVisiblePos + 1, 100);
+        int top4 = mGridView.getLayoutManager().findViewByPosition(lastVisiblePos + 1).getTop();
+        assertEquals(top1 - 100, top4);
+
+        // scroll invisible item before first visible item
+        final int firstVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
+                .mGrid.getFirstVisibleIndex();
+        setSelectedPosition(firstVisiblePos - 1, 100);
+        int top5 = mGridView.getLayoutManager().findViewByPosition(firstVisiblePos - 1).getTop();
+        assertEquals(top1 - 100, top5);
+
+        // scroll to invisible item that is far away.
+        setSelectedPosition(50, 100);
+        int top6 = mGridView.getLayoutManager().findViewByPosition(50).getTop();
+        assertEquals(top1 - 100, top6);
+
+        // scroll to invisible item that is far away.
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mGridView.setSelectedPositionSmooth(100);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        int top7 = mGridView.getLayoutManager().findViewByPosition(100).getTop();
+        assertEquals(top1, top7);
+
+        // scroll to invisible item that is far away.
+        setSelectedPosition(10, 50);
+        int top8 = mGridView.getLayoutManager().findViewByPosition(10).getTop();
+        assertEquals(top1 - 50, top8);
+    }
+
+    public void testSetSelectionWithDeltaInGrid() throws Throwable {
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, true);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 3;
+
+        initActivity(intent);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mGridView.setSelectedPositionSmooth(10);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        int top1 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10));
+
+        humanDelay(500);
+
+        // scroll to position with delta
+        setSelectedPosition(20, 100);
+        int top2 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20));
+        assertEquals(top1 - 100, top2);
+
+        // scroll to same position without delta, it will be reset
+        setSelectedPosition(20, 0);
+        int top3 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20));
+        assertEquals(top1, top3);
+
+        // scroll invisible item after last visible item
+        final int lastVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
+                .mGrid.getLastVisibleIndex();
+        setSelectedPosition(lastVisiblePos + 1, 100);
+        int top4 = getCenterY(mGridView.getLayoutManager().findViewByPosition(lastVisiblePos + 1));
+        verifyMargin();
+        assertEquals(top1 - 100, top4);
+
+        // scroll invisible item before first visible item
+        final int firstVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
+                .mGrid.getFirstVisibleIndex();
+        setSelectedPosition(firstVisiblePos - 1, 100);
+        int top5 = getCenterY(mGridView.getLayoutManager().findViewByPosition(firstVisiblePos - 1));
+        assertEquals(top1 - 100, top5);
+
+        // scroll to invisible item that is far away.
+        setSelectedPosition(100, 100);
+        int top6 = getCenterY(mGridView.getLayoutManager().findViewByPosition(100));
+        assertEquals(top1 - 100, top6);
+
+        // scroll to invisible item that is far away.
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mGridView.setSelectedPositionSmooth(200);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        Thread.sleep(500);
+        int top7 = getCenterY(mGridView.getLayoutManager().findViewByPosition(200));
+        assertEquals(top1, top7);
+
+        // scroll to invisible item that is far away.
+        setSelectedPosition(10, 50);
+        int top8 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10));
+        assertEquals(top1 - 50, top8);
+    }
+
+
+    public void testSetSelectionWithDeltaInGrid1() throws Throwable {
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_grid);
+        intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{
+                193,176,153,141,203,184,232,139,177,206,222,136,132,237,172,137,
+                188,172,163,213,158,219,209,147,133,229,170,197,138,215,188,205,
+                223,192,225,170,195,127,229,229,210,195,134,142,160,139,130,222,
+                150,163,180,176,157,137,234,169,159,167,182,150,224,231,202,236,
+                123,140,181,223,120,185,183,221,123,210,134,158,166,208,149,128,
+                192,214,212,198,133,140,158,133,229,173,226,141,180,128,127,218,
+                192,235,183,213,216,150,143,193,125,141,219,210,195,195,192,191,
+                212,236,157,189,160,220,147,158,220,199,233,231,201,180,168,141,
+                156,204,191,183,190,153,123,210,238,151,139,221,223,200,175,191,
+                132,184,197,204,236,157,230,151,195,219,212,143,172,149,219,184,
+                164,211,132,187,172,142,174,146,127,147,206,238,188,129,199,226,
+                132,220,210,159,235,153,208,182,196,123,180,159,131,135,175,226,
+                127,134,237,211,133,225,132,124,160,226,224,200,173,137,217,169,
+                182,183,176,185,122,168,195,159,172,129,126,129,166,136,149,220,
+                178,191,192,238,180,208,234,154,222,206,239,228,129,140,203,125,
+                214,175,125,169,196,132,234,138,192,142,234,190,215,232,239,122,
+                188,158,128,221,159,237,207,157,232,138,132,214,122,199,121,191,
+                199,209,126,164,175,187,173,186,194,224,191,196,146,208,213,210,
+                164,176,202,213,123,157,179,138,217,129,186,166,237,211,157,130,
+                137,132,171,232,216,239,180,151,137,132,190,133,218,155,171,227,
+                193,147,197,164,120,218,193,154,170,196,138,222,161,235,143,154,
+                192,178,228,195,178,133,203,178,173,206,178,212,136,157,169,124,
+                172,121,128,223,238,125,217,187,184,156,169,215,231,124,210,174,
+                146,226,185,134,223,228,183,182,136,133,199,146,180,233,226,225,
+                174,233,145,235,216,170,192,171,132,132,134,223,233,148,154,162,
+                192,179,197,203,139,197,174,187,135,132,180,136,192,195,124,221,
+                120,189,233,233,146,225,234,163,215,143,132,198,156,205,151,190,
+                204,239,221,229,123,138,134,217,219,136,218,215,167,139,195,125,
+                202,225,178,226,145,208,130,194,228,197,157,215,124,147,174,123,
+                237,140,172,181,161,151,229,216,199,199,179,213,146,122,222,162,
+                139,173,165,150,160,217,207,137,165,175,129,158,134,133,178,199,
+                215,213,122,197
+        });
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, true);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 3;
+
+        initActivity(intent);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mGridView.setSelectedPositionSmooth(10);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        int top1 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10));
+
+        humanDelay(500);
+
+        // scroll to position with delta
+        setSelectedPosition(20, 100);
+        int top2 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20));
+        assertEquals(top1 - 100, top2);
+
+        // scroll to same position without delta, it will be reset
+        setSelectedPosition(20, 0);
+        int top3 = getCenterY(mGridView.getLayoutManager().findViewByPosition(20));
+        assertEquals(top1, top3);
+
+        // scroll invisible item after last visible item
+        final int lastVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
+                .mGrid.getLastVisibleIndex();
+        setSelectedPosition(lastVisiblePos + 1, 100);
+        int top4 = getCenterY(mGridView.getLayoutManager().findViewByPosition(lastVisiblePos + 1));
+        verifyMargin();
+        assertEquals(top1 - 100, top4);
+
+        // scroll invisible item before first visible item
+        final int firstVisiblePos = ((GridLayoutManager)mGridView.getLayoutManager())
+                .mGrid.getFirstVisibleIndex();
+        setSelectedPosition(firstVisiblePos - 1, 100);
+        int top5 = getCenterY(mGridView.getLayoutManager().findViewByPosition(firstVisiblePos - 1));
+        assertEquals(top1 - 100, top5);
+
+        // scroll to invisible item that is far away.
+        setSelectedPosition(100, 100);
+        int top6 = getCenterY(mGridView.getLayoutManager().findViewByPosition(100));
+        assertEquals(top1 - 100, top6);
+
+        // scroll to invisible item that is far away.
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mGridView.setSelectedPositionSmooth(200);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        Thread.sleep(500);
+        int top7 = getCenterY(mGridView.getLayoutManager().findViewByPosition(200));
+        assertEquals(top1, top7);
+
+        // scroll to invisible item that is far away.
+        setSelectedPosition(10, 50);
+        int top8 = getCenterY(mGridView.getLayoutManager().findViewByPosition(10));
+        assertEquals(top1 - 50, top8);
+    }
+
+    public void testSmoothScrollSelectionEvents() throws Throwable {
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 3;
+        initActivity(intent);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mGridView.setSelectedPositionSmooth(30);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        humanDelay(500);
+
+        final ArrayList<Integer> selectedPositions = new ArrayList<Integer>();
+        mGridView.setOnChildSelectedListener(new OnChildSelectedListener() {
+            @Override
+            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
+                selectedPositions.add(position);
+            }
+        });
+
+        sendRepeatedKeys(10, KeyEvent.KEYCODE_DPAD_UP);
+        humanDelay(500);
+        waitForScrollIdle(mVerifyLayout);
+        // should only get childselected event for item 0 once
+        assertTrue(selectedPositions.size() > 0);
+        assertEquals(0, selectedPositions.get(selectedPositions.size() - 1).intValue());
+        for (int i = selectedPositions.size() - 2; i >= 0; i--) {
+            assertFalse(0 == selectedPositions.get(i).intValue());
+        }
+
+    }
+
+    public void testSmoothScrollSelectionEventsLinear() throws Throwable {
+        mInstrumentation = getInstrumentation();
+        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+        initActivity(intent);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mGridView.setSelectedPositionSmooth(10);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        humanDelay(500);
+
+        final ArrayList<Integer> selectedPositions = new ArrayList<Integer>();
+        mGridView.setOnChildSelectedListener(new OnChildSelectedListener() {
+            @Override
+            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
+                selectedPositions.add(position);
+            }
+        });
+
+        sendRepeatedKeys(10, KeyEvent.KEYCODE_DPAD_UP);
+        humanDelay(500);
+        waitForScrollIdle(mVerifyLayout);
+        // should only get childselected event for item 0 once
+        assertTrue(selectedPositions.size() > 0);
+        assertEquals(0, selectedPositions.get(selectedPositions.size() - 1).intValue());
+        for (int i = selectedPositions.size() - 2; i >= 0; i--) {
+            assertFalse(0 == selectedPositions.get(i).intValue());
+        }
+
+    }
+}
diff --git a/v17/tests/src/android/support/v17/leanback/widget/PresenterTest.java b/v17/tests/src/android/support/v17/leanback/widget/PresenterTest.java
new file mode 100644
index 0000000..fe897da
--- /dev/null
+++ b/v17/tests/src/android/support/v17/leanback/widget/PresenterTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.graphics.Bitmap;
+import android.support.v17.leanback.app.HeadersFragment;
+import android.support.v17.leanback.R;
+import android.test.AndroidTestCase;
+import android.widget.FrameLayout;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+public class PresenterTest extends AndroidTestCase {
+
+    public void testZoomFactors() throws Throwable {
+        new ListRowPresenter(FocusHighlight.ZOOM_FACTOR_SMALL);
+        new ListRowPresenter(FocusHighlight.ZOOM_FACTOR_MEDIUM);
+        new ListRowPresenter(FocusHighlight.ZOOM_FACTOR_LARGE);
+        new ListRowPresenter(FocusHighlight.ZOOM_FACTOR_XSMALL);
+        try {
+            new ListRowPresenter(100);
+            fail("Should have thrown exception");
+        } catch (IllegalArgumentException exception) {
+        }
+    }
+
+    private void testHeaderPresenter(RowHeaderPresenter p) {
+        int expectedVisibility;
+        Presenter.ViewHolder vh = p.onCreateViewHolder(new FrameLayout(getContext()));
+        p.onBindViewHolder(vh, null);
+        expectedVisibility = p.isNullItemVisibilityGone() ? View.GONE : View.VISIBLE;
+        assertTrue("Header visibility",
+                vh.view.getVisibility() == expectedVisibility);
+        p.onBindViewHolder(vh, new Row(null));
+        assertTrue("Header visibility",
+                vh.view.getVisibility() == expectedVisibility);
+        p.onBindViewHolder(vh, new Row(new HeaderItem("")));
+        assertTrue("Header visibility",
+                vh.view.getVisibility() == View.VISIBLE);
+    }
+
+    public void testHeaderPresenter() throws Throwable {
+        HeadersFragment hf = new HeadersFragment();
+        PresenterSelector ps = hf.getPresenterSelector();
+        Presenter p = ps.getPresenter(new Object());
+        assertTrue("Row header instance",
+                p instanceof RowHeaderPresenter);
+        assertFalse("isNullItemVisibilityGone",
+                ((RowHeaderPresenter) p).isNullItemVisibilityGone());
+        testHeaderPresenter((RowHeaderPresenter) p);
+
+        ListRowPresenter lrp = new ListRowPresenter();
+        assertTrue("Row header instance",
+                lrp.getHeaderPresenter() instanceof RowHeaderPresenter);
+        RowHeaderPresenter rhp = (RowHeaderPresenter) lrp.getHeaderPresenter();
+        assertTrue("isNullItemVisibilityGone",
+                rhp.isNullItemVisibilityGone());
+        testHeaderPresenter(rhp);
+    }
+
+    public void testPlaybackControlsRowPresenter() {
+        Presenter detailsPresenter = new AbstractDetailsDescriptionPresenter() {
+            @Override
+            protected void onBindDescription(ViewHolder vh, Object item) {
+                vh.getTitle().setText("The quick brown fox jumped over the lazy dog");
+                vh.getSubtitle().setText("Subtitle");
+            }
+        };
+        PlaybackControlsRowPresenter controlsRowPresenter = new PlaybackControlsRowPresenter(
+                detailsPresenter);
+        PlaybackControlsRowPresenter.ViewHolder vh = (PlaybackControlsRowPresenter.ViewHolder)
+                controlsRowPresenter.onCreateViewHolder(new FrameLayout(getContext()));
+
+        Object item = new Object();
+        PlaybackControlsRow controlsRow = new PlaybackControlsRow(item);
+
+        controlsRowPresenter.onBindRowViewHolder(vh, controlsRow);
+        assertEquals("Controls card right panel layout height",
+                vh.view.findViewById(R.id.controls_card_right_panel).getLayoutParams().height,
+                LayoutParams.WRAP_CONTENT);
+        assertEquals("Description dock layout height",
+                vh.view.findViewById(R.id.description_dock).getLayoutParams().height,
+                LayoutParams.WRAP_CONTENT);
+        controlsRowPresenter.onUnbindRowViewHolder(vh);
+
+        controlsRow.setImageBitmap(
+                getContext(), Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888));
+        controlsRowPresenter.onBindRowViewHolder(vh, controlsRow);
+        AssertHelper.assertGreaterThan("Controls card right panel layout height",
+                vh.view.findViewById(R.id.controls_card_right_panel).getLayoutParams().height, 0);
+        assertEquals("Description dock layout height",
+                vh.view.findViewById(R.id.description_dock).getLayoutParams().height, 0);
+        controlsRowPresenter.onUnbindRowViewHolder(vh);
+    }
+}
diff --git a/v17/tests/src/android/support/v17/leanback/widget/StaggeredGridDefaultTest.java b/v17/tests/src/android/support/v17/leanback/widget/StaggeredGridDefaultTest.java
new file mode 100644
index 0000000..76456c4
--- /dev/null
+++ b/v17/tests/src/android/support/v17/leanback/widget/StaggeredGridDefaultTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+/**
+ * Testing StaggeredGridDefault algorithm
+ * @hide
+ */
+public class StaggeredGridDefaultTest extends GridTest {
+
+    StaggeredGridDefault mStaggeredGrid;
+
+    public void testWhenToFillNextRow() throws Throwable {
+        mProvider = new Provider(new int[]{100, 100, 100, 100, 40, 100, 100, 30, 100});
+
+        // layout first 8 items then all items
+        mStaggeredGrid = new StaggeredGridDefault();
+        mStaggeredGrid.setNumRows(3);
+        mStaggeredGrid.setMargin(20);
+        mStaggeredGrid.setProvider(mProvider);
+        mStaggeredGrid.appendVisibleItems(210);
+        assertEquals(dump(mStaggeredGrid) + " Should fill 8 items",
+                8, mStaggeredGrid.mLocations.size());
+        // 2nd fill rest
+        mStaggeredGrid.appendVisibleItems(100000);
+        assertEquals(dump(mStaggeredGrid) + " Should fill 9 items",
+                9, mStaggeredGrid.mLocations.size());
+        int row_result1 = mStaggeredGrid.getLocation(8).row;
+        assertEquals(dump(mStaggeredGrid) + " last item should be placed on row 1",
+                1, row_result1);
+
+        // layout all items together
+        mStaggeredGrid = new StaggeredGridDefault();
+        mStaggeredGrid.setNumRows(3);
+        mStaggeredGrid.setMargin(20);
+        mStaggeredGrid.setProvider(mProvider);
+        mStaggeredGrid.appendVisibleItems(100000);
+        assertEquals(dump(mStaggeredGrid) + " should fill 9 items",
+                9, mStaggeredGrid.mLocations.size());
+        int row_result2 = mStaggeredGrid.getLocation(8).row;
+
+        assertEquals(dump(mStaggeredGrid) + " last item should be placed on row 1",
+                1, row_result2);
+    }
+}
diff --git a/v4/Android.mk b/v4/Android.mk
index 855b4f2..154e687 100644
--- a/v4/Android.mk
+++ b/v4/Android.mk
@@ -184,6 +184,16 @@
 
 # -----------------------------------------------------------------------
 
+# A helper sub-library that makes direct use of V23 APIs.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v4-api23
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, api23)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api22
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# -----------------------------------------------------------------------
+
 # Here is the final static library that apps can link against.
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v4
@@ -195,5 +205,5 @@
     $(call all-Iaidl-files-under, java)
 
 
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4-api22
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4-api23
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v4/api21/android/support/v4/graphics/drawable/DrawableCompatL.java b/v4/api21/android/support/v4/graphics/drawable/DrawableCompatL.java
deleted file mode 100644
index 67d4e26..0000000
--- a/v4/api21/android/support/v4/graphics/drawable/DrawableCompatL.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2014 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.graphics.drawable;
-
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-
-/**
- * Implementation of drawable compatibility that can call L APIs.
- */
-class DrawableCompatL {
-
-    public static void setHotspot(Drawable drawable, float x, float y) {
-        drawable.setHotspot(x, y);
-    }
-
-    public static void setHotspotBounds(Drawable drawable, int left, int top,
-            int right, int bottom) {
-        drawable.setHotspotBounds( left, top, right, bottom);
-    }
-
-    public static void setTint(Drawable drawable, int tint) {
-        drawable.setTint(tint);
-    }
-
-    public static void setTintList(Drawable drawable, ColorStateList tint) {
-        drawable.setTintList(tint);
-    }
-
-    public static void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
-        drawable.setTintMode(tintMode);
-    }
-
-}
diff --git a/v4/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java b/v4/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
new file mode 100644
index 0000000..e5388b9
--- /dev/null
+++ b/v4/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 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.graphics.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+
+/**
+ * Implementation of drawable compatibility that can call L APIs.
+ */
+class DrawableCompatLollipop {
+
+    public static void setHotspot(Drawable drawable, float x, float y) {
+        drawable.setHotspot(x, y);
+    }
+
+    public static void setHotspotBounds(Drawable drawable, int left, int top,
+            int right, int bottom) {
+        drawable.setHotspotBounds( left, top, right, bottom);
+    }
+
+    public static void setTint(Drawable drawable, int tint) {
+        if (drawable instanceof DrawableWrapperLollipop) {
+            // GradientDrawable on Lollipop does not support tinting, so we'll use our compatible
+            // functionality instead
+            DrawableCompatBase.setTint(drawable, tint);
+        } else {
+            // Else, we'll use the framework API
+            drawable.setTint(tint);
+        }
+    }
+
+    public static void setTintList(Drawable drawable, ColorStateList tint) {
+        if (drawable instanceof DrawableWrapperLollipop) {
+            // GradientDrawable on Lollipop does not support tinting, so we'll use our compatible
+            // functionality instead
+            DrawableCompatBase.setTintList(drawable, tint);
+        } else {
+            // Else, we'll use the framework API
+            drawable.setTintList(tint);
+        }
+    }
+
+    public static void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
+        if (drawable instanceof DrawableWrapperLollipop) {
+            // GradientDrawable on Lollipop does not support tinting, so we'll use our compatible
+            // functionality instead
+            DrawableCompatBase.setTintMode(drawable, tintMode);
+        } else {
+            // Else, we'll use the framework API
+            drawable.setTintMode(tintMode);
+        }
+    }
+
+    public static Drawable wrapForTinting(Drawable drawable) {
+        if (drawable instanceof GradientDrawable) {
+            // GradientDrawable on Lollipop does not support tinting, so we'll use our compatible
+            // functionality instead
+            return new DrawableWrapperLollipop(drawable);
+        }
+        return drawable;
+    }
+
+}
diff --git a/v4/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java b/v4/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java
new file mode 100644
index 0000000..1f15040
--- /dev/null
+++ b/v4/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java
@@ -0,0 +1,59 @@
+/*
+ * 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.graphics.drawable;
+
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+class DrawableWrapperLollipop extends DrawableWrapperKitKat {
+
+    DrawableWrapperLollipop(Drawable drawable) {
+        super(drawable);
+    }
+
+    @Override
+    public void setHotspot(float x, float y) {
+        mDrawable.setHotspot(x, y);
+    }
+
+    @Override
+    public void setHotspotBounds(int left, int top, int right, int bottom) {
+        mDrawable.setHotspotBounds(left, top, right, bottom);
+    }
+
+    @Override
+    public void getOutline(Outline outline) {
+        mDrawable.getOutline(outline);
+    }
+
+    @Override
+    public void applyTheme(Resources.Theme t) {
+        mDrawable.applyTheme(t);
+    }
+
+    @Override
+    public boolean canApplyTheme() {
+        return mDrawable.canApplyTheme();
+    }
+
+    @Override
+    public Rect getDirtyBounds() {
+        return mDrawable.getDirtyBounds();
+    }
+}
diff --git a/v4/api21/android/support/v4/view/ViewCompatApi21.java b/v4/api21/android/support/v4/view/ViewCompatApi21.java
deleted file mode 100644
index 6d00e0a..0000000
--- a/v4/api21/android/support/v4/view/ViewCompatApi21.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2014 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.view.View;
-import android.view.WindowInsets;
-
-class ViewCompatApi21 {
-
-    public static void setTransitionName(View view, String transitionName) {
-        view.setTransitionName(transitionName);
-    }
-
-    public static String getTransitionName(View view) {
-        return view.getTransitionName();
-    }
-
-    public static void requestApplyInsets(View view) {
-        view.requestApplyInsets();
-    }
-
-    public static void setElevation(View view, float elevation) {
-        view.setElevation(elevation);
-    }
-
-    public static float getElevation(View view) {
-        return view.getElevation();
-    }
-
-    public static void setTranslationZ(View view, float translationZ) {
-        view.setTranslationZ(translationZ);
-    }
-
-    public static float getTranslationZ(View view) {
-        return view.getTranslationZ();
-    }
-
-    public static void setOnApplyWindowInsetsListener(View view,
-            final OnApplyWindowInsetsListener listener) {
-        view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
-            @Override
-            public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
-                // Wrap the framework insets in our wrapper
-                WindowInsetsCompatApi21 insets = new WindowInsetsCompatApi21(windowInsets);
-                // Give the listener a chance to use the wrapped insets
-                insets = (WindowInsetsCompatApi21) listener.onApplyWindowInsets(view, insets);
-                // Return the unwrapped insets
-                return insets.unwrap();
-            }
-        });
-    }
-
-    public static boolean isImportantForAccessibility(View view) {
-        return view.isImportantForAccessibility();
-    }
-}
diff --git a/v4/api21/android/support/v4/view/ViewCompatLollipop.java b/v4/api21/android/support/v4/view/ViewCompatLollipop.java
new file mode 100644
index 0000000..09b84b3
--- /dev/null
+++ b/v4/api21/android/support/v4/view/ViewCompatLollipop.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014 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.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.view.View;
+import android.view.WindowInsets;
+
+class ViewCompatLollipop {
+
+    public static void setTransitionName(View view, String transitionName) {
+        view.setTransitionName(transitionName);
+    }
+
+    public static String getTransitionName(View view) {
+        return view.getTransitionName();
+    }
+
+    public static void requestApplyInsets(View view) {
+        view.requestApplyInsets();
+    }
+
+    public static void setElevation(View view, float elevation) {
+        view.setElevation(elevation);
+    }
+
+    public static float getElevation(View view) {
+        return view.getElevation();
+    }
+
+    public static void setTranslationZ(View view, float translationZ) {
+        view.setTranslationZ(translationZ);
+    }
+
+    public static float getTranslationZ(View view) {
+        return view.getTranslationZ();
+    }
+
+    public static void setOnApplyWindowInsetsListener(View view,
+            final OnApplyWindowInsetsListener listener) {
+        view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+            @Override
+            public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
+                // Wrap the framework insets in our wrapper
+                WindowInsetsCompatApi21 insets = new WindowInsetsCompatApi21(windowInsets);
+                // Give the listener a chance to use the wrapped insets
+                insets = (WindowInsetsCompatApi21) listener.onApplyWindowInsets(view, insets);
+                // Return the unwrapped insets
+                return insets.unwrap();
+            }
+        });
+    }
+
+    public static boolean isImportantForAccessibility(View view) {
+        return view.isImportantForAccessibility();
+    }
+
+    static ColorStateList getBackgroundTintList(View view) {
+        return view.getBackgroundTintList();
+    }
+
+    static void setBackgroundTintList(View view, ColorStateList tintList) {
+        view.setBackgroundTintList(tintList);
+    }
+
+    static PorterDuff.Mode getBackgroundTintMode(View view) {
+        return view.getBackgroundTintMode();
+    }
+
+    static void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
+        view.setBackgroundTintMode(mode);
+    }
+
+    public static WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
+        if (insets instanceof WindowInsetsCompatApi21) {
+            // First unwrap the compat version so that we have the framework instance
+            WindowInsets unwrapped = ((WindowInsetsCompatApi21) insets).unwrap();
+            // Now call onApplyWindowInsets
+            WindowInsets result = v.onApplyWindowInsets(unwrapped);
+
+            if (result != unwrapped) {
+                // ...and return a newly wrapped compat insets instance if different
+                insets = new WindowInsetsCompatApi21(result);
+            }
+        }
+        return insets;
+    }
+
+    public static WindowInsetsCompat dispatchApplyWindowInsets(View v, WindowInsetsCompat insets) {
+        if (insets instanceof WindowInsetsCompatApi21) {
+            // First unwrap the compat version so that we have the framework instance
+            WindowInsets unwrapped = ((WindowInsetsCompatApi21) insets).unwrap();
+            // Now call dispatchApplyWindowInsets
+            WindowInsets result = v.dispatchApplyWindowInsets(unwrapped);
+
+            if (result != unwrapped) {
+                // ...and return a newly wrapped compat insets instance if different
+                insets = new WindowInsetsCompatApi21(result);
+            }
+        }
+        return insets;
+    }
+
+    public static void setNestedScrollingEnabled(View view, boolean enabled) {
+        view.setNestedScrollingEnabled(enabled);
+    }
+
+    public static boolean isNestedScrollingEnabled(View view) {
+        return view.isNestedScrollingEnabled();
+    }
+
+    public static boolean startNestedScroll(View view, int axes) {
+        return view.startNestedScroll(axes);
+    }
+
+    public static void stopNestedScroll(View view) {
+        view.stopNestedScroll();
+    }
+
+    public static boolean hasNestedScrollingParent(View view) {
+        return view.hasNestedScrollingParent();
+    }
+
+    public static boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
+        return view.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                offsetInWindow);
+    }
+
+    public static boolean dispatchNestedPreScroll(View view, int dx, int dy, int[] consumed,
+            int[] offsetInWindow) {
+        return view.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
+    }
+
+    public static boolean dispatchNestedFling(View view, float velocityX, float velocityY,
+            boolean consumed) {
+        return view.dispatchNestedFling(velocityX, velocityY, consumed);
+    }
+
+    public static boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
+        return view.dispatchNestedPreFling(velocityX, velocityY);
+    }
+
+    public static float getZ(View view) {
+        return view.getZ();
+    }
+}
diff --git a/v4/api21/android/support/v4/view/ViewGroupCompatApi21.java b/v4/api21/android/support/v4/view/ViewGroupCompatLollipop.java
similarity index 86%
rename from v4/api21/android/support/v4/view/ViewGroupCompatApi21.java
rename to v4/api21/android/support/v4/view/ViewGroupCompatLollipop.java
index 5ebd187..1a62404 100644
--- a/v4/api21/android/support/v4/view/ViewGroupCompatApi21.java
+++ b/v4/api21/android/support/v4/view/ViewGroupCompatLollipop.java
@@ -18,7 +18,7 @@
 
 import android.view.ViewGroup;
 
-class ViewGroupCompatApi21 {
+class ViewGroupCompatLollipop {
 
     public static void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
         group.setTransitionGroup(isTransitionGroup);
@@ -27,4 +27,8 @@
     public static boolean isTransitionGroup(ViewGroup group) {
         return group.isTransitionGroup();
     }
+
+    public static int getNestedScrollAxes(ViewGroup group) {
+        return group.getNestedScrollAxes();
+    }
 }
diff --git a/v4/api21/android/support/v4/view/ViewParentCompatLollipop.java b/v4/api21/android/support/v4/view/ViewParentCompatLollipop.java
new file mode 100644
index 0000000..7dbcf61
--- /dev/null
+++ b/v4/api21/android/support/v4/view/ViewParentCompatLollipop.java
@@ -0,0 +1,98 @@
+/*
+ * 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.util.Log;
+import android.view.View;
+import android.view.ViewParent;
+
+class ViewParentCompatLollipop {
+    private static final String TAG = "ViewParentCompat";
+
+    public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
+            int nestedScrollAxes) {
+        try {
+            return parent.onStartNestedScroll(child, target, nestedScrollAxes);
+        } catch (AbstractMethodError e) {
+            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
+                    "method onStartNestedScroll", e);
+            return false;
+        }
+    }
+
+    public static void onNestedScrollAccepted(ViewParent parent, View child, View target,
+            int nestedScrollAxes) {
+        try {
+            parent.onNestedScrollAccepted(child, target, nestedScrollAxes);
+        } catch (AbstractMethodError e) {
+            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
+                    "method onNestedScrollAccepted", e);
+        }
+    }
+
+    public static void onStopNestedScroll(ViewParent parent, View target) {
+        try {
+            parent.onStopNestedScroll(target);
+        } catch (AbstractMethodError e) {
+            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
+                    "method onStopNestedScroll", e);
+        }
+    }
+
+    public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,
+            int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
+        try {
+            parent.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
+        } catch (AbstractMethodError e) {
+            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
+                    "method onNestedScroll", e);
+        }
+    }
+
+    public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
+            int[] consumed) {
+        try {
+            parent.onNestedPreScroll(target, dx, dy, consumed);
+        } catch (AbstractMethodError e) {
+            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
+                    "method onNestedPreScroll", e);
+        }
+    }
+
+    public static boolean onNestedFling(ViewParent parent, View target, float velocityX,
+            float velocityY, boolean consumed) {
+        try {
+            return parent.onNestedFling(target, velocityX, velocityY, consumed);
+        } catch (AbstractMethodError e) {
+            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
+                    "method onNestedFling", e);
+            return false;
+        }
+    }
+
+    public static boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
+            float velocityY) {
+        try {
+            return parent.onNestedPreFling(target, velocityX, velocityY);
+        } catch (AbstractMethodError e) {
+            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
+                    "method onNestedPreFling", e);
+            return false;
+        }
+    }
+}
diff --git a/v4/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java b/v4/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
index 0ae3a5c..ce6abf3 100644
--- a/v4/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
+++ b/v4/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.view.accessibility;
 
+import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
@@ -46,6 +47,22 @@
                 columnSpan, heading, selected);
     }
 
+    public static CharSequence getError(Object info) {
+        return ((AccessibilityNodeInfo) info).getError();
+    }
+
+    public static void setError(Object info, CharSequence error) {
+        ((AccessibilityNodeInfo) info).setError(error);
+    }
+
+    public static void setLabelFor(Object info, View labeled) {
+        ((AccessibilityNodeInfo) info).setLabelFor(labeled);
+    }
+
+    public static void setLabelFor(Object info, View root, int virtualDescendantId) {
+        ((AccessibilityNodeInfo) info).setLabelFor(root, virtualDescendantId);
+    }
+
     static class CollectionItemInfo {
         public static boolean isSelected(Object info) {
             return ((AccessibilityNodeInfo.CollectionItemInfo) info).isSelected();
diff --git a/v4/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java b/v4/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
new file mode 100644
index 0000000..6ba379c
--- /dev/null
+++ b/v4/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
@@ -0,0 +1,27 @@
+/*
+ * 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.widget;
+
+import android.widget.EdgeEffect;
+
+class EdgeEffectCompatLollipop {
+    public static boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
+        ((EdgeEffect) edgeEffect).onPull(deltaDistance, displacement);
+        return true;
+    }
+}
diff --git a/v4/api21/android/support/v4/widget/PopupWindowCompatApi21.java b/v4/api21/android/support/v4/widget/PopupWindowCompatApi21.java
new file mode 100644
index 0000000..3440f3c
--- /dev/null
+++ b/v4/api21/android/support/v4/widget/PopupWindowCompatApi21.java
@@ -0,0 +1,60 @@
+/*
+ * 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.widget;
+
+import android.util.Log;
+import android.widget.PopupWindow;
+
+import java.lang.reflect.Field;
+
+class PopupWindowCompatApi21 {
+
+    private static final String TAG = "PopupWindowCompatApi21";
+
+    private static Field sOverlapAnchorField;
+
+    static {
+        try {
+            sOverlapAnchorField = PopupWindow.class.getDeclaredField("mOverlapAnchor");
+            sOverlapAnchorField.setAccessible(true);
+        } catch (NoSuchFieldException e) {
+            Log.i(TAG, "Could not fetch mOverlapAnchor field from PopupWindow", e);
+        }
+    }
+
+    static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+        if (sOverlapAnchorField != null) {
+            try {
+                sOverlapAnchorField.set(popupWindow, overlapAnchor);
+            } catch (IllegalAccessException e) {
+                Log.i(TAG, "Could not set overlap anchor field in PopupWindow", e);
+            }
+        }
+    }
+
+    static boolean getOverlapAnchor(PopupWindow popupWindow) {
+        if (sOverlapAnchorField != null) {
+            try {
+                return (Boolean) sOverlapAnchorField.get(popupWindow);
+            } catch (IllegalAccessException e) {
+                Log.i(TAG, "Could not get overlap anchor field in PopupWindow", e);
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/v4/api22/android/support/v4/app/ActivityCompat22.java b/v4/api22/android/support/v4/app/ActivityCompat22.java
new file mode 100644
index 0000000..3946f1d
--- /dev/null
+++ b/v4/api22/android/support/v4/app/ActivityCompat22.java
@@ -0,0 +1,26 @@
+/*
+ * 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.app;
+
+import android.app.Activity;
+import android.net.Uri;
+
+class ActivityCompat22 {
+    public static Uri getReferrer(Activity activity) {
+        return activity.getReferrer();
+    }
+}
diff --git a/v4/api22/android/support/v4/graphics/drawable/DrawableCompatApi22.java b/v4/api22/android/support/v4/graphics/drawable/DrawableCompatApi22.java
new file mode 100644
index 0000000..bfd2bea
--- /dev/null
+++ b/v4/api22/android/support/v4/graphics/drawable/DrawableCompatApi22.java
@@ -0,0 +1,31 @@
+/*
+ * 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.graphics.drawable;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Implementation of drawable compatibility that can call Lollipop-MR1 APIs.
+ */
+class DrawableCompatApi22 {
+
+    public static Drawable wrapForTinting(Drawable drawable) {
+        // We don't need to wrap anything in Lollipop-MR1
+        return drawable;
+    }
+
+}
diff --git a/v4/api23/android/support/v4/text/ICUCompatApi23.java b/v4/api23/android/support/v4/text/ICUCompatApi23.java
new file mode 100644
index 0000000..f013522
--- /dev/null
+++ b/v4/api23/android/support/v4/text/ICUCompatApi23.java
@@ -0,0 +1,55 @@
+/*
+ * 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.text;
+
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Locale;
+
+public class ICUCompatApi23 {
+
+    private static final String TAG = "ICUCompatIcs";
+
+    private static Method sAddLikelySubtagsMethod;
+
+    static {
+        try {
+            // This class should always exist on API-23 since it's CTS tested.
+            final Class<?> clazz = Class.forName("libcore.icu.ICU");
+            sAddLikelySubtagsMethod = clazz.getMethod("addLikelySubtags",
+                    new Class[]{ Locale.class });
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+
+    public static String maximizeAndGetScript(Locale locale) {
+        try {
+            final Object[] args = new Object[] { locale };
+            return ((Locale) sAddLikelySubtagsMethod.invoke(null, args)).getScript();
+        } catch (InvocationTargetException e) {
+            Log.w(TAG, e);
+        } catch (IllegalAccessException e) {
+            Log.w(TAG, e);
+        }
+
+        return locale.getScript();
+    }
+}
diff --git a/v4/api23/android/support/v4/view/MenuItemCompatApi23.java b/v4/api23/android/support/v4/view/MenuItemCompatApi23.java
new file mode 100644
index 0000000..4cdeae0
--- /dev/null
+++ b/v4/api23/android/support/v4/view/MenuItemCompatApi23.java
@@ -0,0 +1,31 @@
+/*
+ * 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.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.view.MenuItem;
+
+class MenuItemCompatApi23 {
+    public static MenuItem setIconTintList(MenuItem item, ColorStateList tint) {
+        return item.setIconTintList(tint);
+    }
+
+    public static MenuItem setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) {
+        return item.setIconTintMode(tintMode);
+    }
+}
diff --git a/v4/api23/android/support/v4/widget/PopupWindowCompatApi23.java b/v4/api23/android/support/v4/widget/PopupWindowCompatApi23.java
new file mode 100644
index 0000000..61d74bd
--- /dev/null
+++ b/v4/api23/android/support/v4/widget/PopupWindowCompatApi23.java
@@ -0,0 +1,31 @@
+/*
+ * 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.widget;
+
+import android.widget.PopupWindow;
+
+class PopupWindowCompatApi23 {
+
+    static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+        popupWindow.setOverlapAnchor(overlapAnchor);
+    }
+
+    static boolean getOverlapAnchor(PopupWindow popupWindow) {
+        return popupWindow.getOverlapAnchor();
+    }
+
+}
diff --git a/v4/build.gradle b/v4/build.gradle
index 1cf63e8..d37bb27 100644
--- a/v4/build.gradle
+++ b/v4/build.gradle
@@ -30,6 +30,7 @@
 def api20SS        = createApiSourceset('api20',        'api20',         '20', kitkatSS)
 def api21SS        = createApiSourceset('api21',        'api21',         '21', api20SS)
 def api22SS        = createApiSourceset('api22',        'api22',         'current', api21SS)
+def api23SS        = createApiSourceset('api23',        'api23',         'current', api22SS)
 
 
 def createApiSourceset(String name, String folder, String apiLevel, SourceSet previousSource) {
@@ -88,6 +89,11 @@
         // TODO: fix errors and reenable.
         abortOnError false
     }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
 }
 
 android.libraryVariants.all { variant ->
diff --git a/v4/donut/android/support/v4/graphics/drawable/DrawableCompatBase.java b/v4/donut/android/support/v4/graphics/drawable/DrawableCompatBase.java
new file mode 100644
index 0000000..4809618
--- /dev/null
+++ b/v4/donut/android/support/v4/graphics/drawable/DrawableCompatBase.java
@@ -0,0 +1,53 @@
+/*
+ * 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.graphics.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Base implementation of drawable compatibility.
+ */
+class DrawableCompatBase {
+
+    public static void setTint(Drawable drawable, int tint) {
+        if (drawable instanceof DrawableWrapper) {
+            ((DrawableWrapper) drawable).setTint(tint);
+        }
+    }
+
+    public static void setTintList(Drawable drawable, ColorStateList tint) {
+        if (drawable instanceof DrawableWrapper) {
+            ((DrawableWrapper) drawable).setTintList(tint);
+        }
+    }
+
+    public static void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
+        if (drawable instanceof DrawableWrapper) {
+            ((DrawableWrapper) drawable).setTintMode(tintMode);
+        }
+    }
+
+    public static Drawable wrapForTinting(Drawable drawable) {
+        if (!(drawable instanceof DrawableWrapperDonut)) {
+            return new DrawableWrapperDonut(drawable);
+        }
+        return drawable;
+    }
+
+}
diff --git a/v4/donut/android/support/v4/graphics/drawable/DrawableWrapper.java b/v4/donut/android/support/v4/graphics/drawable/DrawableWrapper.java
new file mode 100644
index 0000000..1073f34
--- /dev/null
+++ b/v4/donut/android/support/v4/graphics/drawable/DrawableWrapper.java
@@ -0,0 +1,41 @@
+/*
+ * 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.graphics.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Interface which allows a {@link android.graphics.drawable.Drawable} to receive tinting calls from
+ * {@code DrawableCompat}.
+ *
+ * @hide
+ */
+public interface DrawableWrapper {
+
+    void setTint(int tint);
+
+    void setTintList(ColorStateList tint);
+
+    void setTintMode(PorterDuff.Mode tintMode);
+
+    Drawable getWrappedDrawable();
+
+    void setWrappedDrawable(Drawable drawable);
+
+}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/DrawableWrapper.java b/v4/donut/android/support/v4/graphics/drawable/DrawableWrapperDonut.java
similarity index 62%
copy from v7/appcompat/src/android/support/v7/internal/widget/DrawableWrapper.java
copy to v4/donut/android/support/v4/graphics/drawable/DrawableWrapperDonut.java
index ad15064..e1cbbe0 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/DrawableWrapper.java
+++ b/v4/donut/android/support/v4/graphics/drawable/DrawableWrapperDonut.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.v7.internal.widget;
+package android.support.v4.graphics.drawable;
 
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
@@ -23,21 +23,26 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.DrawableContainer;
-import android.os.Build;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.view.View;
 
 /**
- * Base wrapper that delegates all calls to another {@link Drawable}. The wrapped {@link Drawable}
- * <em>must</em> be fully released from any {@link View} before wrapping, otherwise internal {@link
- * Drawable.Callback} may be dropped.
+ * Drawable which delegates all calls to it's wrapped {@link android.graphics.drawable.Drawable}.
+ * <p>
+ * Also allows backward compatible tinting via a color or {@link ColorStateList}.
+ * This functionality is accessed via static methods in {@code DrawableCompat}.
  */
-class DrawableWrapper extends Drawable implements Drawable.Callback {
+class DrawableWrapperDonut extends Drawable implements Drawable.Callback, DrawableWrapper {
 
-    private Drawable mDrawable;
+    static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
 
-    public DrawableWrapper(Drawable drawable) {
+    private ColorStateList mTintList;
+    private PorterDuff.Mode mTintMode = DEFAULT_MODE;
+
+    private boolean mValidCurrentColor;
+    private int mCurrentColor;
+
+    Drawable mDrawable;
+
+    DrawableWrapperDonut(Drawable drawable) {
         setWrappedDrawable(drawable);
     }
 
@@ -47,9 +52,8 @@
     }
 
     @Override
-    public void setBounds(int left, int top, int right, int bottom) {
-        super.setBounds(left, top, right, bottom);
-        mDrawable.setBounds(left, top, right, bottom);
+    protected void onBoundsChange(Rect bounds) {
+        mDrawable.setBounds(bounds);
     }
 
     @Override
@@ -84,12 +88,14 @@
 
     @Override
     public boolean isStateful() {
-        return mDrawable.isStateful();
+        return (mTintList != null && mTintList.isStateful()) || mDrawable.isStateful();
     }
 
     @Override
     public boolean setState(final int[] stateSet) {
-        return mDrawable.setState(stateSet);
+        boolean handled = mDrawable.setState(stateSet);
+        handled = updateTint(stateSet) || handled;
+        return handled;
     }
 
     @Override
@@ -97,10 +103,6 @@
         return mDrawable.getState();
     }
 
-    public void jumpToCurrentState() {
-        DrawableCompat.jumpToCurrentState(mDrawable);
-    }
-
     @Override
     public Drawable getCurrent() {
         return mDrawable.getCurrent();
@@ -146,6 +148,18 @@
         return mDrawable.getPadding(padding);
     }
 
+    @Override
+    public Drawable mutate() {
+        Drawable wrapped = mDrawable;
+        Drawable mutated = wrapped.mutate();
+        if (mutated != wrapped) {
+            // If mutate() returned a new instance, update our reference
+            setWrappedDrawable(mutated);
+        }
+        // We return ourselves, since only the wrapped drawable needs to mutate
+        return this;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -173,44 +187,48 @@
     }
 
     @Override
-    public void setAutoMirrored(boolean mirrored) {
-        DrawableCompat.setAutoMirrored(mDrawable, mirrored);
-    }
-
-    @Override
-    public boolean isAutoMirrored() {
-        return DrawableCompat.isAutoMirrored(mDrawable);
-    }
-
-    @Override
     public void setTint(int tint) {
-        DrawableCompat.setTint(mDrawable, tint);
+        setTintList(ColorStateList.valueOf(tint));
     }
 
     @Override
     public void setTintList(ColorStateList tint) {
-        DrawableCompat.setTintList(mDrawable, tint);
+        mTintList = tint;
+        updateTint(getState());
     }
 
     @Override
     public void setTintMode(PorterDuff.Mode tintMode) {
-        DrawableCompat.setTintMode(mDrawable, tintMode);
+        mTintMode = tintMode;
+        updateTint(getState());
     }
 
-    @Override
-    public void setHotspot(float x, float y) {
-        DrawableCompat.setHotspot(mDrawable, x, y);
+    private boolean updateTint(int[] state) {
+        if (mTintList != null && mTintMode != null) {
+            final int color = mTintList.getColorForState(state, mTintList.getDefaultColor());
+            if (!mValidCurrentColor || color != mCurrentColor) {
+                setColorFilter(color, mTintMode);
+                mCurrentColor = color;
+                mValidCurrentColor = true;
+                return true;
+            }
+        } else {
+            mValidCurrentColor = false;
+            clearColorFilter();
+        }
+        return false;
     }
 
-    @Override
-    public void setHotspotBounds(int left, int top, int right, int bottom) {
-        DrawableCompat.setHotspotBounds(mDrawable, left, top, right, bottom);
-    }
-
+    /**
+     * Returns the wrapped {@link Drawable}
+     */
     public Drawable getWrappedDrawable() {
         return mDrawable;
     }
 
+    /**
+     * Sets the current wrapped {@link Drawable}
+     */
     public void setWrappedDrawable(Drawable drawable) {
         if (mDrawable != null) {
             mDrawable.setCallback(null);
@@ -221,5 +239,7 @@
         if (drawable != null) {
             drawable.setCallback(this);
         }
+        // Invalidate ourselves
+        invalidateSelf();
     }
 }
diff --git a/v4/donut/android/support/v4/view/LayoutInflaterCompatBase.java b/v4/donut/android/support/v4/view/LayoutInflaterCompatBase.java
new file mode 100644
index 0000000..8f210d3
--- /dev/null
+++ b/v4/donut/android/support/v4/view/LayoutInflaterCompatBase.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+
+class LayoutInflaterCompatBase {
+
+    static class FactoryWrapper implements LayoutInflater.Factory {
+
+        final LayoutInflaterFactory mDelegateFactory;
+
+        FactoryWrapper(LayoutInflaterFactory delegateFactory) {
+            mDelegateFactory = delegateFactory;
+        }
+
+        @Override
+        public View onCreateView(String name, Context context, AttributeSet attrs) {
+            return mDelegateFactory.onCreateView(null, name, context, attrs);
+        }
+    }
+
+    static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
+        inflater.setFactory(factory != null ? new FactoryWrapper(factory) : null);
+    }
+
+}
diff --git a/v4/donut/android/support/v4/view/LayoutInflaterFactory.java b/v4/donut/android/support/v4/view/LayoutInflaterFactory.java
new file mode 100644
index 0000000..02b3d63
--- /dev/null
+++ b/v4/donut/android/support/v4/view/LayoutInflaterFactory.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 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.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Used with {@code LayoutInflaterCompat.setFactory()}. Offers the same API as
+ * {@code LayoutInflater.Factory2}.
+ */
+public interface LayoutInflaterFactory {
+
+    /**
+     * Hook you can supply that is called when inflating from a LayoutInflater.
+     * You can use this to customize the tag names available in your XML
+     * layout files.
+     *
+     * @param parent The parent that the created view will be placed
+     * in; <em>note that this may be null</em>.
+     * @param name Tag name to be inflated.
+     * @param context The context the view is being created in.
+     * @param attrs Inflation attributes as specified in XML file.
+     *
+     * @return View Newly created view. Return null for the default
+     *         behavior.
+     */
+    public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
+
+}
diff --git a/v4/donut/android/support/v4/view/TintableBackgroundView.java b/v4/donut/android/support/v4/view/TintableBackgroundView.java
new file mode 100644
index 0000000..83c014b
--- /dev/null
+++ b/v4/donut/android/support/v4/view/TintableBackgroundView.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 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.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.support.annotation.Nullable;
+
+/**
+ * Interface which allows a {@link android.view.View} to receive background tinting calls from
+ * {@code ViewCompat} when running on API v20 devices or lower.
+ */
+public interface TintableBackgroundView {
+
+    /**
+     * Applies a tint to the background drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to {@code View.setBackground(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @see #getSupportBackgroundTintList()
+     */
+    void setSupportBackgroundTintList(@Nullable ColorStateList tint);
+
+    /**
+     * Return the tint applied to the background drawable, if specified.
+     *
+     * @return the tint applied to the background drawable
+     */
+    @Nullable
+    ColorStateList getSupportBackgroundTintList();
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setSupportBackgroundTintList(ColorStateList)}} to the background
+     * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @see #getSupportBackgroundTintMode()
+     */
+    void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode);
+
+    /**
+     * Return the blending mode used to apply the tint to the background
+     * drawable, if specified.
+     *
+     * @return the blending mode used to apply the tint to the background
+     *         drawable
+     */
+    @Nullable
+    PorterDuff.Mode getSupportBackgroundTintMode();
+}
diff --git a/v4/donut/android/support/v4/view/ViewCompatBase.java b/v4/donut/android/support/v4/view/ViewCompatBase.java
new file mode 100644
index 0000000..7f525aa
--- /dev/null
+++ b/v4/donut/android/support/v4/view/ViewCompatBase.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.view.View;
+
+class ViewCompatBase {
+
+    static ColorStateList getBackgroundTintList(View view) {
+        return (view instanceof TintableBackgroundView)
+                ? ((TintableBackgroundView) view).getSupportBackgroundTintList()
+                : null;
+    }
+
+    static void setBackgroundTintList(View view, ColorStateList tintList) {
+        if (view instanceof TintableBackgroundView) {
+            ((TintableBackgroundView) view).setSupportBackgroundTintList(tintList);
+        }
+    }
+
+    static PorterDuff.Mode getBackgroundTintMode(View view) {
+        return (view instanceof TintableBackgroundView)
+                ? ((TintableBackgroundView) view).getSupportBackgroundTintMode()
+                : null;
+    }
+
+    static void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
+        if (view instanceof TintableBackgroundView) {
+            ((TintableBackgroundView) view).setSupportBackgroundTintMode(mode);
+        }
+    }
+
+    static boolean isLaidOut(View view) {
+        return view.getWidth() > 0 && view.getHeight() > 0;
+    }
+}
diff --git a/v4/api21/android/support/v4/view/ViewGroupCompatApi21.java b/v4/gingerbread/android/support/v4/view/MotionEventCompatGingerbread.java
similarity index 61%
copy from v4/api21/android/support/v4/view/ViewGroupCompatApi21.java
copy to v4/gingerbread/android/support/v4/view/MotionEventCompatGingerbread.java
index 5ebd187..f43dc0a 100644
--- a/v4/api21/android/support/v4/view/ViewGroupCompatApi21.java
+++ b/v4/gingerbread/android/support/v4/view/MotionEventCompatGingerbread.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -16,15 +16,13 @@
 
 package android.support.v4.view;
 
-import android.view.ViewGroup;
+import android.view.MotionEvent;
 
-class ViewGroupCompatApi21 {
-
-    public static void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-        group.setTransitionGroup(isTransitionGroup);
-    }
-
-    public static boolean isTransitionGroup(ViewGroup group) {
-        return group.isTransitionGroup();
+/**
+ * Motion event compatibility class for API 8+.
+ */
+class MotionEventCompatGingerbread {
+    public static int getSource(MotionEvent event) {
+        return event.getSource();
     }
 }
diff --git a/v4/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java b/v4/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
index 4c5d48b..def23ed 100644
--- a/v4/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
+++ b/v4/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
@@ -16,13 +16,23 @@
 
 package android.support.v4.graphics.drawable;
 
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 
 /**
  * Implementation of drawable compatibility that can call Honeycomb APIs.
  */
 class DrawableCompatHoneycomb {
+
     public static void jumpToCurrentState(Drawable drawable) {
         drawable.jumpToCurrentState();
     }
+
+    public static Drawable wrapForTinting(Drawable drawable) {
+        if (!(drawable instanceof DrawableWrapperHoneycomb)) {
+            return new DrawableWrapperHoneycomb(drawable);
+        }
+        return drawable;
+    }
 }
diff --git a/v4/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java b/v4/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java
new file mode 100644
index 0000000..f9fd7d8
--- /dev/null
+++ b/v4/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java
@@ -0,0 +1,31 @@
+/*
+ * 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.graphics.drawable;
+
+import android.graphics.drawable.Drawable;
+
+class DrawableWrapperHoneycomb extends DrawableWrapperDonut {
+
+    DrawableWrapperHoneycomb(Drawable drawable) {
+        super(drawable);
+    }
+
+    @Override
+    public void jumpToCurrentState() {
+        mDrawable.jumpToCurrentState();
+    }
+}
diff --git a/v4/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java b/v4/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java
new file mode 100644
index 0000000..b57731c
--- /dev/null
+++ b/v4/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+
+class LayoutInflaterCompatHC {
+
+    static class FactoryWrapperHC extends LayoutInflaterCompatBase.FactoryWrapper
+            implements LayoutInflater.Factory2 {
+
+        FactoryWrapperHC(LayoutInflaterFactory delegateFactory) {
+            super(delegateFactory);
+        }
+
+        @Override
+        public View onCreateView(View parent, String name, Context context,
+                AttributeSet attributeSet) {
+            return mDelegateFactory.onCreateView(parent, name, context, attributeSet);
+        }
+    }
+
+    static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
+        inflater.setFactory2(factory != null ? new FactoryWrapperHC(factory) : null);
+    }
+
+}
diff --git a/v4/honeycomb/android/support/v4/view/ViewCompatHC.java b/v4/honeycomb/android/support/v4/view/ViewCompatHC.java
index fbcc31e..8b21259 100644
--- a/v4/honeycomb/android/support/v4/view/ViewCompatHC.java
+++ b/v4/honeycomb/android/support/v4/view/ViewCompatHC.java
@@ -156,4 +156,8 @@
     public static void setActivated(View view, boolean activated) {
         view.setActivated(activated);
     }
+
+    public static int combineMeasuredStates(int curState, int newState) {
+        return View.combineMeasuredStates(curState, newState);
+    }
 }
diff --git a/v4/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java b/v4/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java
new file mode 100644
index 0000000..406fcf3
--- /dev/null
+++ b/v4/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java
@@ -0,0 +1,32 @@
+/*
+ * 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.view.MotionEvent;
+
+/**
+ * Motion event compatibility class for API 12+.
+ */
+class MotionEventCompatHoneycombMr1 {
+    static float getAxisValue(MotionEvent event, int axis) {
+        return event.getAxisValue(axis);
+    }
+
+    static float getAxisValue(MotionEvent event, int axis, int pointerIndex) {
+        return event.getAxisValue(axis, pointerIndex);
+    }
+}
diff --git a/v4/ics/android/support/v4/text/ICUCompatIcs.java b/v4/ics/android/support/v4/text/ICUCompatIcs.java
index 7dc5d3c..dfb9e7e 100644
--- a/v4/ics/android/support/v4/text/ICUCompatIcs.java
+++ b/v4/ics/android/support/v4/text/ICUCompatIcs.java
@@ -20,6 +20,7 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.Locale;
 
 class ICUCompatIcs {
 
@@ -38,15 +39,27 @@
                         new Class[]{ String.class });
             }
         } catch (Exception e) {
+            sGetScriptMethod = null;
+            sAddLikelySubtagsMethod = null;
+
             // Nothing we can do here, we just log the exception
             Log.w(TAG, e);
         }
     }
 
-    public static String getScript(String locale) {
+    public static String maximizeAndGetScript(Locale locale) {
+        final String localeWithSubtags = addLikelySubtags(locale);
+        if (localeWithSubtags != null) {
+            return getScript(localeWithSubtags);
+        }
+
+        return null;
+    }
+
+    private static String getScript(String localeStr) {
         try {
             if (sGetScriptMethod != null) {
-                final Object[] args = new Object[] { locale };
+                final Object[] args = new Object[] { localeStr };
                 return (String) sGetScriptMethod.invoke(null, args);
             }
         } catch (IllegalAccessException e) {
@@ -60,10 +73,11 @@
         return null;
     }
 
-    public static String addLikelySubtags(String locale) {
+    private static String addLikelySubtags(Locale locale) {
+        final String localeStr = locale.toString();
         try {
             if (sAddLikelySubtagsMethod != null) {
-                final Object[] args = new Object[] { locale };
+                final Object[] args = new Object[] { localeStr };
                 return (String) sAddLikelySubtagsMethod.invoke(null, args);
             }
         } catch (IllegalAccessException e) {
@@ -74,6 +88,7 @@
             // Nothing we can do here, we just log the exception
             Log.w(TAG, e);
         }
-        return locale;
+
+        return localeStr;
     }
 }
diff --git a/v4/java/android/support/v4/app/ActivityCompat.java b/v4/java/android/support/v4/app/ActivityCompat.java
index 8d8bf04..5b4f84e 100644
--- a/v4/java/android/support/v4/app/ActivityCompat.java
+++ b/v4/java/android/support/v4/app/ActivityCompat.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.graphics.Matrix;
 import android.graphics.RectF;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -163,6 +164,27 @@
     }
 
     /**
+     * Backwards compatible implementation of {@link android.app.Activity#getReferrer()
+     * Activity.getReferrer}.  Uses the platform's implementation if available, otherwise
+     * only falls back to digging any explicitly specified referrer from the activity's intent.
+     */
+    public Uri getReferrer(Activity activity) {
+        if (Build.VERSION.SDK_INT >= 22) {
+            return ActivityCompat22.getReferrer(activity);
+        }
+        Intent intent = activity.getIntent();
+        Uri referrer = intent.getParcelableExtra("android.intent.extra.REFERRER");
+        if (referrer != null) {
+            return referrer;
+        }
+        String referrerName = intent.getStringExtra("android.intent.extra.REFERRER_NAME");
+        if (referrerName != null) {
+            return Uri.parse(referrerName);
+        }
+        return null;
+    }
+
+    /**
      * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
      * android.view.View, String)} was used to start an Activity, <var>callback</var>
      * will be called to handle shared elements on the <i>launched</i> Activity. This requires
diff --git a/v4/java/android/support/v4/app/BackStackRecord.java b/v4/java/android/support/v4/app/BackStackRecord.java
index da9354e..25f3ebf 100644
--- a/v4/java/android/support/v4/app/BackStackRecord.java
+++ b/v4/java/android/support/v4/app/BackStackRecord.java
@@ -191,6 +191,7 @@
 final class BackStackRecord extends FragmentTransaction implements
         FragmentManager.BackStackEntry, Runnable {
     static final String TAG = FragmentManagerImpl.TAG;
+    static final boolean SUPPORTS_TRANSITIONS = Build.VERSION.SDK_INT >= 21;
 
     final FragmentManagerImpl mManager;
 
@@ -517,7 +518,7 @@
 
     @Override
     public FragmentTransaction addSharedElement(View sharedElement, String name) {
-        if (Build.VERSION.SDK_INT >= 21) {
+        if (SUPPORTS_TRANSITIONS) {
             String transitionName = FragmentTransitionCompat21.getTransitionName(sharedElement);
             if (transitionName == null) {
                 throw new IllegalArgumentException("Unique transitionNames are required for all" +
@@ -651,7 +652,7 @@
         TransitionState state = null;
         SparseArray<Fragment> firstOutFragments = null;
         SparseArray<Fragment> lastInFragments = null;
-        if (Build.VERSION.SDK_INT >= 21) {
+        if (SUPPORTS_TRANSITIONS) {
             firstOutFragments = new SparseArray<Fragment>();
             lastInFragments = new SparseArray<Fragment>();
 
@@ -878,12 +879,14 @@
             dump("  ", null, pw, null);
         }
 
-        if (state == null) {
-            if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
-                state = beginTransition(firstOutFragments, lastInFragments, true);
+        if (SUPPORTS_TRANSITIONS) {
+            if (state == null) {
+                if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
+                    state = beginTransition(firstOutFragments, lastInFragments, true);
+                }
+            } else if (!doStateMove) {
+                setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
             }
-        } else if (!doStateMove) {
-            setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
         }
 
         bumpBackStackNesting(-1);
diff --git a/v4/java/android/support/v4/app/DialogFragment.java b/v4/java/android/support/v4/app/DialogFragment.java
index 3c7773a..343c145 100644
--- a/v4/java/android/support/v4/app/DialogFragment.java
+++ b/v4/java/android/support/v4/app/DialogFragment.java
@@ -23,6 +23,7 @@
 import android.os.Bundle;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.annotation.StyleRes;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -282,7 +283,7 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         mShowsDialog = mContainerId == 0;
diff --git a/v4/java/android/support/v4/app/Fragment.java b/v4/java/android/support/v4/app/Fragment.java
index 9bad061..eb6ab0d 100644
--- a/v4/java/android/support/v4/app/Fragment.java
+++ b/v4/java/android/support/v4/app/Fragment.java
@@ -31,6 +31,7 @@
 import android.support.annotation.StringRes;
 import android.support.v4.util.SimpleArrayMap;
 import android.support.v4.util.DebugUtils;
+import android.support.v4.view.LayoutInflaterCompat;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -409,7 +410,7 @@
      * the given fragment class.  This is a runtime exception; it is not
      * normally expected to happen.
      */
-    public static Fragment instantiate(Context context, String fname, Bundle args) {
+    public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
         try {
             Class<?> clazz = sClassMap.get(fname);
             if (clazz == null) {
@@ -932,7 +933,7 @@
     public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
         LayoutInflater result = mActivity.getLayoutInflater().cloneInContext(mActivity);
         getChildFragmentManager(); // Init if needed; use raw implementation below.
-        result.setFactory(mChildFragmentManager.getLayoutInflaterFactory());
+        LayoutInflaterCompat.setFactory(result, mChildFragmentManager.getLayoutInflaterFactory());
         return result;
     }
     
@@ -1011,7 +1012,7 @@
      * @param savedInstanceState If the fragment is being re-created from
      * a previous saved state, this is the state.
      */
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         mCalled = true;
     }
 
@@ -1034,6 +1035,7 @@
      * 
      * @return Return the View for the fragment's UI, or null.
      */
+    @Nullable
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
             @Nullable Bundle savedInstanceState) {
         return null;
@@ -1741,6 +1743,7 @@
         mChildFragmentManager = new FragmentManagerImpl();
         mChildFragmentManager.attachActivity(mActivity, new FragmentContainer() {
             @Override
+            @Nullable
             public View findViewById(int id) {
                 if (mView == null) {
                     throw new IllegalStateException("Fragment does not have a view");
diff --git a/v4/java/android/support/v4/app/FragmentActivity.java b/v4/java/android/support/v4/app/FragmentActivity.java
index bb1a001..566ffed 100644
--- a/v4/java/android/support/v4/app/FragmentActivity.java
+++ b/v4/java/android/support/v4/app/FragmentActivity.java
@@ -26,6 +26,7 @@
 import android.os.Message;
 import android.os.Parcelable;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.v4.util.SimpleArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -105,6 +106,7 @@
     final FragmentManagerImpl mFragments = new FragmentManagerImpl();
     final FragmentContainer mContainer = new FragmentContainer() {
         @Override
+        @Nullable
         public View findViewById(int id) {
             return FragmentActivity.this.findViewById(id);
         }
@@ -245,7 +247,7 @@
      * Perform initialization of all fragments and loaders.
      */
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
         mFragments.attachActivity(this, mContainer, null);
         // Old versions of the platform didn't do this!
         if (getLayoutInflater().getFactory() == null) {
@@ -289,12 +291,13 @@
      * Add support for inflating the &lt;fragment> tag.
      */
     @Override
+    @Nullable
     public View onCreateView(String name, @NonNull Context context, @NonNull AttributeSet attrs) {
         if (!"fragment".equals(name)) {
             return super.onCreateView(name, context, attrs);
         }
 
-        final View v = mFragments.onCreateView(name, context, attrs);
+        final View v = mFragments.onCreateView(null, name, context, attrs);
         if (v == null) {
             return super.onCreateView(name, context, attrs);
         }
diff --git a/v4/java/android/support/v4/app/FragmentManager.java b/v4/java/android/support/v4/app/FragmentManager.java
index ee596f9..6d41d45 100644
--- a/v4/java/android/support/v4/app/FragmentManager.java
+++ b/v4/java/android/support/v4/app/FragmentManager.java
@@ -26,14 +26,15 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.annotation.IdRes;
+import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
 import android.support.v4.util.DebugUtils;
 import android.support.v4.util.LogWriter;
+import android.support.v4.view.LayoutInflaterFactory;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.LayoutInflater;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
@@ -401,6 +402,7 @@
  * Callbacks from FragmentManagerImpl to its container.
  */
 interface FragmentContainer {
+    @Nullable
     public View findViewById(@IdRes int id);
     public boolean hasView();
 }
@@ -408,7 +410,7 @@
 /**
  * Container for fragments associated with an activity.
  */
-final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory {
+final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
     static boolean DEBUG = false;
     static final String TAG = "FragmentManager";
     
@@ -2116,7 +2118,7 @@
     }
 
     @Override
-    public View onCreateView(String name, Context context, AttributeSet attrs) {
+    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
         if (!"fragment".equals(name)) {
             return null;
         }
@@ -2136,7 +2138,6 @@
             return null;
         }
 
-        View parent = null; // NOTE: no way to get parent pre-Honeycomb.
         int containerId = parent != null ? parent.getId() : 0;
         if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
             throw new IllegalArgumentException(attrs.getPositionDescription()
@@ -2208,7 +2209,7 @@
         return fragment.mView;
     }
 
-    LayoutInflater.Factory getLayoutInflaterFactory() {
+    LayoutInflaterFactory getLayoutInflaterFactory() {
         return this;
     }
 
diff --git a/v4/java/android/support/v4/app/FragmentStatePagerAdapter.java b/v4/java/android/support/v4/app/FragmentStatePagerAdapter.java
index 0fd1d7f..0c3c6c5 100644
--- a/v4/java/android/support/v4/app/FragmentStatePagerAdapter.java
+++ b/v4/java/android/support/v4/app/FragmentStatePagerAdapter.java
@@ -180,7 +180,7 @@
         }
         for (int i=0; i<mFragments.size(); i++) {
             Fragment f = mFragments.get(i);
-            if (f != null) {
+            if (f != null && f.isAdded()) {
                 if (state == null) {
                     state = new Bundle();
                 }
diff --git a/v4/java/android/support/v4/app/LoaderManager.java b/v4/java/android/support/v4/app/LoaderManager.java
index 57c0a30..e57d01c 100644
--- a/v4/java/android/support/v4/app/LoaderManager.java
+++ b/v4/java/android/support/v4/app/LoaderManager.java
@@ -208,7 +208,8 @@
     
     boolean mCreatingLoader;
 
-    final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> {
+    final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,
+            Loader.OnLoadCanceledListener<Object> {
         final int mId;
         final Bundle mArgs;
         LoaderManager.LoaderCallbacks<Object> mCallbacks;
@@ -260,6 +261,7 @@
                 }
                 if (!mListenerRegistered) {
                     mLoader.registerListener(mId, this);
+                    mLoader.registerOnLoadCanceledListener(this);
                     mListenerRegistered = true;
                 }
                 mLoader.startLoading();
@@ -318,11 +320,21 @@
                     // Let the loader know we're done with it
                     mListenerRegistered = false;
                     mLoader.unregisterListener(this);
+                    mLoader.unregisterOnLoadCanceledListener(this);
                     mLoader.stopLoading();
                 }
             }
         }
-        
+
+        void cancel() {
+            if (DEBUG) Log.v(TAG, "  Canceling: " + this);
+            if (mStarted && mLoader != null && mListenerRegistered) {
+                if (!mLoader.cancelLoad()) {
+                    onLoadCanceled(mLoader);
+                }
+            }
+        }
+
         void destroy() {
             if (DEBUG) Log.v(TAG, "  Destroying: " + this);
             mDestroyed = true;
@@ -350,6 +362,7 @@
                 if (mListenerRegistered) {
                     mListenerRegistered = false;
                     mLoader.unregisterListener(this);
+                    mLoader.unregisterOnLoadCanceledListener(this);
                 }
                 mLoader.reset();
             }
@@ -357,8 +370,38 @@
                 mPendingLoader.destroy();
             }
         }
-        
-        @Override public void onLoadComplete(Loader<Object> loader, Object data) {
+
+        @Override
+        public void onLoadCanceled(Loader<Object> loader) {
+            if (DEBUG) Log.v(TAG, "onLoadCanceled: " + this);
+
+            if (mDestroyed) {
+                if (DEBUG) Log.v(TAG, "  Ignoring load canceled -- destroyed");
+                return;
+            }
+
+            if (mLoaders.get(mId) != this) {
+                // This cancellation message is not coming from the current active loader.
+                // We don't care about it.
+                if (DEBUG) Log.v(TAG, "  Ignoring load canceled -- not active");
+                return;
+            }
+
+            LoaderInfo pending = mPendingLoader;
+            if (pending != null) {
+                // There is a new request pending and we were just
+                // waiting for the old one to cancel or complete before starting
+                // it.  So now it is time, switch over to the new loader.
+                if (DEBUG) Log.v(TAG, "  Switching to pending loader: " + pending);
+                mPendingLoader = null;
+                mLoaders.put(mId, null);
+                destroy();
+                installLoader(pending);
+            }
+        }
+
+        @Override
+        public void onLoadComplete(Loader<Object> loader, Object data) {
             if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
             
             if (mDestroyed) {
@@ -622,7 +665,9 @@
                     } else {
                         // Now we have three active loaders... we'll queue
                         // up this request to be processed once one of the other loaders
-                        // finishes.
+                        // finishes or is canceled.
+                        if (DEBUG) Log.v(TAG, "  Current loader is running; attempting to cancel");
+                        info.cancel();
                         if (info.mPendingLoader != null) {
                             if (DEBUG) Log.v(TAG, "  Removing pending loader: " + info.mPendingLoader);
                             info.mPendingLoader.destroy();
diff --git a/v4/java/android/support/v4/app/NotificationCompat.java b/v4/java/android/support/v4/app/NotificationCompat.java
index e007bcd..5eb3ebb 100644
--- a/v4/java/android/support/v4/app/NotificationCompat.java
+++ b/v4/java/android/support/v4/app/NotificationCompat.java
@@ -26,6 +26,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.support.annotation.ColorInt;
 import android.support.v4.view.GravityCompat;
 import android.view.Gravity;
 import android.widget.RemoteViews;
@@ -342,6 +343,7 @@
      * telling the system not to decorate this notification with any special color but instead use
      * default colors when presenting this notification.
      */
+    @ColorInt
     public static final int COLOR_DEFAULT = Color.TRANSPARENT;
 
     /**
@@ -1171,7 +1173,7 @@
          * rate.  The rate is specified in terms of the number of milliseconds to be on
          * and then the number of milliseconds to be off.
          */
-        public Builder setLights(int argb, int onMs, int offMs) {
+        public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
             mNotification.ledARGB = argb;
             mNotification.ledOnMS = onMs;
             mNotification.ledOffMS = offMs;
@@ -1477,7 +1479,7 @@
          *
          * @return The same Builder.
          */
-        public Builder setColor(int argb) {
+        public Builder setColor(@ColorInt int argb) {
             mColor = argb;
             return this;
         }
@@ -2978,7 +2980,7 @@
          * automotive setting. This method can be used to override the color provided in the
          * notification in such a situation.
          */
-        public CarExtender setColor(int color) {
+        public CarExtender setColor(@ColorInt int color) {
             mColor = color;
             return this;
         }
@@ -2988,6 +2990,7 @@
          *
          * @see setColor
          */
+        @ColorInt
         public int getColor() {
             return mColor;
         }
diff --git a/v4/java/android/support/v4/content/AsyncTaskLoader.java b/v4/java/android/support/v4/content/AsyncTaskLoader.java
index 22fe3a2..17d7416 100644
--- a/v4/java/android/support/v4/content/AsyncTaskLoader.java
+++ b/v4/java/android/support/v4/content/AsyncTaskLoader.java
@@ -19,12 +19,14 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.support.v4.os.OperationCanceledException;
 import android.support.v4.util.TimeUtils;
 import android.util.Log;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 
 /**
  * Static library support version of the framework's {@link android.content.AsyncTaskLoader}.
@@ -38,19 +40,33 @@
     static final boolean DEBUG = false;
 
     final class LoadTask extends ModernAsyncTask<Void, Void, D> implements Runnable {
+        private final CountDownLatch mDone = new CountDownLatch(1);
 
-        D result;
+        // Set to true to indicate that the task has been posted to a handler for
+        // execution at a later time.  Used to throttle updates.
         boolean waiting;
 
-        private CountDownLatch done = new CountDownLatch(1);
-
         /* Runs on a worker thread */
         @Override
         protected D doInBackground(Void... params) {
             if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
-            result = AsyncTaskLoader.this.onLoadInBackground();
-            if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
-            return result;
+            try {
+                D data = AsyncTaskLoader.this.onLoadInBackground();
+                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
+                return data;
+            } catch (OperationCanceledException ex) {
+                if (!isCancelled()) {
+                    // onLoadInBackground threw a canceled exception spuriously.
+                    // This is problematic because it means that the LoaderManager did not
+                    // cancel the Loader itself and still expects to receive a result.
+                    // Additionally, the Loader's own state will not have been updated to
+                    // reflect the fact that the task was being canceled.
+                    // So we treat this case as an unhandled exception.
+                    throw ex;
+                }
+                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground (was canceled)", ex);
+                return null;
+            }
         }
 
         /* Runs on the UI thread */
@@ -60,27 +76,41 @@
             try {
                 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
             } finally {
-                done.countDown();
+                mDone.countDown();
             }
         }
 
+        /* Runs on the UI thread */
         @Override
-        protected void onCancelled() {
+        protected void onCancelled(D data) {
             if (DEBUG) Log.v(TAG, this + " onCancelled");
             try {
-                AsyncTaskLoader.this.dispatchOnCancelled(this, result);
+                AsyncTaskLoader.this.dispatchOnCancelled(this, data);
             } finally {
-                done.countDown();
+                mDone.countDown();
             }
         }
 
+        /* Runs on the UI thread, when the waiting task is posted to a handler.
+         * This method is only executed when task execution was deferred (waiting was true). */
         @Override
         public void run() {
             waiting = false;
             AsyncTaskLoader.this.executePendingTask();
         }
+
+        /* Used for testing purposes to wait for the task to complete. */
+        public void waitForLoader() {
+            try {
+                mDone.await();
+            } catch (InterruptedException e) {
+                // Ignore
+            }
+        }
     }
 
+    private final Executor mExecutor;
+
     volatile LoadTask mTask;
     volatile LoadTask mCancellingTask;
 
@@ -89,12 +119,17 @@
     Handler mHandler;
 
     public AsyncTaskLoader(Context context) {
+        this(context, ModernAsyncTask.THREAD_POOL_EXECUTOR);
+    }
+
+    private AsyncTaskLoader(Context context, Executor executor) {
         super(context);
+        mExecutor = executor;
     }
 
     /**
      * Set amount to throttle updates by.  This is the minimum time from
-     * when the last {@link #onLoadInBackground()} call has completed until
+     * when the last {@link #loadInBackground()} call has completed until
      * a new load is scheduled.
      *
      * @param delayMS Amount of delay, in milliseconds.
@@ -115,24 +150,9 @@
         executePendingTask();
     }
 
-    /**
-     * Attempt to cancel the current load task. See {@link android.os.AsyncTask#cancel(boolean)}
-     * for more info.  Must be called on the main thread of the process.
-     *
-     * <p>Cancelling is not an immediate operation, since the load is performed
-     * in a background thread.  If there is currently a load in progress, this
-     * method requests that the load be cancelled, and notes this is the case;
-     * once the background thread has completed its work its remaining state
-     * will be cleared.  If another load request comes in during this time,
-     * it will be held until the cancelled load is complete.
-     *
-     * @return Returns <tt>false</tt> if the task could not be cancelled,
-     *         typically because it has already completed normally, or
-     *         because {@link #startLoading()} hasn't been called; returns
-     *         <tt>true</tt> otherwise.
-     */
-    public boolean cancelLoad() {
-        if (DEBUG) Log.v(TAG, "cancelLoad: mTask=" + mTask);
+    @Override
+    protected boolean onCancelLoad() {
+        if (DEBUG) Log.v(TAG, "onCancelLoad: mTask=" + mTask);
         if (mTask != null) {
             if (mCancellingTask != null) {
                 // There was a pending task already waiting for a previous
@@ -158,6 +178,7 @@
                 if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
                 if (cancelled) {
                     mCancellingTask = mTask;
+                    cancelLoadInBackground();
                 }
                 mTask = null;
                 return cancelled;
@@ -168,7 +189,10 @@
 
     /**
      * Called if the task was canceled before it was completed.  Gives the class a chance
-     * to properly dispose of the result.
+     * to clean up post-cancellation and to properly dispose of the result.
+     *
+     * @param data The value that was returned by {@link #loadInBackground}, or null
+     * if the task threw {@link OperationCanceledException}.
      */
     public void onCanceled(D data) {
     }
@@ -192,7 +216,7 @@
                 }
             }
             if (DEBUG) Log.v(TAG, "Executing: " + mTask);
-            mTask.executeOnExecutor(ModernAsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+            mTask.executeOnExecutor(mExecutor, (Void[]) null);
         }
     }
 
@@ -203,6 +227,8 @@
             rollbackContentChanged();
             mLastLoadCompleteTime = SystemClock.uptimeMillis();
             mCancellingTask = null;
+            if (DEBUG) Log.v(TAG, "Delivering cancellation");
+            deliverCancellation();
             executePendingTask();
         }
     }
@@ -226,23 +252,76 @@
     }
 
     /**
+     * Called on a worker thread to perform the actual load and to return
+     * the result of the load operation.
+     *
+     * Implementations should not deliver the result directly, but should return them
+     * from this method, which will eventually end up calling {@link #deliverResult} on
+     * the UI thread.  If implementations need to process the results on the UI thread
+     * they may override {@link #deliverResult} and do so there.
+     *
+     * To support cancellation, this method should periodically check the value of
+     * {@link #isLoadInBackgroundCanceled} and terminate when it returns true.
+     * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load
+     * directly instead of polling {@link #isLoadInBackgroundCanceled}.
+     *
+     * When the load is canceled, this method may either return normally or throw
+     * {@link OperationCanceledException}.  In either case, the {@link Loader} will
+     * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
+     * result object, if any.
+     *
+     * @return The result of the load operation.
+     *
+     * @throws OperationCanceledException if the load is canceled during execution.
+     *
+     * @see #isLoadInBackgroundCanceled
+     * @see #cancelLoadInBackground
+     * @see #onCanceled
      */
     public abstract D loadInBackground();
 
     /**
-     * Called on a worker thread to perform the actual load. Implementations should not deliver the
-     * result directly, but should return them from this method, which will eventually end up
-     * calling {@link #deliverResult} on the UI thread. If implementations need to process
-     * the results on the UI thread they may override {@link #deliverResult} and do so
-     * there.
+     * Calls {@link #loadInBackground()}.
      *
-     * @return Implementations must return the result of their load operation.
+     * This method is reserved for use by the loader framework.
+     * Subclasses should override {@link #loadInBackground} instead of this method.
+     *
+     * @return The result of the load operation.
+     *
+     * @throws OperationCanceledException if the load is canceled during execution.
+     *
+     * @see #loadInBackground
      */
     protected D onLoadInBackground() {
         return loadInBackground();
     }
 
     /**
+     * Called on the main thread to abort a load in progress.
+     *
+     * Override this method to abort the current invocation of {@link #loadInBackground}
+     * that is running in the background on a worker thread.
+     *
+     * This method should do nothing if {@link #loadInBackground} has not started
+     * running or if it has already finished.
+     *
+     * @see #loadInBackground
+     */
+    public void cancelLoadInBackground() {
+    }
+
+    /**
+     * Returns true if the current invocation of {@link #loadInBackground} is being canceled.
+     *
+     * @return True if the current invocation of {@link #loadInBackground} is being canceled.
+     *
+     * @see #loadInBackground
+     */
+    public boolean isLoadInBackgroundCanceled() {
+        return mCancellingTask != null;
+    }
+
+    /**
      * Locks the current thread until the loader completes the current load
      * operation. Returns immediately if there is no load operation running.
      * Should not be called from the UI thread: calling it from the UI
@@ -255,11 +334,7 @@
     public void waitForLoader() {
         LoadTask task = mTask;
         if (task != null) {
-            try {
-                task.done.await();
-            } catch (InterruptedException e) {
-                // Ignore
-            }
+            task.waitForLoader();
         }
     }
 
diff --git a/v4/java/android/support/v4/content/ContentResolverCompat.java b/v4/java/android/support/v4/content/ContentResolverCompat.java
new file mode 100644
index 0000000..3b5f01c
--- /dev/null
+++ b/v4/java/android/support/v4/content/ContentResolverCompat.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 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.content;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.support.v4.os.CancellationSignal;
+import android.support.v4.os.OperationCanceledException;
+
+/**
+ * Helper for accessing features in {@link android.content.ContentResolver}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class ContentResolverCompat {
+    interface ContentResolverCompatImpl {
+        Cursor query(ContentResolver resolver,
+                Uri uri, String[] projection, String selection, String[] selectionArgs,
+                String sortOrder, CancellationSignal cancellationSignal);
+    }
+
+    static class ContentResolverCompatImplBase implements ContentResolverCompatImpl {
+        @Override
+        public Cursor query(ContentResolver resolver, Uri uri, String[] projection,
+                String selection, String[] selectionArgs, String sortOrder,
+                CancellationSignal cancellationSignal) {
+            // Note that the cancellation signal cannot cancel the query in progress
+            // prior to Jellybean so we cancel it preemptively here if needed.
+            if (cancellationSignal != null) {
+                cancellationSignal.throwIfCanceled();
+            }
+            return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
+        }
+    }
+
+    static class ContentResolverCompatImplJB extends ContentResolverCompatImplBase {
+        @Override
+        public Cursor query(ContentResolver resolver, Uri uri, String[] projection,
+                String selection, String[] selectionArgs, String sortOrder,
+                CancellationSignal cancellationSignal) {
+            return ContentResolverCompatJellybean.query(resolver,
+                    uri, projection, selection, selectionArgs, sortOrder,
+                    cancellationSignal != null ?
+                            cancellationSignal.getCancellationSignalObject() : null);
+        }
+    }
+
+    private static final ContentResolverCompatImpl IMPL;
+    static {
+        final int version = Build.VERSION.SDK_INT;
+        if (version >= 16) {
+            IMPL = new ContentResolverCompatImplJB();
+        } else {
+            IMPL = new ContentResolverCompatImplBase();
+        }
+    }
+
+    private ContentResolverCompat() {
+        /* Hide constructor */
+    }
+
+    /**
+     * Query the given URI, returning a {@link Cursor} over the result set
+     * with optional support for cancellation.
+     * <p>
+     * For best performance, the caller should follow these guidelines:
+     * <ul>
+     * <li>Provide an explicit projection, to prevent
+     * reading data from storage that aren't going to be used.</li>
+     * <li>Use question mark parameter markers such as 'phone=?' instead of
+     * explicit values in the {@code selection} parameter, so that queries
+     * that differ only by those values will be recognized as the same
+     * for caching purposes.</li>
+     * </ul>
+     * </p>
+     *
+     * @param uri The URI, using the content:// scheme, for the content to
+     *         retrieve.
+     * @param projection A list of which columns to return. Passing null will
+     *         return all columns, which is inefficient.
+     * @param selection A filter declaring which rows to return, formatted as an
+     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
+     *         return all rows for the given URI.
+     * @param selectionArgs You may include ?s in selection, which will be
+     *         replaced by the values from selectionArgs, in the order that they
+     *         appear in the selection. The values will be bound as Strings.
+     * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
+     *         clause (excluding the ORDER BY itself). Passing null will use the
+     *         default sort order, which may be unordered.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
+     * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+     * when the query is executed.
+     * @return A Cursor object, which is positioned before the first entry, or null
+     * @see Cursor
+     */
+    public static Cursor query(ContentResolver resolver,
+            Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder, CancellationSignal cancellationSignal) {
+        return IMPL.query(resolver, uri, projection, selection, selectionArgs,
+                sortOrder, cancellationSignal);
+    }
+}
diff --git a/v4/java/android/support/v4/content/CursorLoader.java b/v4/java/android/support/v4/content/CursorLoader.java
index 980e7d9..503bb9c 100644
--- a/v4/java/android/support/v4/content/CursorLoader.java
+++ b/v4/java/android/support/v4/content/CursorLoader.java
@@ -16,10 +16,12 @@
 
 package android.support.v4.content;
 
+import android.content.ContentResolver;
 import android.content.Context;
-import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
+import android.support.v4.os.CancellationSignal;
+import android.support.v4.os.OperationCanceledException;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -42,18 +44,48 @@
     String mSortOrder;
 
     Cursor mCursor;
+    CancellationSignal mCancellationSignal;
 
     /* Runs on a worker thread */
     @Override
     public Cursor loadInBackground() {
-        Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
-                mSelectionArgs, mSortOrder);
-        if (cursor != null) {
-            // Ensure the cursor window is filled
-            cursor.getCount();
-            cursor.registerContentObserver(mObserver);
+        synchronized (this) {
+            if (isLoadInBackgroundCanceled()) {
+                throw new OperationCanceledException();
+            }
+            mCancellationSignal = new CancellationSignal();
         }
-        return cursor;
+        try {
+            Cursor cursor = ContentResolverCompat.query(getContext().getContentResolver(),
+                    mUri, mProjection, mSelection, mSelectionArgs, mSortOrder,
+                    mCancellationSignal);
+            if (cursor != null) {
+                try {
+                    // Ensure the cursor window is filled.
+                    cursor.getCount();
+                    cursor.registerContentObserver(mObserver);
+                } catch (RuntimeException ex) {
+                    cursor.close();
+                    throw ex;
+                }
+            }
+            return cursor;
+        } finally {
+            synchronized (this) {
+                mCancellationSignal = null;
+            }
+        }
+    }
+
+    @Override
+    public void cancelLoadInBackground() {
+        super.cancelLoadInBackground();
+
+        synchronized (this) {
+            if (mCancellationSignal != null) {
+                mCancellationSignal.cancel();
+            }
+        }
     }
 
     /* Runs on the UI thread */
@@ -90,7 +122,7 @@
 
     /**
      * Creates a fully-specified CursorLoader.  See
-     * {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)
+     * {@link ContentResolver#query(Uri, String[], String, String[], String)
      * ContentResolver.query()} for documentation on the meaning of the
      * parameters.  These will be passed as-is to that call.
      */
diff --git a/v4/java/android/support/v4/content/Loader.java b/v4/java/android/support/v4/content/Loader.java
index bd01b0a..cf6cc99 100644
--- a/v4/java/android/support/v4/content/Loader.java
+++ b/v4/java/android/support/v4/content/Loader.java
@@ -34,6 +34,7 @@
 public class Loader<D> {
     int mId;
     OnLoadCompleteListener<D> mListener;
+    OnLoadCanceledListener<D> mOnLoadCanceledListener;
     Context mContext;
     boolean mStarted = false;
     boolean mAbandoned = false;
@@ -45,8 +46,8 @@
      * An implementation of a ContentObserver that takes care of connecting
      * it to the Loader to have the loader re-load its data when the observer
      * is told it has changed.  You do not normally need to use this yourself;
-     * it is used for you by {@link android.support.v4.content.CursorLoader}
-     * to take care of executing an update when the cursor's backing data changes.
+     * it is used for you by {@link CursorLoader} to take care of executing
+     * an update when the cursor's backing data changes.
      */
     public final class ForceLoadContentObserver extends ContentObserver {
         public ForceLoadContentObserver() {
@@ -83,8 +84,29 @@
     }
 
     /**
-     * Stores away the application context associated with context. Since Loaders can be used
-     * across multiple activities it's dangerous to store the context directly.
+     * Interface that is implemented to discover when a Loader has been canceled
+     * before it finished loading its data.  You do not normally need to implement
+     * this yourself; it is used in the implementation of {@link android.support.v4.app.LoaderManager}
+     * to find out when a Loader it is managing has been canceled so that it
+     * can schedule the next Loader.  This interface should only be used if a
+     * Loader is not being used in conjunction with LoaderManager.
+     */
+    public interface OnLoadCanceledListener<D> {
+        /**
+         * Called on the thread that created the Loader when the load is canceled.
+         *
+         * @param loader the loader that canceled the load
+         */
+        public void onLoadCanceled(Loader<D> loader);
+    }
+
+    /**
+     * Stores away the application context associated with context.
+     * Since Loaders can be used across multiple activities it's dangerous to
+     * store the context directly; always use {@link #getContext()} to retrieve
+     * the Loader's Context, don't use the constructor argument directly.
+     * The Context returned by {@link #getContext} is safe to use across
+     * Activity instances.
      *
      * @param context used to retrieve the application context.
      */
@@ -106,6 +128,18 @@
     }
 
     /**
+     * Informs the registered {@link OnLoadCanceledListener} that the load has been canceled.
+     * Should only be called by subclasses.
+     *
+     * Must be called from the process's main thread.
+     */
+    public void deliverCancellation() {
+        if (mOnLoadCanceledListener != null) {
+            mOnLoadCanceledListener.onLoadCanceled(this);
+        }
+    }
+
+    /**
      * @return an application context retrieved from the Context passed to the constructor.
      */
     public Context getContext() {
@@ -150,6 +184,40 @@
     }
 
     /**
+     * Registers a listener that will receive callbacks when a load is canceled.
+     * The callback will be called on the process's main thread so it's safe to
+     * pass the results to widgets.
+     *
+     * Must be called from the process's main thread.
+     *
+     * @param listener The listener to register.
+     */
+    public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {
+        if (mOnLoadCanceledListener != null) {
+            throw new IllegalStateException("There is already a listener registered");
+        }
+        mOnLoadCanceledListener = listener;
+    }
+
+    /**
+     * Unregisters a listener that was previously added with
+     * {@link #registerOnLoadCanceledListener}.
+     *
+     * Must be called from the process's main thread.
+     *
+     * @param listener The listener to unregister.
+     */
+    public void unregisterOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {
+        if (mOnLoadCanceledListener == null) {
+            throw new IllegalStateException("No listener register");
+        }
+        if (mOnLoadCanceledListener != listener) {
+            throw new IllegalArgumentException("Attempting to unregister the wrong listener");
+        }
+        mOnLoadCanceledListener = null;
+    }
+
+    /**
      * Return whether this load has been started.  That is, its {@link #startLoading()}
      * has been called and no calls to {@link #stopLoading()} or
      * {@link #reset()} have yet been made.
@@ -177,6 +245,12 @@
     }
 
     /**
+     * This function will normally be called for you automatically by
+     * {@link android.support.v4.app.LoaderManager} when the associated fragment/activity
+     * is being started.  When using a Loader with {@link android.support.v4.app.LoaderManager},
+     * you <em>must not</em> call this method yourself, or you will conflict
+     * with its management of the Loader.
+     *
      * Starts an asynchronous load of the Loader's data. When the result
      * is ready the callbacks will be called on the process's main thread.
      * If a previous load has been completed and is still valid
@@ -207,6 +281,43 @@
     }
 
     /**
+     * Attempt to cancel the current load task.
+     * Must be called on the main thread of the process.
+     *
+     * <p>Cancellation is not an immediate operation, since the load is performed
+     * in a background thread.  If there is currently a load in progress, this
+     * method requests that the load be canceled, and notes this is the case;
+     * once the background thread has completed its work its remaining state
+     * will be cleared.  If another load request comes in during this time,
+     * it will be held until the canceled load is complete.
+     *
+     * @return Returns <tt>false</tt> if the task could not be canceled,
+     * typically because it has already completed normally, or
+     * because {@link #startLoading()} hasn't been called; returns
+     * <tt>true</tt> otherwise.  When <tt>true</tt> is returned, the task
+     * is still running and the {@link OnLoadCanceledListener} will be called
+     * when the task completes.
+     */
+    public boolean cancelLoad() {
+        return onCancelLoad();
+    }
+
+    /**
+     * Subclasses must implement this to take care of requests to {@link #cancelLoad()}.
+     * This will always be called from the process's main thread.
+     *
+     * @return Returns <tt>false</tt> if the task could not be canceled,
+     * typically because it has already completed normally, or
+     * because {@link #startLoading()} hasn't been called; returns
+     * <tt>true</tt> otherwise.  When <tt>true</tt> is returned, the task
+     * is still running and the {@link OnLoadCanceledListener} will be called
+     * when the task completes.
+     */
+    protected boolean onCancelLoad() {
+        return false;
+    }
+
+    /**
      * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
      * loaded data set and load a new one.  This simply calls through to the
      * implementation's {@link #onForceLoad()}.  You generally should only call this
@@ -226,7 +337,13 @@
     }
 
     /**
-     * Stops delivery of updates until the next time {@link #startLoading()} is called.
+     * This function will normally be called for you automatically by
+     * {@link android.support.v4.app.LoaderManager} when the associated fragment/activity
+     * is being stopped.  When using a Loader with {@link android.support.v4.app.LoaderManager},
+     * you <em>must not</em> call this method yourself, or you will conflict
+     * with its management of the Loader.
+     *
+     * <p>Stops delivery of updates until the next time {@link #startLoading()} is called.
      * Implementations should <em>not</em> invalidate their data at this point --
      * clients are still free to use the last data the loader reported.  They will,
      * however, typically stop reporting new data if the data changes; they can
@@ -254,6 +371,12 @@
     }
 
     /**
+     * This function will normally be called for you automatically by
+     * {@link android.support.v4.app.LoaderManager} when restarting a Loader.  When using
+     * a Loader with {@link android.support.v4.app.LoaderManager},
+     * you <em>must not</em> call this method yourself, or you will conflict
+     * with its management of the Loader.
+     *
      * Tell the Loader that it is being abandoned.  This is called prior
      * to {@link #reset} to have it retain its current data but not report
      * any new data.
@@ -272,10 +395,16 @@
      * {@link #onReset()} happens.  You can retrieve the current abandoned
      * state with {@link #isAbandoned}.
      */
-    protected void onAbandon() {        
+    protected void onAbandon() {
     }
     
     /**
+     * This function will normally be called for you automatically by
+     * {@link android.support.v4.app.LoaderManager} when destroying a Loader.  When using
+     * a Loader with {@link android.support.v4.app.LoaderManager},
+     * you <em>must not</em> call this method yourself, or you will conflict
+     * with its management of the Loader.
+     *
      * Resets the state of the Loader.  The Loader should at this point free
      * all of its resources, since it may never be called again; however, its
      * {@link #startLoading()} may later be called at which point it must be
diff --git a/v4/java/android/support/v4/content/ModernAsyncTask.java b/v4/java/android/support/v4/content/ModernAsyncTask.java
index 43b17f4..503f1a2 100644
--- a/v4/java/android/support/v4/content/ModernAsyncTask.java
+++ b/v4/java/android/support/v4/content/ModernAsyncTask.java
@@ -134,13 +134,13 @@
                 } catch (InterruptedException e) {
                     android.util.Log.w(LOG_TAG, e);
                 } catch (ExecutionException e) {
-                    throw new RuntimeException("An error occured while executing doInBackground()",
-                            e.getCause());
+                    throw new RuntimeException(
+                            "An error occurred while executing doInBackground()", e.getCause());
                 } catch (CancellationException e) {
                     postResultIfNotInvoked(null);
                 } catch (Throwable t) {
-                    throw new RuntimeException("An error occured while executing "
-                            + "doInBackground()", t);
+                    throw new RuntimeException(
+                            "An error occurred while executing doInBackground()", t);
                 }
             }
         };
diff --git a/v4/java/android/support/v4/graphics/ColorUtils.java b/v4/java/android/support/v4/graphics/ColorUtils.java
new file mode 100644
index 0000000..f2d56da
--- /dev/null
+++ b/v4/java/android/support/v4/graphics/ColorUtils.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 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.graphics;
+
+import android.graphics.Color;
+
+/**
+ * A set of color-related utility methods, building upon those available in {@code Color}.
+ */
+public class ColorUtils {
+
+    private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10;
+    private static final int MIN_ALPHA_SEARCH_PRECISION = 10;
+
+    private ColorUtils() {}
+
+    /**
+     * Composite two potentially translucent colors over each other and returns the result.
+     */
+    public static int compositeColors(int foreground, int background) {
+        int bgAlpha = Color.alpha(background);
+        int fgAlpha = Color.alpha(foreground);
+        int a = compositeAlpha(fgAlpha, bgAlpha);
+
+        int r = compositeComponent(Color.red(foreground), fgAlpha,
+                Color.red(background), bgAlpha, a);
+        int g = compositeComponent(Color.green(foreground), fgAlpha,
+                Color.green(background), bgAlpha, a);
+        int b = compositeComponent(Color.blue(foreground), fgAlpha,
+                Color.blue(background), bgAlpha, a);
+
+        return Color.argb(a, r, g, b);
+    }
+
+    private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
+        return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
+    }
+
+    private static int compositeComponent(int fgC, int fgA, int bgC, int bgA, int a) {
+        if (a == 0) return 0;
+        return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (a * 0xFF);
+    }
+
+    /**
+     * Returns the luminance of a color.
+     *
+     * Formula defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
+     */
+    public static double calculateLuminance(int color) {
+        double red = Color.red(color) / 255d;
+        red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4);
+
+        double green = Color.green(color) / 255d;
+        green = green < 0.03928 ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4);
+
+        double blue = Color.blue(color) / 255d;
+        blue = blue < 0.03928 ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4);
+
+        return (0.2126 * red) + (0.7152 * green) + (0.0722 * blue);
+    }
+
+    /**
+     * Returns the contrast ratio between {@code foreground} and {@code background}.
+     * {@code background} must be opaque.
+     * <p>
+     * Formula defined
+     * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>.
+     */
+    public static double calculateContrast(int foreground, int background) {
+        if (Color.alpha(background) != 255) {
+            throw new IllegalArgumentException("background can not be translucent");
+        }
+        if (Color.alpha(foreground) < 255) {
+            // If the foreground is translucent, composite the foreground over the background
+            foreground = compositeColors(foreground, background);
+        }
+
+        final double luminance1 = calculateLuminance(foreground) + 0.05;
+        final double luminance2 = calculateLuminance(background) + 0.05;
+
+        // Now return the lighter luminance divided by the darker luminance
+        return Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2);
+    }
+
+    /**
+     * Calculates the minimum alpha value which can be applied to {@code foreground} so that would
+     * have a contrast value of at least {@code minContrastRatio} when compared to
+     * {@code background}.
+     *
+     * @param foreground       the foreground color.
+     * @param background       the background color. Should be opaque.
+     * @param minContrastRatio the minimum contrast ratio.
+     * @return the alpha value in the range 0-255, or -1 if no value could be calculated.
+     */
+    public static int calculateMinimumAlpha(int foreground, int background,
+            float minContrastRatio) {
+        if (Color.alpha(background) != 255) {
+            throw new IllegalArgumentException("background can not be translucent");
+        }
+
+        // First lets check that a fully opaque foreground has sufficient contrast
+        int testForeground = setAlphaComponent(foreground, 255);
+        double testRatio = calculateContrast(testForeground, background);
+        if (testRatio < minContrastRatio) {
+            // Fully opaque foreground does not have sufficient contrast, return error
+            return -1;
+        }
+
+        // Binary search to find a value with the minimum value which provides sufficient contrast
+        int numIterations = 0;
+        int minAlpha = 0;
+        int maxAlpha = 255;
+
+        while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS &&
+                (maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) {
+            final int testAlpha = (minAlpha + maxAlpha) / 2;
+
+            testForeground = setAlphaComponent(foreground, testAlpha);
+            testRatio = calculateContrast(testForeground, background);
+
+            if (testRatio < minContrastRatio) {
+                minAlpha = testAlpha;
+            } else {
+                maxAlpha = testAlpha;
+            }
+
+            numIterations++;
+        }
+
+        // Conservatively return the max of the range of possible alphas, which is known to pass.
+        return maxAlpha;
+    }
+
+    /**
+     * Convert RGB components to HSL (hue-saturation-lightness).
+     * <ul>
+     * <li>hsl[0] is Hue [0 .. 360)</li>
+     * <li>hsl[1] is Saturation [0...1]</li>
+     * <li>hsl[2] is Lightness [0...1]</li>
+     * </ul>
+     *
+     * @param r   red component value [0..255]
+     * @param g   green component value [0..255]
+     * @param b   blue component value [0..255]
+     * @param hsl 3 element array which holds the resulting HSL components.
+     */
+    public static void RGBToHSL(int r, int g, int b, float[] hsl) {
+        final float rf = r / 255f;
+        final float gf = g / 255f;
+        final float bf = b / 255f;
+
+        final float max = Math.max(rf, Math.max(gf, bf));
+        final float min = Math.min(rf, Math.min(gf, bf));
+        final float deltaMaxMin = max - min;
+
+        float h, s;
+        float l = (max + min) / 2f;
+
+        if (max == min) {
+            // Monochromatic
+            h = s = 0f;
+        } else {
+            if (max == rf) {
+                h = ((gf - bf) / deltaMaxMin) % 6f;
+            } else if (max == gf) {
+                h = ((bf - rf) / deltaMaxMin) + 2f;
+            } else {
+                h = ((rf - gf) / deltaMaxMin) + 4f;
+            }
+
+            s = deltaMaxMin / (1f - Math.abs(2f * l - 1f));
+        }
+
+        hsl[0] = (h * 60f) % 360f;
+        hsl[1] = s;
+        hsl[2] = l;
+    }
+
+    /**
+     * Convert the ARGB color to its HSL (hue-saturation-lightness) components.
+     * <ul>
+     * <li>hsl[0] is Hue [0 .. 360)</li>
+     * <li>hsl[1] is Saturation [0...1]</li>
+     * <li>hsl[2] is Lightness [0...1]</li>
+     * </ul>
+     *
+     * @param color the ARGB color to convert. The alpha component is ignored.
+     * @param hsl 3 element array which holds the resulting HSL components.
+     */
+    public static void colorToHSL(int color, float[] hsl) {
+        RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hsl);
+    }
+
+    /**
+     * Convert HSL (hue-saturation-lightness) components to a RGB color.
+     * <ul>
+     * <li>hsl[0] is Hue [0 .. 360)</li>
+     * <li>hsl[1] is Saturation [0...1]</li>
+     * <li>hsl[2] is Lightness [0...1]</li>
+     * </ul>
+     * If hsv values are out of range, they are pinned.
+     *
+     * @param hsl 3 element array which holds the input HSL components.
+     * @return the resulting RGB color
+     */
+    public static int HSLToColor(float[] hsl) {
+        final float h = hsl[0];
+        final float s = hsl[1];
+        final float l = hsl[2];
+
+        final float c = (1f - Math.abs(2 * l - 1f)) * s;
+        final float m = l - 0.5f * c;
+        final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f));
+
+        final int hueSegment = (int) h / 60;
+
+        int r = 0, g = 0, b = 0;
+
+        switch (hueSegment) {
+            case 0:
+                r = Math.round(255 * (c + m));
+                g = Math.round(255 * (x + m));
+                b = Math.round(255 * m);
+                break;
+            case 1:
+                r = Math.round(255 * (x + m));
+                g = Math.round(255 * (c + m));
+                b = Math.round(255 * m);
+                break;
+            case 2:
+                r = Math.round(255 * m);
+                g = Math.round(255 * (c + m));
+                b = Math.round(255 * (x + m));
+                break;
+            case 3:
+                r = Math.round(255 * m);
+                g = Math.round(255 * (x + m));
+                b = Math.round(255 * (c + m));
+                break;
+            case 4:
+                r = Math.round(255 * (x + m));
+                g = Math.round(255 * m);
+                b = Math.round(255 * (c + m));
+                break;
+            case 5:
+            case 6:
+                r = Math.round(255 * (c + m));
+                g = Math.round(255 * m);
+                b = Math.round(255 * (x + m));
+                break;
+        }
+
+        r = Math.max(0, Math.min(255, r));
+        g = Math.max(0, Math.min(255, g));
+        b = Math.max(0, Math.min(255, b));
+
+        return Color.rgb(r, g, b);
+    }
+
+    /**
+     * Set the alpha component of {@code color} to be {@code alpha}.
+     */
+    public static int setAlphaComponent(int color, int alpha) {
+        if (alpha < 0 || alpha > 255) {
+            throw new IllegalArgumentException("alpha must be between 0 and 255.");
+        }
+        return (color & 0x00ffffff) | (alpha << 24);
+    }
+
+}
diff --git a/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java b/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java
index c6f0d2a..99c762f 100644
--- a/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java
+++ b/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java
@@ -19,7 +19,6 @@
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 
 /**
  * Helper for accessing features in {@link android.graphics.drawable.Drawable}
@@ -38,6 +37,7 @@
         void setTint(Drawable drawable, int tint);
         void setTintList(Drawable drawable, ColorStateList tint);
         void setTintMode(Drawable drawable, PorterDuff.Mode tintMode);
+        Drawable wrap(Drawable drawable);
     }
 
     /**
@@ -67,14 +67,22 @@
 
         @Override
         public void setTint(Drawable drawable, int tint) {
+            DrawableCompatBase.setTint(drawable, tint);
         }
 
         @Override
         public void setTintList(Drawable drawable, ColorStateList tint) {
+            DrawableCompatBase.setTintList(drawable, tint);
         }
 
         @Override
         public void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
+            DrawableCompatBase.setTintMode(drawable, tintMode);
+        }
+
+        @Override
+        public Drawable wrap(Drawable drawable) {
+            return DrawableCompatBase.wrapForTinting(drawable);
         }
     }
 
@@ -86,6 +94,11 @@
         public void jumpToCurrentState(Drawable drawable) {
             DrawableCompatHoneycomb.jumpToCurrentState(drawable);
         }
+
+        @Override
+        public Drawable wrap(Drawable drawable) {
+            return DrawableCompatHoneycomb.wrapForTinting(drawable);
+        }
     }
 
     /**
@@ -101,35 +114,55 @@
         public boolean isAutoMirrored(Drawable drawable) {
             return DrawableCompatKitKat.isAutoMirrored(drawable);
         }
+
+        @Override
+        public Drawable wrap(Drawable drawable) {
+            return DrawableCompatKitKat.wrapForTinting(drawable);
+        }
     }
 
     /**
      * Interface implementation for devices with at least L APIs.
      */
-    static class LDrawableImpl extends KitKatDrawableImpl {
+    static class LollipopDrawableImpl extends KitKatDrawableImpl {
         @Override
         public void setHotspot(Drawable drawable, float x, float y) {
-            DrawableCompatL.setHotspot(drawable, x, y);
+            DrawableCompatLollipop.setHotspot(drawable, x, y);
         }
 
         @Override
         public void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom) {
-            DrawableCompatL.setHotspotBounds(drawable, left, top, right, bottom);
+            DrawableCompatLollipop.setHotspotBounds(drawable, left, top, right, bottom);
         }
 
         @Override
         public void setTint(Drawable drawable, int tint) {
-            DrawableCompatL.setTint(drawable, tint);
+            DrawableCompatLollipop.setTint(drawable, tint);
         }
 
         @Override
         public void setTintList(Drawable drawable, ColorStateList tint) {
-            DrawableCompatL.setTintList(drawable, tint);
+            DrawableCompatLollipop.setTintList(drawable, tint);
         }
 
         @Override
         public void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
-            DrawableCompatL.setTintMode(drawable, tintMode);
+            DrawableCompatLollipop.setTintMode(drawable, tintMode);
+        }
+
+        @Override
+        public Drawable wrap(Drawable drawable) {
+            return DrawableCompatLollipop.wrapForTinting(drawable);
+        }
+    }
+
+    /**
+     * Interface implementation for devices with at least L APIs.
+     */
+    static class LollipopMr1DrawableImpl extends LollipopDrawableImpl {
+        @Override
+        public Drawable wrap(Drawable drawable) {
+            return DrawableCompatApi22.wrapForTinting(drawable);
         }
     }
 
@@ -139,8 +172,10 @@
     static final DrawableImpl IMPL;
     static {
         final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 21) {
-            IMPL = new LDrawableImpl();
+        if (version >= 22) {
+            IMPL = new LollipopMr1DrawableImpl();
+        } else if (version >= 21) {
+            IMPL = new LollipopDrawableImpl();
         } else if (version >= 19) {
             IMPL = new KitKatDrawableImpl();
         } else if (version >= 11) {
@@ -219,7 +254,7 @@
      * Specifies a tint for {@code drawable}.
      *
      * @param drawable The Drawable against which to invoke the method.
-     * @param tint Color to use for tinting this drawable
+     * @param tint     Color to use for tinting this drawable
      */
     public static void setTint(Drawable drawable, int tint) {
         IMPL.setTint(drawable, tint);
@@ -229,8 +264,7 @@
      * Specifies a tint for {@code drawable} as a color state list.
      *
      * @param drawable The Drawable against which to invoke the method.
-     * @param tint Color state list to use for tinting this drawable, or null to
-     *            clear the tint
+     * @param tint     Color state list to use for tinting this drawable, or null to clear the tint
      */
     public static void setTintList(Drawable drawable, ColorStateList tint) {
         IMPL.setTintList(drawable, tint);
@@ -240,11 +274,45 @@
      * Specifies a tint blending mode for {@code drawable}.
      *
      * @param drawable The Drawable against which to invoke the method.
-     * @param tintMode Color state list to use for tinting this drawable, or null to
-     *            clear the tint
      * @param tintMode A Porter-Duff blending mode
      */
     public static void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
         IMPL.setTintMode(drawable, tintMode);
     }
+
+    /**
+     * Potentially wrap {@code drawable} so that it may be used for tinting across the
+     * different API levels, via the tinting methods in this class.
+     * <p>
+     * If you need to get hold of the original {@link android.graphics.drawable.Drawable} again,
+     * you can use the value returned from {@link #unwrap(Drawable)}.
+     *
+     * @param drawable The Drawable to process
+     * @return A drawable capable of being tinted across all API levels.
+     *
+     * @see #setTint(Drawable, int)
+     * @see #setTintList(Drawable, ColorStateList)
+     * @see #setTintMode(Drawable, PorterDuff.Mode)
+     * @see #unwrap(Drawable)
+     */
+    public static Drawable wrap(Drawable drawable) {
+        return IMPL.wrap(drawable);
+    }
+
+    /**
+     * Unwrap {@code drawable} if it is the result of a call to {@link #wrap(Drawable)}. If
+     * the {@code drawable} is not the result of a call to {@link #wrap(Drawable)} then
+     * {@code drawable} is returned as-is.
+     *
+     * @param drawable The drawable to unwrap
+     * @return the unwrapped {@link Drawable} or {@code drawable} if it hasn't been wrapped.
+     *
+     * @see #wrap(Drawable)
+     */
+    public static <T extends Drawable> T unwrap(Drawable drawable) {
+        if (drawable instanceof DrawableWrapper) {
+            return (T) ((DrawableWrapper) drawable).getWrappedDrawable();
+        }
+        return (T) drawable;
+    }
 }
diff --git a/v4/java/android/support/v4/internal/view/SupportMenuItem.java b/v4/java/android/support/v4/internal/view/SupportMenuItem.java
index 0a1bccb..e739ec8 100644
--- a/v4/java/android/support/v4/internal/view/SupportMenuItem.java
+++ b/v4/java/android/support/v4/internal/view/SupportMenuItem.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.internal.view;
 
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
 import android.support.v4.view.ActionProvider;
 import android.support.v4.view.MenuItemCompat;
 import android.view.MenuItem;
@@ -201,4 +203,26 @@
      * @return This menu item instance for call chaining
      */
     public SupportMenuItem setSupportOnActionExpandListener(MenuItemCompat.OnActionExpandListener listener);
+
+    /**
+     * Applies a tint to the icon drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to {@link android.view.MenuItem#setIcon(android.graphics.drawable.Drawable)}
+     * will automatically mutate the drawable and apply the specified tint and tint mode.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @return This menu item instance for call chaining
+     */
+    public MenuItem setIconTintList(ColorStateList tint);
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by {@link
+     * #setIconTintList(ColorStateList)} to the icon drawable. The default mode is {@link
+     * PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be {@code null} to clear tint
+     * @return This menu item instance for call chaining
+     */
+    public MenuItem setIconTintMode(PorterDuff.Mode tintMode);
 }
\ No newline at end of file
diff --git a/v4/java/android/support/v4/os/CancellationSignal.java b/v4/java/android/support/v4/os/CancellationSignal.java
new file mode 100644
index 0000000..41bdfe6
--- /dev/null
+++ b/v4/java/android/support/v4/os/CancellationSignal.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+import android.os.Build;
+
+/**
+ * Static library support version of the framework's {@link android.os.CancellationSignal}.
+ * Used to write apps that run on platforms prior to Android 4.1.  See the framework SDK
+ * documentation for a class overview.
+ */
+public final class CancellationSignal {
+    private boolean mIsCanceled;
+    private OnCancelListener mOnCancelListener;
+    private Object mCancellationSignalObj;
+    private boolean mCancelInProgress;
+
+    /**
+     * Creates a cancellation signal, initially not canceled.
+     */
+    public CancellationSignal() {
+    }
+
+    /**
+     * Returns true if the operation has been canceled.
+     *
+     * @return True if the operation has been canceled.
+     */
+    public boolean isCanceled() {
+        synchronized (this) {
+            return mIsCanceled;
+        }
+    }
+
+    /**
+     * Throws {@link OperationCanceledException} if the operation has been canceled.
+     *
+     * @throws OperationCanceledException if the operation has been canceled.
+     */
+    public void throwIfCanceled() {
+        if (isCanceled()) {
+            throw new OperationCanceledException();
+        }
+    }
+
+    /**
+     * Cancels the operation and signals the cancellation listener.
+     * If the operation has not yet started, then it will be canceled as soon as it does.
+     */
+    public void cancel() {
+        final OnCancelListener listener;
+        final Object obj;
+        synchronized (this) {
+            if (mIsCanceled) {
+                return;
+            }
+            mIsCanceled = true;
+            mCancelInProgress = true;
+            listener = mOnCancelListener;
+            obj = mCancellationSignalObj;
+        }
+
+        try {
+            if (listener != null) {
+                listener.onCancel();
+            }
+            if (obj != null) {
+                CancellationSignalCompatJellybean.cancel(obj);
+            }
+        } finally {
+            synchronized (this) {
+                mCancelInProgress = false;
+                notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Sets the cancellation listener to be called when canceled.
+     *
+     * This method is intended to be used by the recipient of a cancellation signal
+     * such as a database or a content provider to handle cancellation requests
+     * while performing a long-running operation.  This method is not intended to be
+     * used by applications themselves.
+     *
+     * If {@link CancellationSignal#cancel} has already been called, then the provided
+     * listener is invoked immediately.
+     *
+     * This method is guaranteed that the listener will not be called after it
+     * has been removed.
+     *
+     * @param listener The cancellation listener, or null to remove the current listener.
+     */
+    public void setOnCancelListener(OnCancelListener listener) {
+        synchronized (this) {
+            waitForCancelFinishedLocked();
+
+            if (mOnCancelListener == listener) {
+                return;
+            }
+            mOnCancelListener = listener;
+            if (!mIsCanceled || listener == null) {
+                return;
+            }
+        }
+        listener.onCancel();
+    }
+
+    /**
+     * Gets the framework {@link android.os.CancellationSignal} associated with this object.
+     * <p>
+     * Framework support for cancellation signals was added in
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} so this method will always
+     * return null on older versions of the platform.
+     * </p>
+     *
+     * @return A framework cancellation signal object, or null on platform versions
+     * prior to Jellybean.
+     */
+    public Object getCancellationSignalObject() {
+        if (Build.VERSION.SDK_INT < 16) {
+            return null;
+        }
+        synchronized (this) {
+            if (mCancellationSignalObj == null) {
+                mCancellationSignalObj = CancellationSignalCompatJellybean.create();
+                if (mIsCanceled) {
+                    CancellationSignalCompatJellybean.cancel(mCancellationSignalObj);
+                }
+            }
+            return mCancellationSignalObj;
+        }
+    }
+
+    private void waitForCancelFinishedLocked() {
+        while (mCancelInProgress) {
+            try {
+                wait();
+            } catch (InterruptedException ex) {
+            }
+        }
+    }
+
+     /**
+     * Listens for cancellation.
+     */
+    public interface OnCancelListener {
+        /**
+         * Called when {@link CancellationSignal#cancel} is invoked.
+         */
+        void onCancel();
+    }
+}
diff --git a/v4/java/android/support/v4/os/OperationCanceledException.java b/v4/java/android/support/v4/os/OperationCanceledException.java
new file mode 100644
index 0000000..9b28030
--- /dev/null
+++ b/v4/java/android/support/v4/os/OperationCanceledException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 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.os;
+
+
+/**
+ * An exception type that is thrown when an operation in progress is canceled.
+ */
+public class OperationCanceledException extends RuntimeException {
+    public OperationCanceledException() {
+        this(null);
+    }
+
+    public OperationCanceledException(String message) {
+        super(message != null ? message : "The operation has been canceled.");
+    }
+}
diff --git a/v4/java/android/support/v4/os/ParcelableCompat.java b/v4/java/android/support/v4/os/ParcelableCompat.java
index 40ad994..fbb064f 100644
--- a/v4/java/android/support/v4/os/ParcelableCompat.java
+++ b/v4/java/android/support/v4/os/ParcelableCompat.java
@@ -34,7 +34,7 @@
     public static <T> Parcelable.Creator<T> newCreator(
             ParcelableCompatCreatorCallbacks<T> callbacks) {
         if (android.os.Build.VERSION.SDK_INT >= 13) {
-            ParcelableCompatCreatorHoneycombMR2Stub.instantiate(callbacks);
+            return ParcelableCompatCreatorHoneycombMR2Stub.instantiate(callbacks);
         }
         return new CompatCreator<T>(callbacks);
     }
diff --git a/v4/java/android/support/v4/text/ICUCompat.java b/v4/java/android/support/v4/text/ICUCompat.java
index 6df35ff..43ada52 100644
--- a/v4/java/android/support/v4/text/ICUCompat.java
+++ b/v4/java/android/support/v4/text/ICUCompat.java
@@ -18,34 +18,32 @@
 
 import android.os.Build;
 
+import java.util.Locale;
+
 public class ICUCompat {
 
     interface ICUCompatImpl {
-        public String getScript(String locale);
-        public String addLikelySubtags(String locale);
+        public String maximizeAndGetScript(Locale locale);
     }
 
     static class ICUCompatImplBase implements ICUCompatImpl {
         @Override
-        public String getScript(String locale) {
+        public String maximizeAndGetScript(Locale locale) {
             return null;
         }
-
-        @Override
-        public String addLikelySubtags(String locale) {
-            return locale;
-        }
     }
 
     static class ICUCompatImplIcs implements ICUCompatImpl {
         @Override
-        public String getScript(String locale) {
-            return ICUCompatIcs.getScript(locale);
+        public String maximizeAndGetScript(Locale locale) {
+            return ICUCompatIcs.maximizeAndGetScript(locale);
         }
+    }
 
+    static class ICUCompatImplLollipop implements ICUCompatImpl {
         @Override
-        public String addLikelySubtags(String locale) {
-            return ICUCompatIcs.addLikelySubtags(locale);
+        public String maximizeAndGetScript(Locale locale) {
+            return ICUCompatApi23.maximizeAndGetScript(locale);
         }
     }
 
@@ -53,7 +51,9 @@
 
     static {
         final int version = Build.VERSION.SDK_INT;
-        if (version >= 14) {
+        if (version >= 21) {
+            IMPL = new ICUCompatImplLollipop();
+        } else if (version >= 14) {
             IMPL = new ICUCompatImplIcs();
         } else {
             IMPL = new ICUCompatImplBase();
@@ -61,18 +61,11 @@
     }
 
     /**
-     * Returns the script (language code) of a script.
+     * Returns the script for a given Locale.
      *
-     * @param locale The locale.
-     * @return a String representing the script (language code) of the locale.
-     */
-    public static String getScript(String locale) {
-        return IMPL.getScript(locale);
-    }
-
-    /**
-     * Add the likely subtags for a provided locale ID, per the algorithm described in the following
-     * CLDR technical report:
+     * If the locale isn't already in its maximal form, likely subtags for the provided locale
+     * ID are added before we determine the script. For further details, see the following CLDR
+     * technical report :
      *
      * http://www.unicode.org/reports/tr35/#Likely_Subtags
      *
@@ -87,12 +80,10 @@
      * "sr" maximizes to "sr_Cyrl_RS"
      * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
      * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
-
-     * @param locale The locale to maximize
      *
-     * @return the maximized locale
+     * @return
      */
-    public static String addLikelySubtags(String locale) {
-        return IMPL.addLikelySubtags(locale);
+    public static String maximizeAndGetScript(Locale locale) {
+        return IMPL.maximizeAndGetScript(locale);
     }
 }
diff --git a/v4/java/android/support/v4/text/TextUtilsCompat.java b/v4/java/android/support/v4/text/TextUtilsCompat.java
index 436d72f..507a006 100644
--- a/v4/java/android/support/v4/text/TextUtilsCompat.java
+++ b/v4/java/android/support/v4/text/TextUtilsCompat.java
@@ -74,8 +74,7 @@
      */
     public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
         if (locale != null && !locale.equals(ROOT)) {
-            final String scriptSubtag = ICUCompat.getScript(
-                    ICUCompat.addLikelySubtags(locale.toString()));
+            final String scriptSubtag = ICUCompat.maximizeAndGetScript(locale);
             if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale);
 
             if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
diff --git a/v4/java/android/support/v4/util/CircularArray.java b/v4/java/android/support/v4/util/CircularArray.java
index 91a27da..e2ac26b 100644
--- a/v4/java/android/support/v4/util/CircularArray.java
+++ b/v4/java/android/support/v4/util/CircularArray.java
@@ -14,11 +14,11 @@
 package android.support.v4.util;
 
 /**
- * A circular array implementation that provides O(1) random read and O(1)
- * prepend and O(1) append.
+ * CircularArray is a generic circular array data structure that provides O(1) random read, O(1)
+ * prepend and O(1) append. The CircularArray automatically grows its capacity when number of added
+ * items is over its capacity.
  */
-public class CircularArray<E>
-{
+public final class CircularArray<E> {
     private E[] mElements;
     private int mHead;
     private int mTail;
@@ -29,12 +29,12 @@
         int r = n - mHead;
         int newCapacity = n << 1;
         if (newCapacity < 0) {
-            throw new RuntimeException("Too big");
+            throw new RuntimeException("Max array capacity exceeded");
         }
         Object[] a = new Object[newCapacity];
         System.arraycopy(mElements, mHead, a, 0, r);
         System.arraycopy(mElements, 0, a, r, mHead);
-        mElements = (E[])a;
+        mElements = (E[]) a;
         mHead = 0;
         mTail = n;
         mCapacityBitmask = newCapacity - 1;
@@ -50,7 +50,7 @@
     /**
      * Create a CircularArray with capacity for at least minCapacity elements.
      *
-     * @param minCapacity The minimum capacity required for the circular array.
+     * @param minCapacity The minimum capacity required for the CircularArray.
      */
     public CircularArray(int minCapacity) {
         if (minCapacity <= 0) {
@@ -66,7 +66,11 @@
         mElements = (E[]) new Object[arrayCapacity];
     }
 
-    public final void addFirst(E e) {
+    /**
+     * Add an element in front of the CircularArray.
+     * @param e  Element to add.
+     */
+    public void addFirst(E e) {
         mHead = (mHead - 1) & mCapacityBitmask;
         mElements[mHead] = e;
         if (mHead == mTail) {
@@ -74,7 +78,11 @@
         }
     }
 
-    public final void addLast(E e) {
+    /**
+     * Add an element at end of the CircularArray.
+     * @param e  Element to add.
+     */
+    public void addLast(E e) {
         mElements[mTail] = e;
         mTail = (mTail + 1) & mCapacityBitmask;
         if (mTail == mHead) {
@@ -82,16 +90,30 @@
         }
     }
 
-    public final E popFirst() {
-        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
+    /**
+     * Remove first element from front of the CircularArray and return it.
+     * @return  The element removed.
+     * @throws ArrayIndexOutOfBoundsException if CircularArray is empty.
+     */
+    public E popFirst() {
+        if (mHead == mTail) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
         E result = mElements[mHead];
         mElements[mHead] = null;
         mHead = (mHead + 1) & mCapacityBitmask;
         return result;
     }
 
-    public final E popLast() {
-        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
+    /**
+     * Remove last element from end of the CircularArray and return it.
+     * @return  The element removed.
+     * @throws ArrayIndexOutOfBoundsException if CircularArray is empty.
+     */
+    public E popLast() {
+        if (mHead == mTail) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
         int t = (mTail - 1) & mCapacityBitmask;
         E result = mElements[t];
         mElements[t] = null;
@@ -99,27 +121,131 @@
         return result;
     }
 
-    public final E getFirst() {
-        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
+    /**
+     * Remove all elements from the CircularArray.
+     */
+    public void clear() {
+        removeFromStart(size());
+    }
+
+    /**
+     * Remove multiple elements from front of the CircularArray, ignore when numOfElements
+     * is less than or equals to 0.
+     * @param numOfElements  Number of elements to remove.
+     * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
+     *         {@link #size()}
+     */
+    public void removeFromStart(int numOfElements) {
+        if (numOfElements <= 0) {
+            return;
+        }
+        if (numOfElements > size()) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        int end = mElements.length;
+        if (numOfElements < end - mHead) {
+            end = mHead + numOfElements;
+        }
+        for (int i = mHead; i < end; i++) {
+            mElements[i] = null;
+        }
+        int removed = (end - mHead);
+        numOfElements -= removed;
+        mHead = (mHead + removed) & mCapacityBitmask;
+        if (numOfElements > 0) {
+            // mHead wrapped to 0
+            for (int i = 0; i < numOfElements; i++) {
+                mElements[i] = null;
+            }
+            mHead = numOfElements;
+        }
+    }
+
+    /**
+     * Remove multiple elements from end of the CircularArray, ignore when numOfElements
+     * is less than or equals to 0.
+     * @param numOfElements  Number of elements to remove.
+     * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
+     *         {@link #size()}
+     */
+    public void removeFromEnd(int numOfElements) {
+        if (numOfElements <= 0) {
+            return;
+        }
+        if (numOfElements > size()) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        int start = 0;
+        if (numOfElements < mTail) {
+            start = mTail - numOfElements;
+        }
+        for (int i = start; i < mTail; i++) {
+            mElements[i] = null;
+        }
+        int removed = (mTail - start);
+        numOfElements -= removed;
+        mTail = mTail - removed;
+        if (numOfElements > 0) {
+            // mTail wrapped to mElements.length
+            mTail = mElements.length;
+            int newTail = mTail - numOfElements;
+            for (int i = newTail; i < mTail; i++) {
+                mElements[i] = null;
+            }
+            mTail = newTail;
+        }
+    }
+
+    /**
+     * Get first element of the CircularArray.
+     * @return The first element.
+     * @throws {@link ArrayIndexOutOfBoundsException} if CircularArray is empty.
+     */
+    public E getFirst() {
+        if (mHead == mTail) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
         return mElements[mHead];
     }
 
-    public final E getLast() {
-        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
+    /**
+     * Get last element of the CircularArray.
+     * @return The last element.
+     * @throws {@link ArrayIndexOutOfBoundsException} if CircularArray is empty.
+     */
+    public E getLast() {
+        if (mHead == mTail) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
         return mElements[(mTail - 1) & mCapacityBitmask];
     }
 
-    public final E get(int i) {
-        if (i < 0 || i >= size()) throw new ArrayIndexOutOfBoundsException();
-        int p = (mHead + i) & mCapacityBitmask;
-        return mElements[p];
+    /**
+     * Get nth (0 <= n <= size()-1) element of the CircularArray.
+     * @param n  The zero based element index in the CircularArray.
+     * @return The nth element.
+     * @throws {@link ArrayIndexOutOfBoundsException} if n < 0 or n >= size().
+     */
+    public E get(int n) {
+        if (n < 0 || n >= size()) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        return mElements[(mHead + n) & mCapacityBitmask];
     }
 
-    public final int size() {
+    /**
+     * Get number of elements in the CircularArray.
+     * @return Number of elements in the CircularArray.
+     */
+    public int size() {
         return (mTail - mHead) & mCapacityBitmask;
     }
 
-    public final boolean isEmpty() {
+    /**
+     * Return true if size() is 0.
+     * @return true if size() is 0.
+     */
+    public boolean isEmpty() {
         return mHead == mTail;
     }
 
diff --git a/v4/java/android/support/v4/util/CircularIntArray.java b/v4/java/android/support/v4/util/CircularIntArray.java
new file mode 100644
index 0000000..706d73b
--- /dev/null
+++ b/v4/java/android/support/v4/util/CircularIntArray.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2014 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.util;
+
+/**
+ * CircularIntArray is a circular integer array data structure that provides O(1) random read, O(1)
+ * prepend and O(1) append. The CircularIntArray automatically grows its capacity when number of
+ * added integers is over its capacity.
+ */
+public final class CircularIntArray
+{
+    private int[] mElements;
+    private int mHead;
+    private int mTail;
+    private int mCapacityBitmask;
+
+    private void doubleCapacity() {
+        int n = mElements.length;
+        int r = n - mHead;
+        int newCapacity = n << 1;
+        if (newCapacity < 0) {
+            throw new RuntimeException("Max array capacity exceeded");
+        }
+        int[] a = new int[newCapacity];
+        System.arraycopy(mElements, mHead, a, 0, r);
+        System.arraycopy(mElements, 0, a, r, mHead);
+        mElements = a;
+        mHead = 0;
+        mTail = n;
+        mCapacityBitmask = newCapacity - 1;
+    }
+
+    /**
+     * Create a CircularIntArray with default capacity.
+     */
+    public CircularIntArray() {
+        this(8);
+    }
+
+    /**
+     * Create a CircularIntArray with capacity for at least minCapacity elements.
+     *
+     * @param minCapacity The minimum capacity required for the CircularIntArray.
+     */
+    public CircularIntArray(int minCapacity) {
+        if (minCapacity <= 0) {
+            throw new IllegalArgumentException("capacity must be positive");
+        }
+        int arrayCapacity = minCapacity;
+        // If minCapacity isn't a power of 2, round up to the next highest power
+        // of 2.
+        if (Integer.bitCount(minCapacity) != 1) {
+            arrayCapacity = 1 << (Integer.highestOneBit(minCapacity) + 1);
+        }
+        mCapacityBitmask = arrayCapacity - 1;
+        mElements = new int[arrayCapacity];
+    }
+
+    /**
+     * Add an integer in front of the CircularIntArray.
+     * @param e  Integer to add.
+     */
+    public void addFirst(int e) {
+        mHead = (mHead - 1) & mCapacityBitmask;
+        mElements[mHead] = e;
+        if (mHead == mTail) {
+            doubleCapacity();
+        }
+    }
+
+    /**
+     * Add an integer at end of the CircularIntArray.
+     * @param e  Integer to add.
+     */
+    public void addLast(int e) {
+        mElements[mTail] = e;
+        mTail = (mTail + 1) & mCapacityBitmask;
+        if (mTail == mHead) {
+            doubleCapacity();
+        }
+    }
+
+    /**
+     * Remove first integer from front of the CircularIntArray and return it.
+     * @return  The integer removed.
+     * @throws ArrayIndexOutOfBoundsException if CircularIntArray is empty.
+     */
+    public int popFirst() {
+        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
+        int result = mElements[mHead];
+        mHead = (mHead + 1) & mCapacityBitmask;
+        return result;
+    }
+
+    /**
+     * Remove last integer from end of the CircularIntArray and return it.
+     * @return  The integer removed.
+     * @throws ArrayIndexOutOfBoundsException if CircularIntArray is empty.
+     */
+    public int popLast() {
+        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
+        int t = (mTail - 1) & mCapacityBitmask;
+        int result = mElements[t];
+        mTail = t;
+        return result;
+    }
+
+    /**
+     * Remove all integers from the CircularIntArray.
+     */
+    public void clear() {
+        mTail = mHead;
+    }
+
+    /**
+     * Remove multiple integers from front of the CircularIntArray, ignore when numOfElements
+     * is less than or equals to 0.
+     * @param numOfElements  Number of integers to remove.
+     * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
+     *         {@link #size()}
+     */
+    public void removeFromStart(int numOfElements) {
+        if (numOfElements <= 0) {
+            return;
+        }
+        if (numOfElements > size()) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        mHead = (mHead + numOfElements) & mCapacityBitmask;
+    }
+
+    /**
+     * Remove multiple elements from end of the CircularIntArray, ignore when numOfElements
+     * is less than or equals to 0.
+     * @param numOfElements  Number of integers to remove.
+     * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
+     *         {@link #size()}
+     */
+    public void removeFromEnd(int numOfElements) {
+        if (numOfElements <= 0) {
+            return;
+        }
+        if (numOfElements > size()) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        mTail = (mTail - numOfElements) & mCapacityBitmask;
+    }
+
+    /**
+     * Get first integer of the CircularIntArray.
+     * @return The first integer.
+     * @throws {@link ArrayIndexOutOfBoundsException} if CircularIntArray is empty.
+     */
+    public int getFirst() {
+        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
+        return mElements[mHead];
+    }
+
+    /**
+     * Get last integer of the CircularIntArray.
+     * @return The last integer.
+     * @throws {@link ArrayIndexOutOfBoundsException} if CircularIntArray is empty.
+     */
+    public int getLast() {
+        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
+        return mElements[(mTail - 1) & mCapacityBitmask];
+    }
+
+    /**
+     * Get nth (0 <= n <= size()-1) integer of the CircularIntArray.
+     * @param n  The zero based element index in the CircularIntArray.
+     * @return The nth integer.
+     * @throws {@link ArrayIndexOutOfBoundsException} if n < 0 or n >= size().
+     */
+    public int get(int n) {
+        if (n < 0 || n >= size()) throw new ArrayIndexOutOfBoundsException();
+        return mElements[(mHead + n) & mCapacityBitmask];
+    }
+
+    /**
+     * Get number of integers in the CircularIntArray.
+     * @return Number of integers in the CircularIntArray.
+     */
+    public int size() {
+        return (mTail - mHead) & mCapacityBitmask;
+    }
+
+    /**
+     * Return true if size() is 0.
+     * @return true if size() is 0.
+     */
+    public boolean isEmpty() {
+        return mHead == mTail;
+    }
+
+}
diff --git a/v4/java/android/support/v4/view/InputDeviceCompat.java b/v4/java/android/support/v4/view/InputDeviceCompat.java
new file mode 100644
index 0000000..7a202cd
--- /dev/null
+++ b/v4/java/android/support/v4/view/InputDeviceCompat.java
@@ -0,0 +1,211 @@
+/*
+ * 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;
+
+/**
+ * Helper class for accessing values in {@link android.view.InputDevice}.
+ */
+public class InputDeviceCompat {
+
+    /**
+     * A mask for input source classes.
+     *
+     * Each distinct input source constant has one or more input source class bits set to
+     * specify the desired interpretation for its input events.
+     */
+    public static final int SOURCE_CLASS_MASK = 0x000000ff;
+
+    /**
+     * The input source has no class.
+     *
+     * It is up to the application to determine how to handle the device based on the device type.
+     */
+    public static final int SOURCE_CLASS_NONE = 0x00000000;
+
+    /**
+     * The input source has buttons or keys.
+     * Examples: {@link #SOURCE_KEYBOARD}, {@link #SOURCE_DPAD}.
+     *
+     * A {@link android.view.KeyEvent} should be interpreted as a button or key press.
+     */
+    public static final int SOURCE_CLASS_BUTTON = 0x00000001;
+
+    /**
+     * The input source is a pointing device associated with a display.
+     * Examples: {@link #SOURCE_TOUCHSCREEN}, {@link #SOURCE_MOUSE}.
+     *
+     * A {@link android.view.MotionEvent} should be interpreted as absolute coordinates in
+     * display units according to the {@link android.view.View} hierarchy.  Pointer down/up
+     * indicated when
+     * the finger touches the display or when the selection button is pressed/released.
+     *
+     * Use {@link android.view.InputDevice#getMotionRange} to query the range of the pointing
+     * device.  Some devices permit
+     * touches outside the display area so the effective range may be somewhat smaller or larger
+     * than the actual display size.
+     */
+    public static final int SOURCE_CLASS_POINTER = 0x00000002;
+
+    /**
+     * The input source is a trackball navigation device.
+     * Examples: {@link #SOURCE_TRACKBALL}.
+     *
+     * A {@link android.view.MotionEvent} should be interpreted as relative movements in
+     * device-specific
+     * units used for navigation purposes.  Pointer down/up indicates when the selection button
+     * is pressed/released.
+     *
+     * Use {@link android.view.InputDevice#getMotionRange} to query the range of motion.
+     */
+    public static final int SOURCE_CLASS_TRACKBALL = 0x00000004;
+
+    /**
+     * The input source is an absolute positioning device not associated with a display
+     * (unlike {@link #SOURCE_CLASS_POINTER}).
+     *
+     * A {@link android.view.MotionEvent} should be interpreted as absolute coordinates in
+     * device-specific surface units.
+     *
+     * Use {@link android.view.InputDevice#getMotionRange} to query the range of positions.
+     */
+    public static final int SOURCE_CLASS_POSITION = 0x00000008;
+
+    /**
+     * The input source is a joystick.
+     *
+     * A {@link android.view.MotionEvent} should be interpreted as absolute joystick movements.
+     *
+     * Use {@link android.view.InputDevice#getMotionRange} to query the range of positions.
+     */
+    public static final int SOURCE_CLASS_JOYSTICK = 0x00000010;
+
+    /**
+     * The input source is unknown.
+     */
+    public static final int SOURCE_UNKNOWN = 0x00000000;
+
+    /**
+     * The input source is a keyboard.
+     *
+     * This source indicates pretty much anything that has buttons.  Use
+     * {@link android.view.InputDevice#getKeyboardType()} to determine whether the keyboard has
+     * alphabetic keys
+     * and can be used to enter text.
+     *
+     * @see #SOURCE_CLASS_BUTTON
+     */
+    public static final int SOURCE_KEYBOARD = 0x00000100 | SOURCE_CLASS_BUTTON;
+
+    /**
+     * The input source is a DPad.
+     *
+     * @see #SOURCE_CLASS_BUTTON
+     */
+    public static final int SOURCE_DPAD = 0x00000200 | SOURCE_CLASS_BUTTON;
+
+    /**
+     * The input source is a game pad.
+     * (It may also be a {@link #SOURCE_JOYSTICK}).
+     *
+     * @see #SOURCE_CLASS_BUTTON
+     */
+    public static final int SOURCE_GAMEPAD = 0x00000400 | SOURCE_CLASS_BUTTON;
+
+    /**
+     * The input source is a touch screen pointing device.
+     *
+     * @see #SOURCE_CLASS_POINTER
+     */
+    public static final int SOURCE_TOUCHSCREEN = 0x00001000 | SOURCE_CLASS_POINTER;
+
+    /**
+     * The input source is a mouse pointing device.
+     * This code is also used for other mouse-like pointing devices such as trackpads
+     * and trackpoints.
+     *
+     * @see #SOURCE_CLASS_POINTER
+     */
+    public static final int SOURCE_MOUSE = 0x00002000 | SOURCE_CLASS_POINTER;
+
+    /**
+     * The input source is a stylus pointing device.
+     * <p>
+     * Note that this bit merely indicates that an input device is capable of obtaining
+     * input from a stylus.  To determine whether a given touch event was produced
+     * by a stylus, examine the tool type returned by {@link android.view.MotionEvent#getToolType(int)}
+     * for each individual pointer.
+     * </p><p>
+     * A single touch event may multiple pointers with different tool types,
+     * such as an event that has one pointer with tool type
+     * {@link android.view.MotionEvent#TOOL_TYPE_FINGER} and another pointer with tool type
+     * {@link android.view.MotionEvent#TOOL_TYPE_STYLUS}.  So it is important to examine
+     * the tool type of each pointer, regardless of the source reported
+     * by {@link android.view.MotionEvent#getSource()}.
+     * </p>
+     *
+     * @see #SOURCE_CLASS_POINTER
+     */
+    public static final int SOURCE_STYLUS = 0x00004000 | SOURCE_CLASS_POINTER;
+
+    /**
+     * The input source is a trackball.
+     *
+     * @see #SOURCE_CLASS_TRACKBALL
+     */
+    public static final int SOURCE_TRACKBALL = 0x00010000 | SOURCE_CLASS_TRACKBALL;
+
+    /**
+     * The input source is a touch pad or digitizer tablet that is not
+     * associated with a display (unlike {@link #SOURCE_TOUCHSCREEN}).
+     *
+     * @see #SOURCE_CLASS_POSITION
+     */
+    public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION;
+
+    /**
+     * The input source is a touch device whose motions should be interpreted as navigation events.
+     *
+     * For example, an upward swipe should be as an upward focus traversal in the same manner as
+     * pressing up on a D-Pad would be. Swipes to the left, right and down should be treated in a
+     * similar manner.
+     *
+     * @see #SOURCE_CLASS_NONE
+     */
+    public static final int SOURCE_TOUCH_NAVIGATION = 0x00200000 | SOURCE_CLASS_NONE;
+
+    /**
+     * The input source is a joystick.
+     * (It may also be a {@link #SOURCE_GAMEPAD}).
+     *
+     * @see #SOURCE_CLASS_JOYSTICK
+     */
+    public static final int SOURCE_JOYSTICK = 0x01000000 | SOURCE_CLASS_JOYSTICK;
+
+    /**
+     * The input source is a device connected through HDMI-based bus.
+     *
+     * The key comes in through HDMI-CEC or MHL signal line, and is treated as if it were
+     * generated by a locally connected DPAD or keyboard.
+     */
+    public static final int SOURCE_HDMI = 0x02000000 | SOURCE_CLASS_BUTTON;
+
+    /**
+     * A special input source constant that is used when filtering input devices
+     * to match devices that provide any type of input source.
+     */
+    public static final int SOURCE_ANY = 0xffffff00;
+}
diff --git a/v4/java/android/support/v4/view/LayoutInflaterCompat.java b/v4/java/android/support/v4/view/LayoutInflaterCompat.java
new file mode 100644
index 0000000..56ed8f3
--- /dev/null
+++ b/v4/java/android/support/v4/view/LayoutInflaterCompat.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 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.os.Build;
+import android.view.LayoutInflater;
+
+/**
+ * Helper for accessing features in {@link android.view.LayoutInflater}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class LayoutInflaterCompat {
+
+    interface LayoutInflaterCompatImpl {
+        public void setFactory(LayoutInflater layoutInflater, LayoutInflaterFactory factory);
+    }
+
+    static class LayoutInflaterCompatImplBase implements LayoutInflaterCompatImpl {
+        @Override
+        public void setFactory(LayoutInflater layoutInflater, LayoutInflaterFactory factory) {
+            LayoutInflaterCompatBase.setFactory(layoutInflater, factory);
+        }
+    }
+
+    static class LayoutInflaterCompatImplV11 extends LayoutInflaterCompatImplBase {
+        @Override
+        public void setFactory(LayoutInflater layoutInflater, LayoutInflaterFactory factory) {
+            LayoutInflaterCompatHC.setFactory(layoutInflater, factory);
+        }
+    }
+
+    static final LayoutInflaterCompatImpl IMPL;
+    static {
+        final int version = Build.VERSION.SDK_INT;
+        if (version >= 11) {
+            IMPL = new LayoutInflaterCompatImplV11();
+        } else {
+            IMPL = new LayoutInflaterCompatImplBase();
+        }
+    }
+
+    /*
+     * Hide the constructor.
+     */
+    private LayoutInflaterCompat() {
+    }
+
+    /**
+     * Attach a custom Factory interface for creating views while using
+     * this LayoutInflater. This must not be null, and can only be set once;
+     * after setting, you can not change the factory.
+     *
+     * @see LayoutInflater#setFactory(android.view.LayoutInflater.Factory)
+     */
+    public static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
+        IMPL.setFactory(inflater, factory);
+    }
+
+}
diff --git a/v4/java/android/support/v4/view/MenuItemCompat.java b/v4/java/android/support/v4/view/MenuItemCompat.java
index 8a52e9e..5f776ba 100644
--- a/v4/java/android/support/v4/view/MenuItemCompat.java
+++ b/v4/java/android/support/v4/view/MenuItemCompat.java
@@ -16,6 +16,10 @@
 
 package android.support.v4.view;
 
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.util.Log;
 import android.view.MenuItem;
@@ -77,6 +81,8 @@
         boolean collapseActionView(MenuItem item);
         boolean isActionViewExpanded(MenuItem item);
         MenuItem setOnActionExpandListener(MenuItem item, OnActionExpandListener listener);
+        MenuItem setIconTintList(MenuItem item, ColorStateList tintList);
+        MenuItem setIconTintMode(MenuItem item, PorterDuff.Mode tintMode);
     }
 
     /**
@@ -150,12 +156,30 @@
         public MenuItem setOnActionExpandListener(MenuItem item, OnActionExpandListener listener) {
             return item;
         }
+
+        @Override
+        public MenuItem setIconTintList(MenuItem item, ColorStateList tintList) {
+            Drawable icon = item.getIcon();
+            if (icon != null) {
+                DrawableCompat.setTintList(icon, tintList);
+            }
+            return item;
+        }
+
+        @Override
+        public MenuItem setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) {
+            Drawable icon = item.getIcon();
+            if (icon != null) {
+                DrawableCompat.setTintMode(icon, tintMode);
+            }
+            return item;
+        }
     }
 
     /**
      * Interface implementation for devices with at least v11 APIs.
      */
-    static class HoneycombMenuVersionImpl implements MenuVersionImpl {
+    static class HoneycombMenuVersionImpl extends BaseMenuVersionImpl {
         @Override
         public void setShowAsAction(MenuItem item, int actionEnum) {
             MenuItemCompatHoneycomb.setShowAsAction(item, actionEnum);
@@ -239,13 +263,27 @@
         }
     }
 
+    static class Api23MenuVersionImpl extends IcsMenuVersionImpl {
+        @Override
+        public MenuItem setIconTintList(MenuItem item, ColorStateList tintList) {
+            return MenuItemCompatApi23.setIconTintList(item, tintList);
+        }
+
+        @Override
+        public MenuItem setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) {
+            return MenuItemCompatApi23.setIconTintMode(item, tintMode);
+        }
+    }
+
     /**
      * Select the correct implementation to use for the current platform.
      */
     static final MenuVersionImpl IMPL;
     static {
         final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 14) {
+        if (version >= 23) {
+            IMPL = new Api23MenuVersionImpl();
+        } else if (version >= 14) {
             IMPL = new IcsMenuVersionImpl();
         } else if (version >= 11) {
             IMPL = new HoneycombMenuVersionImpl();
@@ -437,4 +475,50 @@
         }
         return IMPL.setOnActionExpandListener(item, listener);
     }
+
+    /**
+     * Applies a tint to the icon drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to {@link android.view.MenuItem#setIcon(Drawable)}
+     * will automatically mutate the drawable and apply the specified tint and tint mode.
+     * <p>
+     * Where available, this method will call directly into the framework API. Otherwise it will
+     * try to tint the icon directly using
+     * {@link DrawableCompat#setTintList(Drawable, ColorStateList)}. You should wrap the icon
+     * drawable manually using {@link DrawableCompat#wrap(Drawable)} for this to work on all
+     * API levels.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @return This menu item instance for call chaining
+     */
+    public static MenuItem setIconTintList(MenuItem item, ColorStateList tint) {
+        if (item instanceof SupportMenuItem) {
+            return ((SupportMenuItem) item).setIconTintList(tint);
+        } else {
+            return IMPL.setIconTintList(item, tint);
+        }
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by {@link
+     * #setIconTintList(MenuItem, ColorStateList)} to the icon drawable. The default mode is {@link
+     * PorterDuff.Mode#SRC_IN}.
+     * <p>
+     * Where available, this method will call directly into the framework API. Otherwise it will
+     * try to set the mode directly on the icon using
+     * {@link DrawableCompat#setTintMode(Drawable, PorterDuff.Mode)}. You should wrap the icon
+     * drawable manually using {@link DrawableCompat#wrap(Drawable)} for this to work on all
+     * API levels.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be {@code null} to clear tint
+     * @return This menu item instance for call chaining
+     */
+    public static MenuItem setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) {
+        if (item instanceof SupportMenuItem) {
+            return ((SupportMenuItem) item).setIconTintMode(tintMode);
+        } else {
+            return IMPL.setIconTintMode(item, tintMode);
+        }
+    }
 }
diff --git a/v4/java/android/support/v4/view/MotionEventCompat.java b/v4/java/android/support/v4/view/MotionEventCompat.java
index 18e9e5d..8c7d07d 100644
--- a/v4/java/android/support/v4/view/MotionEventCompat.java
+++ b/v4/java/android/support/v4/view/MotionEventCompat.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.view;
 
+import android.os.Build;
 import android.view.MotionEvent;
 
 /**
@@ -32,6 +33,9 @@
         public float getX(MotionEvent event, int pointerIndex);
         public float getY(MotionEvent event, int pointerIndex);
         public int getPointerCount(MotionEvent event);
+        public int getSource(MotionEvent event);
+        float getAxisValue(MotionEvent event, int axis);
+        float getAxisValue(MotionEvent event, int axis, int pointerIndex);
     }
 
     /**
@@ -72,12 +76,27 @@
         public int getPointerCount(MotionEvent event) {
             return 1;
         }
+
+        @Override
+        public int getSource(MotionEvent event) {
+            return InputDeviceCompat.SOURCE_UNKNOWN;
+        }
+
+        @Override
+        public float getAxisValue(MotionEvent event, int axis) {
+            return 0;
+        }
+
+        @Override
+        public float getAxisValue(MotionEvent event, int axis, int pointerIndex) {
+            return 0;
+        }
     }
 
     /**
-     * Interface implementation for devices with at least v11 APIs.
+     * Interface implementation for devices with at least v5 APIs.
      */
-    static class EclairMotionEventVersionImpl implements MotionEventVersionImpl {
+    static class EclairMotionEventVersionImpl extends BaseMotionEventVersionImpl {
         @Override
         public int findPointerIndex(MotionEvent event, int pointerId) {
             return MotionEventCompatEclair.findPointerIndex(event, pointerId);
@@ -101,11 +120,41 @@
     }
 
     /**
+     * Interface implementation for devices with at least v8 APIs.
+     */
+    static class GingerbreadMotionEventVersionImpl extends EclairMotionEventVersionImpl {
+        @Override
+        public int getSource(MotionEvent event) {
+            return MotionEventCompatGingerbread.getSource(event);
+        }
+    }
+
+    /**
+     * Interface implementation for devices with at least v12 APIs.
+     */
+    static class HoneycombMr1MotionEventVersionImpl extends GingerbreadMotionEventVersionImpl {
+
+        @Override
+        public float getAxisValue(MotionEvent event, int axis) {
+            return MotionEventCompatHoneycombMr1.getAxisValue(event, axis);
+        }
+
+        @Override
+        public float getAxisValue(MotionEvent event, int axis, int pointerIndex) {
+            return MotionEventCompatHoneycombMr1.getAxisValue(event, axis, pointerIndex);
+        }
+    }
+
+    /**
      * Select the correct implementation to use for the current platform.
      */
     static final MotionEventVersionImpl IMPL;
     static {
-        if (android.os.Build.VERSION.SDK_INT >= 5) {
+        if (Build.VERSION.SDK_INT >= 12) {
+            IMPL = new HoneycombMr1MotionEventVersionImpl();
+        } else if (Build.VERSION.SDK_INT >= 9) {
+            IMPL = new GingerbreadMotionEventVersionImpl();
+        } else if (Build.VERSION.SDK_INT >= 5) {
             IMPL = new EclairMotionEventVersionImpl();
         } else {
             IMPL = new BaseMotionEventVersionImpl();
@@ -150,32 +199,226 @@
     public static final int ACTION_POINTER_INDEX_SHIFT = 8;
 
     /**
-     * Constant for {@link #getActionMasked}: The pointer is not down but has entered the
-     * boundaries of a window or view.
-     * <p>
-     * This action is always delivered to the window or view under the pointer.
-     * </p><p>
-     * This action is not a touch event so it is delivered to
-     * {@link android.view.View#onGenericMotionEvent(MotionEvent)} rather than
-     * {@link android.view.View#onTouchEvent(MotionEvent)}.
-     * </p>
+     * Synonym for {@link MotionEvent#ACTION_HOVER_ENTER}.
      */
     public static final int ACTION_HOVER_ENTER = 9;
 
     /**
-     * Constant for {@link #getActionMasked}: The pointer is not down but has exited the
-     * boundaries of a window or view.
-     * <p>
-     * This action is always delivered to the window or view that was previously under the pointer.
-     * </p><p>
-     * This action is not a touch event so it is delivered to
-     * {@link android.view.View#onGenericMotionEvent(MotionEvent)} rather than
-     * {@link android.view.View#onTouchEvent(MotionEvent)}.
-     * </p>
+     * Synonym for {@link MotionEvent#ACTION_HOVER_EXIT}.
      */
     public static final int ACTION_HOVER_EXIT = 10;
 
     /**
+     * Synonym for {@link MotionEvent#AXIS_X}.
+     */
+    public static final int AXIS_X = 0;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_Y}.
+     */
+    public static final int AXIS_Y = 1;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_PRESSURE}.
+     */
+    public static final int AXIS_PRESSURE = 2;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_SIZE}.
+     */
+    public static final int AXIS_SIZE = 3;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_TOUCH_MAJOR}.
+     */
+    public static final int AXIS_TOUCH_MAJOR = 4;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_TOUCH_MINOR}.
+     */
+    public static final int AXIS_TOUCH_MINOR = 5;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_TOOL_MAJOR}.
+     */
+    public static final int AXIS_TOOL_MAJOR = 6;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_TOOL_MINOR}.
+     */
+    public static final int AXIS_TOOL_MINOR = 7;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_ORIENTATION}.
+     */
+    public static final int AXIS_ORIENTATION = 8;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_VSCROLL}.
+     */
+    public static final int AXIS_VSCROLL = 9;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_HSCROLL}.
+     */
+    public static final int AXIS_HSCROLL = 10;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_Z}.
+     */
+    public static final int AXIS_Z = 11;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_RX}.
+     */
+    public static final int AXIS_RX = 12;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_RY}.
+     */
+    public static final int AXIS_RY = 13;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_RZ}.
+     */
+    public static final int AXIS_RZ = 14;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_HAT_X}.
+     */
+    public static final int AXIS_HAT_X = 15;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_HAT_Y}.
+     */
+    public static final int AXIS_HAT_Y = 16;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_LTRIGGER}.
+     */
+    public static final int AXIS_LTRIGGER = 17;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_RTRIGGER}.
+     */
+    public static final int AXIS_RTRIGGER = 18;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_THROTTLE}.
+     */
+    public static final int AXIS_THROTTLE = 19;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_RUDDER}.
+     */
+    public static final int AXIS_RUDDER = 20;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_WHEEL}.
+     */
+    public static final int AXIS_WHEEL = 21;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GAS}.
+     */
+    public static final int AXIS_GAS = 22;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_BRAKE}.
+     */
+    public static final int AXIS_BRAKE = 23;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_DISTANCE}.
+     */
+    public static final int AXIS_DISTANCE = 24;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_TILT}.
+     */
+    public static final int AXIS_TILT = 25;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_1}.
+     */
+    public static final int AXIS_GENERIC_1 = 32;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_2}.
+     */
+    public static final int AXIS_GENERIC_2 = 33;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_3}.
+     */
+    public static final int AXIS_GENERIC_3 = 34;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_4}.
+     */
+    public static final int AXIS_GENERIC_4 = 35;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_5}.
+     */
+    public static final int AXIS_GENERIC_5 = 36;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_6}.
+     */
+    public static final int AXIS_GENERIC_6 = 37;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_7}.
+     */
+    public static final int AXIS_GENERIC_7 = 38;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_8}.
+     */
+    public static final int AXIS_GENERIC_8 = 39;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_9}.
+     */
+    public static final int AXIS_GENERIC_9 = 40;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_10}.
+     */
+    public static final int AXIS_GENERIC_10 = 41;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_11}.
+     */
+    public static final int AXIS_GENERIC_11 = 42;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_12}.
+     */
+    public static final int AXIS_GENERIC_12 = 43;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_13}.
+     */
+    public static final int AXIS_GENERIC_13 = 44;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_14}.
+     */
+    public static final int AXIS_GENERIC_14 = 45;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_15}.
+     */
+    public static final int AXIS_GENERIC_15 = 46;
+
+    /**
+     * Synonym for {@link MotionEvent#AXIS_GENERIC_16}.
+     */
+    public static final int AXIS_GENERIC_16 = 47;
+
+    /**
      * Call {@link MotionEvent#getAction}, returning only the {@link #ACTION_MASK}
      * portion.
      */
@@ -235,4 +478,42 @@
     public static int getPointerCount(MotionEvent event) {
         return IMPL.getPointerCount(event);
     }
+
+    /**
+     * Gets the source of the event.
+     *
+     * @return The event source or {@link InputDeviceCompat#SOURCE_UNKNOWN} if unknown.
+     */
+    public static int getSource(MotionEvent event) {
+        return IMPL.getSource(event);
+    }
+
+    /**
+     * Get axis value for the first pointer index (may be an
+     * arbitrary pointer identifier).
+     *
+     * @param axis The axis identifier for the axis value to retrieve.
+     *
+     * @see #AXIS_X
+     * @see #AXIS_Y
+     */
+    public static float getAxisValue(MotionEvent event, int axis) {
+        return IMPL.getAxisValue(event, axis);
+    }
+
+    /**
+     * Returns the value of the requested axis for the given pointer <em>index</em>
+     * (use {@link #getPointerId(MotionEvent, int)} to find the pointer identifier for this index).
+     *
+     * @param axis The axis identifier for the axis value to retrieve.
+     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
+     * (the first pointer that is down) to {@link #getPointerCount(MotionEvent)}-1.
+     * @return The value of the axis, or 0 if the axis is not available.
+     *
+     * @see #AXIS_X
+     * @see #AXIS_Y
+     */
+    public static float getAxisValue(MotionEvent event, int axis, int pointerIndex) {
+        return IMPL.getAxisValue(event, axis, pointerIndex);
+    }
 }
diff --git a/v4/java/android/support/v4/view/NestedScrollingChild.java b/v4/java/android/support/v4/view/NestedScrollingChild.java
new file mode 100644
index 0000000..40e0a09
--- /dev/null
+++ b/v4/java/android/support/v4/view/NestedScrollingChild.java
@@ -0,0 +1,227 @@
+/*
+ * 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.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewParent;
+
+/**
+ * This interface should be implemented by {@link android.view.View View} subclasses that wish
+ * to support dispatching nested scrolling operations to a cooperating parent
+ * {@link android.view.ViewGroup ViewGroup}.
+ *
+ * <p>Classes implementing this interface should create a final instance of a
+ * {@link NestedScrollingChildHelper} as a field and delegate any View methods to the
+ * <code>NestedScrollingChildHelper</code> methods of the same signature.</p>
+ *
+ * <p>Views invoking nested scrolling functionality should always do so from the relevant
+ * {@link ViewCompat}, {@link ViewGroupCompat} or {@link ViewParentCompat} compatibility
+ * shim static methods. This ensures interoperability with nested scrolling views on Android
+ * 5.0 Lollipop and newer.</p>
+ */
+public interface NestedScrollingChild {
+    /**
+     * Enable or disable nested scrolling for this view.
+     *
+     * <p>If this property is set to true the view will be permitted to initiate nested
+     * scrolling operations with a compatible parent view in the current hierarchy. If this
+     * view does not implement nested scrolling this will have no effect. Disabling nested scrolling
+     * while a nested scroll is in progress has the effect of {@link #stopNestedScroll() stopping}
+     * the nested scroll.</p>
+     *
+     * @param enabled true to enable nested scrolling, false to disable
+     *
+     * @see #isNestedScrollingEnabled()
+     */
+    public void setNestedScrollingEnabled(boolean enabled);
+
+    /**
+     * Returns true if nested scrolling is enabled for this view.
+     *
+     * <p>If nested scrolling is enabled and this View class implementation supports it,
+     * this view will act as a nested scrolling child view when applicable, forwarding data
+     * about the scroll operation in progress to a compatible and cooperating nested scrolling
+     * parent.</p>
+     *
+     * @return true if nested scrolling is enabled
+     *
+     * @see #setNestedScrollingEnabled(boolean)
+     */
+    public boolean isNestedScrollingEnabled();
+
+    /**
+     * Begin a nestable scroll operation along the given axes.
+     *
+     * <p>A view starting a nested scroll promises to abide by the following contract:</p>
+     *
+     * <p>The view will call startNestedScroll upon initiating a scroll operation. In the case
+     * of a touch scroll this corresponds to the initial {@link MotionEvent#ACTION_DOWN}.
+     * In the case of touch scrolling the nested scroll will be terminated automatically in
+     * the same manner as {@link ViewParent#requestDisallowInterceptTouchEvent(boolean)}.
+     * In the event of programmatic scrolling the caller must explicitly call
+     * {@link #stopNestedScroll()} to indicate the end of the nested scroll.</p>
+     *
+     * <p>If <code>startNestedScroll</code> returns true, a cooperative parent was found.
+     * If it returns false the caller may ignore the rest of this contract until the next scroll.
+     * Calling startNestedScroll while a nested scroll is already in progress will return true.</p>
+     *
+     * <p>At each incremental step of the scroll the caller should invoke
+     * {@link #dispatchNestedPreScroll(int, int, int[], int[]) dispatchNestedPreScroll}
+     * once it has calculated the requested scrolling delta. If it returns true the nested scrolling
+     * parent at least partially consumed the scroll and the caller should adjust the amount it
+     * scrolls by.</p>
+     *
+     * <p>After applying the remainder of the scroll delta the caller should invoke
+     * {@link #dispatchNestedScroll(int, int, int, int, int[]) dispatchNestedScroll}, passing
+     * both the delta consumed and the delta unconsumed. A nested scrolling parent may treat
+     * these values differently. See
+     * {@link NestedScrollingParent#onNestedScroll(View, int, int, int, int)}.
+     * </p>
+     *
+     * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}
+     *             and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}.
+     * @return true if a cooperative parent was found and nested scrolling has been enabled for
+     *         the current gesture.
+     *
+     * @see #stopNestedScroll()
+     * @see #dispatchNestedPreScroll(int, int, int[], int[])
+     * @see #dispatchNestedScroll(int, int, int, int, int[])
+     */
+    public boolean startNestedScroll(int axes);
+
+    /**
+     * Stop a nested scroll in progress.
+     *
+     * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p>
+     *
+     * @see #startNestedScroll(int)
+     */
+    public void stopNestedScroll();
+
+    /**
+     * Returns true if this view has a nested scrolling parent.
+     *
+     * <p>The presence of a nested scrolling parent indicates that this view has initiated
+     * a nested scroll and it was accepted by an ancestor view further up the view hierarchy.</p>
+     *
+     * @return whether this view has a nested scrolling parent
+     */
+    public boolean hasNestedScrollingParent();
+
+    /**
+     * Dispatch one step of a nested scroll in progress.
+     *
+     * <p>Implementations of views that support nested scrolling should call this to report
+     * info about a scroll in progress to the current nested scrolling parent. If a nested scroll
+     * is not currently in progress or nested scrolling is not
+     * {@link #isNestedScrollingEnabled() enabled} for this view this method does nothing.</p>
+     *
+     * <p>Compatible View implementations should also call
+     * {@link #dispatchNestedPreScroll(int, int, int[], int[]) dispatchNestedPreScroll} before
+     * consuming a component of the scroll event themselves.</p>
+     *
+     * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step
+     * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step
+     * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view
+     * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view
+     * @param offsetInWindow Optional. If not null, on return this will contain the offset
+     *                       in local view coordinates of this view from before this operation
+     *                       to after it completes. View implementations may use this to adjust
+     *                       expected input coordinate tracking.
+     * @return true if the event was dispatched, false if it could not be dispatched.
+     * @see #dispatchNestedPreScroll(int, int, int[], int[])
+     */
+    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
+
+    /**
+     * Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
+     *
+     * <p>Nested pre-scroll events are to nested scroll events what touch intercept is to touch.
+     * <code>dispatchNestedPreScroll</code> offers an opportunity for the parent view in a nested
+     * scrolling operation to consume some or all of the scroll operation before the child view
+     * consumes it.</p>
+     *
+     * @param dx Horizontal scroll distance in pixels
+     * @param dy Vertical scroll distance in pixels
+     * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx
+     *                 and consumed[1] the consumed dy.
+     * @param offsetInWindow Optional. If not null, on return this will contain the offset
+     *                       in local view coordinates of this view from before this operation
+     *                       to after it completes. View implementations may use this to adjust
+     *                       expected input coordinate tracking.
+     * @return true if the parent consumed some or all of the scroll delta
+     * @see #dispatchNestedScroll(int, int, int, int, int[])
+     */
+    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
+
+    /**
+     * Dispatch a fling to a nested scrolling parent.
+     *
+     * <p>This method should be used to indicate that a nested scrolling child has detected
+     * suitable conditions for a fling. Generally this means that a touch scroll has ended with a
+     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+     * along a scrollable axis.</p>
+     *
+     * <p>If a nested scrolling child view would normally fling but it is at the edge of
+     * its own content, it can use this method to delegate the fling to its nested scrolling
+     * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
+     *
+     * @param velocityX Horizontal fling velocity in pixels per second
+     * @param velocityY Vertical fling velocity in pixels per second
+     * @param consumed true if the child consumed the fling, false otherwise
+     * @return true if the nested scrolling parent consumed or otherwise reacted to the fling
+     */
+    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
+
+    /**
+     * Dispatch a fling to a nested scrolling parent before it is processed by this view.
+     *
+     * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
+     * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
+     * offsets an opportunity for the parent view in a nested fling to fully consume the fling
+     * before the child view consumes it. If this method returns <code>true</code>, a nested
+     * parent view consumed the fling and this view should not scroll as a result.</p>
+     *
+     * <p>For a better user experience, only one view in a nested scrolling chain should consume
+     * the fling at a time. If a parent view consumed the fling this method will return false.
+     * Custom view implementations should account for this in two ways:</p>
+     *
+     * <ul>
+     *     <li>If a custom view is paged and needs to settle to a fixed page-point, do not
+     *     call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
+     *     position regardless.</li>
+     *     <li>If a nested parent does consume the fling, this view should not scroll at all,
+     *     even to settle back to a valid idle position.</li>
+     * </ul>
+     *
+     * <p>Views should also not offer fling velocities to nested parent views along an axis
+     * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
+     * should not offer a horizontal fling velocity to its parents since scrolling along that
+     * axis is not permitted and carrying velocity along that motion does not make sense.</p>
+     *
+     * @param velocityX Horizontal fling velocity in pixels per second
+     * @param velocityY Vertical fling velocity in pixels per second
+     * @return true if a nested scrolling parent consumed the fling
+     */
+    public boolean dispatchNestedPreFling(float velocityX, float velocityY);
+}
diff --git a/v4/java/android/support/v4/view/NestedScrollingChildHelper.java b/v4/java/android/support/v4/view/NestedScrollingChildHelper.java
new file mode 100644
index 0000000..9e25667
--- /dev/null
+++ b/v4/java/android/support/v4/view/NestedScrollingChildHelper.java
@@ -0,0 +1,282 @@
+/*
+ * 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.view.View;
+import android.view.ViewParent;
+
+/**
+ * Helper class for implementing nested scrolling child views compatible with Android platform
+ * versions earlier than Android 5.0 Lollipop (API 21).
+ *
+ * <p>{@link android.view.View View} subclasses should instantiate a final instance of this
+ * class as a field at construction. For each <code>View</code> method that has a matching
+ * method signature in this class, delegate the operation to the helper instance in an overriden
+ * method implementation. This implements the standard framework policy for nested scrolling.</p>
+ *
+ * <p>Views invoking nested scrolling functionality should always do so from the relevant
+ * {@link ViewCompat}, {@link ViewGroupCompat} or {@link ViewParentCompat} compatibility
+ * shim static methods. This ensures interoperability with nested scrolling views on Android
+ * 5.0 Lollipop and newer.</p>
+ */
+public class NestedScrollingChildHelper {
+    private final View mView;
+    private ViewParent mNestedScrollingParent;
+    private boolean mIsNestedScrollingEnabled;
+    private int[] mTempNestedScrollConsumed;
+
+    /**
+     * Construct a new helper for a given view.
+     */
+    public NestedScrollingChildHelper(View view) {
+        mView = view;
+    }
+
+    /**
+     * Enable nested scrolling.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     *
+     * @param enabled true to enable nested scrolling dispatch from this view, false otherwise
+     */
+    public void setNestedScrollingEnabled(boolean enabled) {
+        if (mIsNestedScrollingEnabled) {
+            ViewCompat.stopNestedScroll(mView);
+        }
+        mIsNestedScrollingEnabled = enabled;
+    }
+
+    /**
+     * Check if nested scrolling is enabled for this view.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     *
+     * @return true if nested scrolling is enabled for this view
+     */
+    public boolean isNestedScrollingEnabled() {
+        return mIsNestedScrollingEnabled;
+    }
+
+    /**
+     * Check if this view has a nested scrolling parent view currently receiving events for
+     * a nested scroll in progress.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     *
+     * @return true if this view has a nested scrolling parent, false otherwise
+     */
+    public boolean hasNestedScrollingParent() {
+        return mNestedScrollingParent != null;
+    }
+
+    /**
+     * Start a new nested scroll for this view.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     *
+     * @param axes Supported nested scroll axes.
+     *             See {@link NestedScrollingChild#startNestedScroll(int)}.
+     * @return true if a cooperating parent view was found and nested scrolling started successfully
+     */
+    public boolean startNestedScroll(int axes) {
+        if (hasNestedScrollingParent()) {
+            // Already in progress
+            return true;
+        }
+        if (isNestedScrollingEnabled()) {
+            ViewParent p = mView.getParent();
+            View child = mView;
+            while (p != null) {
+                if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
+                    mNestedScrollingParent = p;
+                    ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
+                    return true;
+                }
+                if (p instanceof View) {
+                    child = (View) p;
+                }
+                p = p.getParent();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Stop a nested scroll in progress.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     */
+    public void stopNestedScroll() {
+        if (mNestedScrollingParent != null) {
+            ViewParentCompat.onStopNestedScroll(mNestedScrollingParent, mView);
+            mNestedScrollingParent = null;
+        }
+    }
+
+    /**
+     * Dispatch one step of a nested scrolling operation to the current nested scrolling parent.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     *
+     * @return true if the parent consumed any of the nested scroll
+     */
+    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
+        if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
+            if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) {
+                int startX = 0;
+                int startY = 0;
+                if (offsetInWindow != null) {
+                    mView.getLocationInWindow(offsetInWindow);
+                    startX = offsetInWindow[0];
+                    startY = offsetInWindow[1];
+                }
+
+                ViewParentCompat.onNestedScroll(mNestedScrollingParent, mView, dxConsumed,
+                        dyConsumed, dxUnconsumed, dyUnconsumed);
+
+                if (offsetInWindow != null) {
+                    mView.getLocationInWindow(offsetInWindow);
+                    offsetInWindow[0] -= startX;
+                    offsetInWindow[1] -= startY;
+                }
+                return true;
+            } else if (offsetInWindow != null) {
+                // No motion, no dispatch. Keep offsetInWindow up to date.
+                offsetInWindow[0] = 0;
+                offsetInWindow[1] = 0;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Dispatch one step of a nested pre-scrolling operation to the current nested scrolling parent.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     *
+     * @return true if the parent consumed any of the nested scroll
+     */
+    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
+        if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
+            if (dx != 0 || dy != 0) {
+                int startX = 0;
+                int startY = 0;
+                if (offsetInWindow != null) {
+                    mView.getLocationInWindow(offsetInWindow);
+                    startX = offsetInWindow[0];
+                    startY = offsetInWindow[1];
+                }
+
+                if (consumed == null) {
+                    if (mTempNestedScrollConsumed == null) {
+                        mTempNestedScrollConsumed = new int[2];
+                    }
+                    consumed = mTempNestedScrollConsumed;
+                }
+                consumed[0] = 0;
+                consumed[1] = 0;
+                ViewParentCompat.onNestedPreScroll(mNestedScrollingParent, mView, dx, dy, consumed);
+
+                if (offsetInWindow != null) {
+                    mView.getLocationInWindow(offsetInWindow);
+                    offsetInWindow[0] -= startX;
+                    offsetInWindow[1] -= startY;
+                }
+                return consumed[0] != 0 || consumed[1] != 0;
+            } else if (offsetInWindow != null) {
+                offsetInWindow[0] = 0;
+                offsetInWindow[1] = 0;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Dispatch a nested fling operation to the current nested scrolling parent.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     *
+     * @return true if the parent consumed the nested fling
+     */
+    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
+        if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
+            return ViewParentCompat.onNestedFling(mNestedScrollingParent, mView, velocityX,
+                    velocityY, consumed);
+        }
+        return false;
+    }
+
+    /**
+     * Dispatch a nested pre-fling operation to the current nested scrolling parent.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     *
+     * @return true if the parent consumed the nested fling
+     */
+    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
+        if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
+            return ViewParentCompat.onNestedPreFling(mNestedScrollingParent, mView, velocityX,
+                    velocityY);
+        }
+        return false;
+    }
+
+    /**
+     * View subclasses should always call this method on their
+     * <code>NestedScrollingChildHelper</code> when detached from a window.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     */
+    public void onDetachedFromWindow() {
+        ViewCompat.stopNestedScroll(mView);
+    }
+
+    /**
+     * Called when a nested scrolling child stops its current nested scroll operation.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
+     * method/{@link NestedScrollingChild} interface method with the same signature to implement
+     * the standard policy.</p>
+     *
+     * @param child Child view stopping its nested scroll. This may not be a direct child view.
+     */
+    public void onStopNestedScroll(View child) {
+        ViewCompat.stopNestedScroll(mView);
+    }
+}
diff --git a/v4/java/android/support/v4/view/NestedScrollingParent.java b/v4/java/android/support/v4/view/NestedScrollingParent.java
new file mode 100644
index 0000000..bc056b6
--- /dev/null
+++ b/v4/java/android/support/v4/view/NestedScrollingParent.java
@@ -0,0 +1,196 @@
+/*
+ * 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.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+/**
+ * This interface should be implemented by {@link android.view.ViewGroup ViewGroup} subclasses
+ * that wish to support scrolling operations delegated by a nested child view.
+ *
+ * <p>Classes implementing this interface should create a final instance of a
+ * {@link NestedScrollingParentHelper} as a field and delegate any View or ViewGroup methods
+ * to the <code>NestedScrollingParentHelper</code> methods of the same signature.</p>
+ *
+ * <p>Views invoking nested scrolling functionality should always do so from the relevant
+ * {@link ViewCompat}, {@link ViewGroupCompat} or {@link ViewParentCompat} compatibility
+ * shim static methods. This ensures interoperability with nested scrolling views on Android
+ * 5.0 Lollipop and newer.</p>
+ */
+public interface NestedScrollingParent {
+    /**
+     * React to a descendant view initiating a nestable scroll operation, claiming the
+     * nested scroll operation if appropriate.
+     *
+     * <p>This method will be called in response to a descendant view invoking
+     * {@link ViewCompat#startNestedScroll(View, int)}. Each parent up the view hierarchy will be
+     * given an opportunity to respond and claim the nested scrolling operation by returning
+     * <code>true</code>.</p>
+     *
+     * <p>This method may be overridden by ViewParent implementations to indicate when the view
+     * is willing to support a nested scrolling operation that is about to begin. If it returns
+     * true, this ViewParent will become the target view's nested scrolling parent for the duration
+     * of the scroll operation in progress. When the nested scroll is finished this ViewParent
+     * will receive a call to {@link #onStopNestedScroll(View)}.
+     * </p>
+     *
+     * @param child Direct child of this ViewParent containing target
+     * @param target View that initiated the nested scroll
+     * @param nestedScrollAxes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
+     * @return true if this ViewParent accepts the nested scroll operation
+     */
+    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
+
+    /**
+     * React to the successful claiming of a nested scroll operation.
+     *
+     * <p>This method will be called after
+     * {@link #onStartNestedScroll(View, View, int) onStartNestedScroll} returns true. It offers
+     * an opportunity for the view and its superclasses to perform initial configuration
+     * for the nested scroll. Implementations of this method should always call their superclass's
+     * implementation of this method if one is present.</p>
+     *
+     * @param child Direct child of this ViewParent containing target
+     * @param target View that initiated the nested scroll
+     * @param nestedScrollAxes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
+     * @see #onStartNestedScroll(View, View, int)
+     * @see #onStopNestedScroll(View)
+     */
+    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
+
+    /**
+     * React to a nested scroll operation ending.
+     *
+     * <p>Perform cleanup after a nested scrolling operation.
+     * This method will be called when a nested scroll stops, for example when a nested touch
+     * scroll ends with a {@link MotionEvent#ACTION_UP} or {@link MotionEvent#ACTION_CANCEL} event.
+     * Implementations of this method should always call their superclass's implementation of this
+     * method if one is present.</p>
+     *
+     * @param target View that initiated the nested scroll
+     */
+    public void onStopNestedScroll(View target);
+
+    /**
+     * React to a nested scroll in progress.
+     *
+     * <p>This method will be called when the ViewParent's current nested scrolling child view
+     * dispatches a nested scroll event. To receive calls to this method the ViewParent must have
+     * previously returned <code>true</code> for a call to
+     * {@link #onStartNestedScroll(View, View, int)}.</p>
+     *
+     * <p>Both the consumed and unconsumed portions of the scroll distance are reported to the
+     * ViewParent. An implementation may choose to use the consumed portion to match or chase scroll
+     * position of multiple child elements, for example. The unconsumed portion may be used to
+     * allow continuous dragging of multiple scrolling or draggable elements, such as scrolling
+     * a list within a vertical drawer where the drawer begins dragging once the edge of inner
+     * scrolling content is reached.</p>
+     *
+     * @param target The descendent view controlling the nested scroll
+     * @param dxConsumed Horizontal scroll distance in pixels already consumed by target
+     * @param dyConsumed Vertical scroll distance in pixels already consumed by target
+     * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by target
+     * @param dyUnconsumed Vertical scroll distance in pixels not consumed by target
+     */
+    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed);
+
+    /**
+     * React to a nested scroll in progress before the target view consumes a portion of the scroll.
+     *
+     * <p>When working with nested scrolling often the parent view may want an opportunity
+     * to consume the scroll before the nested scrolling child does. An example of this is a
+     * drawer that contains a scrollable list. The user will want to be able to scroll the list
+     * fully into view before the list itself begins scrolling.</p>
+     *
+     * <p><code>onNestedPreScroll</code> is called when a nested scrolling child invokes
+     * {@link View#dispatchNestedPreScroll(int, int, int[], int[])}. The implementation should
+     * report how any pixels of the scroll reported by dx, dy were consumed in the
+     * <code>consumed</code> array. Index 0 corresponds to dx and index 1 corresponds to dy.
+     * This parameter will never be null. Initial values for consumed[0] and consumed[1]
+     * will always be 0.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param dx Horizontal scroll distance in pixels
+     * @param dy Vertical scroll distance in pixels
+     * @param consumed Output. The horizontal and vertical scroll distance consumed by this parent
+     */
+    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
+
+    /**
+     * Request a fling from a nested scroll.
+     *
+     * <p>This method signifies that a nested scrolling child has detected suitable conditions
+     * for a fling. Generally this means that a touch scroll has ended with a
+     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+     * along a scrollable axis.</p>
+     *
+     * <p>If a nested scrolling child view would normally fling but it is at the edge of
+     * its own content, it can use this method to delegate the fling to its nested scrolling
+     * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param velocityX Horizontal velocity in pixels per second
+     * @param velocityY Vertical velocity in pixels per second
+     * @param consumed true if the child consumed the fling, false otherwise
+     * @return true if this parent consumed or otherwise reacted to the fling
+     */
+    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
+
+    /**
+     * React to a nested fling before the target view consumes it.
+     *
+     * <p>This method siginfies that a nested scrolling child has detected a fling with the given
+     * velocity along each axis. Generally this means that a touch scroll has ended with a
+     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+     * along a scrollable axis.</p>
+     *
+     * <p>If a nested scrolling parent is consuming motion as part of a
+     * {@link #onNestedPreScroll(View, int, int, int[]) pre-scroll}, it may be appropriate for
+     * it to also consume the pre-fling to complete that same motion. By returning
+     * <code>true</code> from this method, the parent indicates that the child should not
+     * fling its own internal content as well.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param velocityX Horizontal velocity in pixels per second
+     * @param velocityY Vertical velocity in pixels per second
+     * @return true if this parent consumed the fling ahead of the target view
+     */
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY);
+
+    /**
+     * Return the current axes of nested scrolling for this NestedScrollingParent.
+     *
+     * <p>A NestedScrollingParent returning something other than {@link ViewCompat#SCROLL_AXIS_NONE}
+     * is currently acting as a nested scrolling parent for one or more descendant views in
+     * the hierarchy.</p>
+     *
+     * @return Flags indicating the current axes of nested scrolling
+     * @see ViewCompat#SCROLL_AXIS_HORIZONTAL
+     * @see ViewCompat#SCROLL_AXIS_VERTICAL
+     * @see ViewCompat#SCROLL_AXIS_NONE
+     */
+    public int getNestedScrollAxes();
+}
diff --git a/v4/java/android/support/v4/view/NestedScrollingParentHelper.java b/v4/java/android/support/v4/view/NestedScrollingParentHelper.java
new file mode 100644
index 0000000..91522f1
--- /dev/null
+++ b/v4/java/android/support/v4/view/NestedScrollingParentHelper.java
@@ -0,0 +1,84 @@
+/*
+ * 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.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Helper class for implementing nested scrolling parent views compatible with Android platform
+ * versions earlier than Android 5.0 Lollipop (API 21).
+ *
+ * <p>{@link android.view.ViewGroup ViewGroup} subclasses should instantiate a final instance
+ * of this class as a field at construction. For each <code>ViewGroup</code> method that has
+ * a matching method signature in this class, delegate the operation to the helper instance
+ * in an overriden method implementation. This implements the standard framework policy
+ * for nested scrolling.</p>
+ *
+ * <p>Views invoking nested scrolling functionality should always do so from the relevant
+ * {@link ViewCompat}, {@link ViewGroupCompat} or {@link ViewParentCompat} compatibility
+ * shim static methods. This ensures interoperability with nested scrolling views on Android
+ * 5.0 Lollipop and newer.</p>
+ */
+public class NestedScrollingParentHelper {
+    private final ViewGroup mViewGroup;
+    private int mNestedScrollAxes;
+
+    /**
+     * Construct a new helper for a given ViewGroup
+     */
+    public NestedScrollingParentHelper(ViewGroup viewGroup) {
+        mViewGroup = viewGroup;
+    }
+
+    /**
+     * Called when a nested scrolling operation initiated by a descendant view is accepted
+     * by this ViewGroup.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.ViewGroup ViewGroup}
+     * subclass method/{@link NestedScrollingParent} interface method with the same signature
+     * to implement the standard policy.</p>
+     */
+    public void onNestedScrollAccepted(View child, View target, int axes) {
+        mNestedScrollAxes = axes;
+    }
+
+    /**
+     * Return the current axes of nested scrolling for this ViewGroup.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.ViewGroup ViewGroup}
+     * subclass method/{@link NestedScrollingParent} interface method with the same signature
+     * to implement the standard policy.</p>
+     */
+    public int getNestedScrollAxes() {
+        return mNestedScrollAxes;
+    }
+
+    /**
+     * React to a nested scroll operation ending.
+     *
+     * <p>This is a delegate method. Call it from your {@link android.view.ViewGroup ViewGroup}
+     * subclass method/{@link NestedScrollingParent} interface method with the same signature
+     * to implement the standard policy.</p>
+     *
+     * @param target View that initiated the nested scroll
+     */
+    public void onStopNestedScroll(View target) {
+        mNestedScrollAxes = 0;
+    }
+}
diff --git a/v4/java/android/support/v4/view/PagerTabStrip.java b/v4/java/android/support/v4/view/PagerTabStrip.java
index 834035c..cd0206b 100644
--- a/v4/java/android/support/v4/view/PagerTabStrip.java
+++ b/v4/java/android/support/v4/view/PagerTabStrip.java
@@ -21,6 +21,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
 import android.support.annotation.ColorRes;
 import android.support.annotation.DrawableRes;
 import android.util.AttributeSet;
@@ -127,7 +128,7 @@
      *
      * @param color Color to set as an 0xRRGGBB value. The high byte (alpha) is ignored.
      */
-    public void setTabIndicatorColor(int color) {
+    public void setTabIndicatorColor(@ColorInt int color) {
         mIndicatorColor = color;
         mTabPaint.setColor(mIndicatorColor);
         invalidate();
@@ -145,6 +146,7 @@
     /**
      * @return The current tab indicator color as an 0xRRGGBB value.
      */
+    @ColorInt
     public int getTabIndicatorColor() {
         return mIndicatorColor;
     }
@@ -174,7 +176,7 @@
     }
 
     @Override
-    public void setBackgroundColor(int color) {
+    public void setBackgroundColor(@ColorInt int color) {
         super.setBackgroundColor(color);
         if (!mDrawFullUnderlineSet) {
             mDrawFullUnderline = (color & 0xFF000000) == 0;
diff --git a/v4/java/android/support/v4/view/PagerTitleStrip.java b/v4/java/android/support/v4/view/PagerTitleStrip.java
index 79c771f..cb9cd75 100644
--- a/v4/java/android/support/v4/view/PagerTitleStrip.java
+++ b/v4/java/android/support/v4/view/PagerTitleStrip.java
@@ -20,6 +20,8 @@
 import android.content.res.TypedArray;
 import android.database.DataSetObserver;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.FloatRange;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
 import android.util.TypedValue;
@@ -189,7 +191,7 @@
      *
      * @param alpha Opacity value in the range 0-1f
      */
-    public void setNonPrimaryAlpha(float alpha) {
+    public void setNonPrimaryAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
         mNonPrimaryAlpha = (int) (alpha * 255) & 0xFF;
         final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
         mPrevText.setTextColor(transparentColor);
@@ -202,7 +204,7 @@
      *
      * @param color Color hex code in 0xAARRGGBB format
      */
-    public void setTextColor(int color) {
+    public void setTextColor(@ColorInt int color) {
         mTextColor = color;
         mCurrText.setTextColor(color);
         final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
diff --git a/v4/java/android/support/v4/view/ScrollingView.java b/v4/java/android/support/v4/view/ScrollingView.java
new file mode 100644
index 0000000..4fe0507
--- /dev/null
+++ b/v4/java/android/support/v4/view/ScrollingView.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014 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;
+
+/**
+ * An interface that can be implemented by Views to provide scroll related APIs.
+ */
+public interface ScrollingView {
+    /**
+     * <p>Compute the horizontal range that the horizontal scrollbar
+     * represents.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeHorizontalScrollExtent()} and
+     * {@link #computeHorizontalScrollOffset()}.</p>
+     *
+     * <p>The default range is the drawing width of this view.</p>
+     *
+     * @return the total horizontal range represented by the horizontal
+     *         scrollbar
+     *
+     * @see #computeHorizontalScrollExtent()
+     * @see #computeHorizontalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeHorizontalScrollRange();
+
+    /**
+     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb
+     * within the horizontal range. This value is used to compute the position
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeHorizontalScrollRange()} and
+     * {@link #computeHorizontalScrollExtent()}.</p>
+     *
+     * <p>The default offset is the scroll offset of this view.</p>
+     *
+     * @return the horizontal offset of the scrollbar's thumb
+     *
+     * @see #computeHorizontalScrollRange()
+     * @see #computeHorizontalScrollExtent()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeHorizontalScrollOffset();
+
+    /**
+     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb
+     * within the horizontal range. This value is used to compute the length
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeHorizontalScrollRange()} and
+     * {@link #computeHorizontalScrollOffset()}.</p>
+     *
+     * <p>The default extent is the drawing width of this view.</p>
+     *
+     * @return the horizontal extent of the scrollbar's thumb
+     *
+     * @see #computeHorizontalScrollRange()
+     * @see #computeHorizontalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeHorizontalScrollExtent();
+
+    /**
+     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeVerticalScrollExtent()} and
+     * {@link #computeVerticalScrollOffset()}.</p>
+     *
+     * @return the total vertical range represented by the vertical scrollbar
+     *
+     * <p>The default range is the drawing height of this view.</p>
+     *
+     * @see #computeVerticalScrollExtent()
+     * @see #computeVerticalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeVerticalScrollRange();
+
+    /**
+     * <p>Compute the vertical offset of the vertical scrollbar's thumb
+     * within the horizontal range. This value is used to compute the position
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeVerticalScrollRange()} and
+     * {@link #computeVerticalScrollExtent()}.</p>
+     *
+     * <p>The default offset is the scroll offset of this view.</p>
+     *
+     * @return the vertical offset of the scrollbar's thumb
+     *
+     * @see #computeVerticalScrollRange()
+     * @see #computeVerticalScrollExtent()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeVerticalScrollOffset();
+
+    /**
+     * <p>Compute the vertical extent of the vertical scrollbar's thumb
+     * within the vertical range. This value is used to compute the length
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeVerticalScrollRange()} and
+     * {@link #computeVerticalScrollOffset()}.</p>
+     *
+     * <p>The default extent is the drawing height of this view.</p>
+     *
+     * @return the vertical extent of the scrollbar's thumb
+     *
+     * @see #computeVerticalScrollRange()
+     * @see #computeVerticalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeVerticalScrollExtent();
+}
diff --git a/v4/java/android/support/v4/view/ViewCompat.java b/v4/java/android/support/v4/view/ViewCompat.java
index aaabf04..633c37e 100644
--- a/v4/java/android/support/v4/view/ViewCompat.java
+++ b/v4/java/android/support/v4/view/ViewCompat.java
@@ -16,18 +16,24 @@
 
 package android.support.v4.view;
 
+import android.content.res.ColorStateList;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.support.annotation.FloatRange;
 import android.support.annotation.IdRes;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
 import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
@@ -254,6 +260,21 @@
      */
     public static final int MEASURED_STATE_TOO_SMALL = 0x01000000;
 
+    /**
+     * Indicates no axis of view scrolling.
+     */
+    public static final int SCROLL_AXIS_NONE = 0;
+
+    /**
+     * Indicates scrolling along the horizontal axis.
+     */
+    public static final int SCROLL_AXIS_HORIZONTAL = 1 << 0;
+
+    /**
+     * Indicates scrolling along the vertical axis.
+     */
+    public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
+
     interface ViewCompatImpl {
         public boolean canScrollHorizontally(View v, int direction);
         public boolean canScrollVertically(View v, int direction);
@@ -335,8 +356,29 @@
         void setFitsSystemWindows(View view, boolean fitSystemWindows);
         void jumpDrawablesToCurrentState(View v);
         void setOnApplyWindowInsetsListener(View view, OnApplyWindowInsetsListener listener);
+        WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets);
+        WindowInsetsCompat dispatchApplyWindowInsets(View v, WindowInsetsCompat insets);
         void setSaveFromParentEnabled(View view, boolean enabled);
         void setActivated(View view, boolean activated);
+        boolean isPaddingRelative(View view);
+        ColorStateList getBackgroundTintList(View view);
+        void setBackgroundTintList(View view, ColorStateList tintList);
+        PorterDuff.Mode getBackgroundTintMode(View view);
+        void setBackgroundTintMode(View view, PorterDuff.Mode mode);
+        void setNestedScrollingEnabled(View view, boolean enabled);
+        boolean isNestedScrollingEnabled(View view);
+        boolean startNestedScroll(View view, int axes);
+        void stopNestedScroll(View view);
+        boolean hasNestedScrollingParent(View view);
+        boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed, int dxUnconsumed,
+                int dyUnconsumed, int[] offsetInWindow);
+        boolean dispatchNestedPreScroll(View view, int dx, int dy, int[] consumed,
+                int[] offsetInWindow);
+        boolean dispatchNestedFling(View view, float velocityX, float velocityY, boolean consumed);
+        boolean dispatchNestedPreFling(View view, float velocityX, float velocityY);
+        boolean isLaidOut(View view);
+        int combineMeasuredStates(int curState, int newState);
+        public float getZ(View view);
     }
 
     static class BaseViewCompatImpl implements ViewCompatImpl {
@@ -347,10 +389,12 @@
 
 
         public boolean canScrollHorizontally(View v, int direction) {
-            return false;
+            return (v instanceof ScrollingView) &&
+                canScrollingViewScrollHorizontally((ScrollingView) v, direction);
         }
         public boolean canScrollVertically(View v, int direction) {
-            return false;
+            return (v instanceof ScrollingView) &&
+                    canScrollingViewScrollVertically((ScrollingView) v, direction);
         }
         public int getOverScrollMode(View v) {
             return OVER_SCROLL_NEVER;
@@ -739,6 +783,16 @@
         }
 
         @Override
+        public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
+            return insets;
+        }
+
+        @Override
+        public WindowInsetsCompat dispatchApplyWindowInsets(View v, WindowInsetsCompat insets) {
+            return insets;
+        }
+
+        @Override
         public void setSaveFromParentEnabled(View v, boolean enabled) {
             // noop
         }
@@ -747,6 +801,144 @@
         public void setActivated(View view, boolean activated) {
             // noop
         }
+
+        @Override
+        public boolean isPaddingRelative(View view) {
+            return false;
+        }
+
+        public void setNestedScrollingEnabled(View view, boolean enabled) {
+            if (view instanceof NestedScrollingChild) {
+                ((NestedScrollingChild) view).setNestedScrollingEnabled(enabled);
+            }
+        }
+
+        @Override
+        public boolean isNestedScrollingEnabled(View view) {
+            if (view instanceof NestedScrollingChild) {
+                return ((NestedScrollingChild) view).isNestedScrollingEnabled();
+            }
+            return false;
+        }
+
+        @Override
+        public ColorStateList getBackgroundTintList(View view) {
+            return ViewCompatBase.getBackgroundTintList(view);
+        }
+
+        @Override
+        public void setBackgroundTintList(View view, ColorStateList tintList) {
+            ViewCompatBase.setBackgroundTintList(view, tintList);
+        }
+
+        @Override
+        public void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
+            ViewCompatBase.setBackgroundTintMode(view, mode);
+        }
+
+        @Override
+        public PorterDuff.Mode getBackgroundTintMode(View view) {
+            return ViewCompatBase.getBackgroundTintMode(view);
+        }
+
+        private boolean canScrollingViewScrollHorizontally(ScrollingView view, int direction) {
+            final int offset = view.computeHorizontalScrollOffset();
+            final int range = view.computeHorizontalScrollRange() -
+                    view.computeHorizontalScrollExtent();
+            if (range == 0) return false;
+            if (direction < 0) {
+                return offset > 0;
+            } else {
+                return offset < range - 1;
+            }
+        }
+
+        private boolean canScrollingViewScrollVertically(ScrollingView view, int direction) {
+            final int offset = view.computeVerticalScrollOffset();
+            final int range = view.computeVerticalScrollRange() -
+                    view.computeVerticalScrollExtent();
+            if (range == 0) return false;
+            if (direction < 0) {
+                return offset > 0;
+            } else {
+                return offset < range - 1;
+            }
+        }
+
+        public boolean startNestedScroll(View view, int axes) {
+            if (view instanceof NestedScrollingChild) {
+                return ((NestedScrollingChild) view).startNestedScroll(axes);
+            }
+            return false;
+        }
+
+        @Override
+        public void stopNestedScroll(View view) {
+            if (view instanceof NestedScrollingChild) {
+                ((NestedScrollingChild) view).stopNestedScroll();
+            }
+        }
+
+        @Override
+        public boolean hasNestedScrollingParent(View view) {
+            if (view instanceof NestedScrollingChild) {
+                return ((NestedScrollingChild) view).hasNestedScrollingParent();
+            }
+            return false;
+        }
+
+        @Override
+        public boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
+            if (view instanceof NestedScrollingChild) {
+                return ((NestedScrollingChild) view).dispatchNestedScroll(dxConsumed, dyConsumed,
+                        dxUnconsumed, dyUnconsumed, offsetInWindow);
+            }
+            return false;
+        }
+
+        @Override
+        public boolean dispatchNestedPreScroll(View view, int dx, int dy,
+                int[] consumed, int[] offsetInWindow) {
+            if (view instanceof NestedScrollingChild) {
+                return ((NestedScrollingChild) view).dispatchNestedPreScroll(dx, dy, consumed,
+                        offsetInWindow);
+            }
+            return false;
+        }
+
+        @Override
+        public boolean dispatchNestedFling(View view, float velocityX, float velocityY,
+                boolean consumed) {
+            if (view instanceof NestedScrollingChild) {
+                return ((NestedScrollingChild) view).dispatchNestedFling(velocityX, velocityY,
+                        consumed);
+            }
+            return false;
+        }
+
+        @Override
+        public boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
+            if (view instanceof NestedScrollingChild) {
+                return ((NestedScrollingChild) view).dispatchNestedPreFling(velocityX, velocityY);
+            }
+            return false;
+        }
+
+        @Override
+        public boolean isLaidOut(View view) {
+            return ViewCompatBase.isLaidOut(view);
+        }
+
+        @Override
+        public int combineMeasuredStates(int curState, int newState) {
+            return curState | newState;
+        }
+
+        @Override
+        public float getZ(View view) {
+            return getTranslationZ(view) + getElevation(view);
+        }
     }
 
     static class EclairMr1ViewCompatImpl extends BaseViewCompatImpl {
@@ -926,6 +1118,11 @@
         public void setActivated(View view, boolean activated) {
             ViewCompatHC.setActivated(view, activated);
         }
+
+        @Override
+        public int combineMeasuredStates(int curState, int newState) {
+            return ViewCompatHC.combineMeasuredStates(curState, newState);
+        }
     }
 
     static class ICSViewCompatImpl extends HCViewCompatImpl {
@@ -1125,6 +1322,11 @@
         public int getWindowSystemUiVisibility(View view) {
             return ViewCompatJellybeanMr1.getWindowSystemUiVisibility(view);
         }
+
+        @Override
+        public boolean isPaddingRelative(View view) {
+            return ViewCompatJellybeanMr1.isPaddingRelative(view);
+        }
     }
 
     static class KitKatViewCompatImpl extends JbMr1ViewCompatImpl {
@@ -1142,52 +1344,142 @@
         public void setImportantForAccessibility(View view, int mode) {
             ViewCompatJB.setImportantForAccessibility(view, mode);
         }
+
+        @Override
+        public boolean isLaidOut(View view) {
+            return ViewCompatKitKat.isLaidOut(view);
+        }
     }
 
-    static class Api21ViewCompatImpl extends KitKatViewCompatImpl {
+    static class LollipopViewCompatImpl extends KitKatViewCompatImpl {
         @Override
         public void setTransitionName(View view, String transitionName) {
-            ViewCompatApi21.setTransitionName(view, transitionName);
+            ViewCompatLollipop.setTransitionName(view, transitionName);
         }
 
         @Override
         public String getTransitionName(View view) {
-            return ViewCompatApi21.getTransitionName(view);
+            return ViewCompatLollipop.getTransitionName(view);
         }
 
         @Override
         public void requestApplyInsets(View view) {
-            ViewCompatApi21.requestApplyInsets(view);
+            ViewCompatLollipop.requestApplyInsets(view);
         }
 
         @Override
         public void setElevation(View view, float elevation) {
-            ViewCompatApi21.setElevation(view, elevation);
+            ViewCompatLollipop.setElevation(view, elevation);
         }
 
         @Override
         public float getElevation(View view) {
-            return ViewCompatApi21.getElevation(view);
+            return ViewCompatLollipop.getElevation(view);
         }
 
         @Override
         public void setTranslationZ(View view, float translationZ) {
-            ViewCompatApi21.setTranslationZ(view, translationZ);
+            ViewCompatLollipop.setTranslationZ(view, translationZ);
         }
 
         @Override
         public float getTranslationZ(View view) {
-            return ViewCompatApi21.getTranslationZ(view);
+            return ViewCompatLollipop.getTranslationZ(view);
         }
 
         @Override
         public void setOnApplyWindowInsetsListener(View view, OnApplyWindowInsetsListener listener) {
-            ViewCompatApi21.setOnApplyWindowInsetsListener(view, listener);
+            ViewCompatLollipop.setOnApplyWindowInsetsListener(view, listener);
+        }
+
+        @Override
+        public void setNestedScrollingEnabled(View view, boolean enabled) {
+            ViewCompatLollipop.setNestedScrollingEnabled(view, enabled);
+        }
+
+        @Override
+        public boolean isNestedScrollingEnabled(View view) {
+            return ViewCompatLollipop.isNestedScrollingEnabled(view);
+        }
+
+        @Override
+        public boolean startNestedScroll(View view, int axes) {
+            return ViewCompatLollipop.startNestedScroll(view, axes);
+        }
+
+        @Override
+        public void stopNestedScroll(View view) {
+            ViewCompatLollipop.stopNestedScroll(view);
+        }
+
+        @Override
+        public boolean hasNestedScrollingParent(View view) {
+            return ViewCompatLollipop.hasNestedScrollingParent(view);
+        }
+
+        @Override
+        public boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
+            return ViewCompatLollipop.dispatchNestedScroll(view, dxConsumed, dyConsumed,
+                    dxUnconsumed, dyUnconsumed, offsetInWindow);
+        }
+
+        @Override
+        public boolean dispatchNestedPreScroll(View view, int dx, int dy,
+                int[] consumed, int[] offsetInWindow) {
+            return ViewCompatLollipop.dispatchNestedPreScroll(view, dx, dy, consumed,
+                    offsetInWindow);
+        }
+
+        @Override
+        public boolean dispatchNestedFling(View view, float velocityX, float velocityY,
+                boolean consumed) {
+            return ViewCompatLollipop.dispatchNestedFling(view, velocityX, velocityY, consumed);
+        }
+
+        @Override
+        public boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
+            return ViewCompatLollipop.dispatchNestedPreFling(view, velocityX, velocityY);
         }
 
         @Override
         public boolean isImportantForAccessibility(View view) {
-            return ViewCompatApi21.isImportantForAccessibility(view);
+            return ViewCompatLollipop.isImportantForAccessibility(view);
+        }
+
+        @Override
+        public ColorStateList getBackgroundTintList(View view) {
+            return ViewCompatLollipop.getBackgroundTintList(view);
+        }
+
+        @Override
+        public void setBackgroundTintList(View view, ColorStateList tintList) {
+            ViewCompatLollipop.setBackgroundTintList(view, tintList);
+        }
+
+        @Override
+        public void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
+            ViewCompatLollipop.setBackgroundTintMode(view, mode);
+        }
+
+        @Override
+        public PorterDuff.Mode getBackgroundTintMode(View view) {
+            return ViewCompatLollipop.getBackgroundTintMode(view);
+        }
+
+        @Override
+        public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
+            return ViewCompatLollipop.onApplyWindowInsets(v, insets);
+        }
+
+        @Override
+        public WindowInsetsCompat dispatchApplyWindowInsets(View v, WindowInsetsCompat insets) {
+            return ViewCompatLollipop.dispatchApplyWindowInsets(v, insets);
+        }
+
+        @Override
+        public float getZ(View view) {
+            return ViewCompatLollipop.getZ(view);
         }
     }
 
@@ -1195,7 +1487,7 @@
     static {
         final int version = android.os.Build.VERSION.SDK_INT;
         if (version >= 21) {
-            IMPL = new Api21ViewCompatImpl();
+            IMPL = new LollipopViewCompatImpl();
         } else if (version >= 19) {
             IMPL = new KitKatViewCompatImpl();
         } else if (version >= 17) {
@@ -1818,6 +2110,18 @@
     }
 
     /**
+     * Merge two states as returned by {@link #getMeasuredState(View)}.
+     * @param curState The current state as returned from a view or the result
+     * of combining multiple views.
+     * @param newState The new view state to combine.
+     * @return Returns a new integer reflecting the combination of the two
+     * states.
+     */
+    public static int combineMeasuredStates(int curState, int newState) {
+        return IMPL.combineMeasuredStates(curState, newState);
+    }
+
+    /**
      * Gets the live region mode for the specified View.
      *
      * @param view The view from which to obtain the live region mode
@@ -2019,7 +2323,7 @@
      *
      * @param value The opacity of the view.
      */
-    public static void setAlpha(View view, float value) {
+    public static void setAlpha(View view, @FloatRange(from=0.0, to=1.0) float value) {
         IMPL.setAlpha(view, value);
     }
 
@@ -2318,6 +2622,39 @@
     }
 
     /**
+     * Called when the view should apply {@link WindowInsetsCompat} according to its internal policy.
+     *
+     * <p>Clients may supply an {@link OnApplyWindowInsetsListener} to a view. If one is set
+     * it will be called during dispatch instead of this method. The listener may optionally
+     * call this method from its own implementation if it wishes to apply the view's default
+     * insets policy in addition to its own.</p>
+     *
+     * @param view The View against which to invoke the method.
+     * @param insets Insets to apply
+     * @return The supplied insets with any applied insets consumed
+     */
+    public static WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
+        return IMPL.onApplyWindowInsets(view, insets);
+    }
+
+    /**
+     * Request to apply the given window insets to this view or another view in its subtree.
+     *
+     * <p>This method should be called by clients wishing to apply insets corresponding to areas
+     * obscured by window decorations or overlays. This can include the status and navigation bars,
+     * action bars, input methods and more. New inset categories may be added in the future.
+     * The method returns the insets provided minus any that were applied by this view or its
+     * children.</p>
+     *
+     * @param insets Insets to apply
+     * @return The provided insets minus the insets that were consumed
+     */
+    public static WindowInsetsCompat dispatchApplyWindowInsets(View view,
+            WindowInsetsCompat insets) {
+        return IMPL.dispatchApplyWindowInsets(view, insets);
+    }
+
+    /**
      * Controls whether the entire hierarchy under this view will save its
      * state when a state saving traversal occurs from its parent.
      *
@@ -2341,5 +2678,286 @@
         IMPL.setActivated(view, activated);
     }
 
+    /**
+     * Return if the padding as been set through relative values
+     * {@code View.setPaddingRelative(int, int, int, int)} or thru
+     *
+     * @return true if the padding is relative or false if it is not.
+     */
+    public static boolean isPaddingRelative(View view) {
+        return IMPL.isPaddingRelative(view);
+    }
+
+    /**
+     * Return the tint applied to the background drawable, if specified.
+     * <p>
+     * Only returns meaningful info when running on API v21 or newer, or if {@code view}
+     * implements the {@code TintableBackgroundView} interface.
+     */
+    public static ColorStateList getBackgroundTintList(View view) {
+        return IMPL.getBackgroundTintList(view);
+    }
+
+    /**
+     * Applies a tint to the background drawable.
+     * <p>
+     * This will always take effect when running on API v21 or newer. When running on platforms
+     * previous to API v21, it will only take effect if {@code view} implement the
+     * {@code TintableBackgroundView} interface.
+     */
+    public static void setBackgroundTintList(View view, ColorStateList tintList) {
+        IMPL.setBackgroundTintList(view, tintList);
+    }
+
+    /**
+     * Return the blending mode used to apply the tint to the background
+     * drawable, if specified.
+     * <p>
+     * Only returns meaningful info when running on API v21 or newer, or if {@code view}
+     * implements the {@code TintableBackgroundView} interface.
+     */
+    public static PorterDuff.Mode getBackgroundTintMode(View view) {
+        return IMPL.getBackgroundTintMode(view);
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setBackgroundTintList(android.view.View, android.content.res.ColorStateList)} to
+     * the background drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
+     * <p>
+     * This will always take effect when running on API v21 or newer. When running on platforms
+     * previous to API v21, it will only take effect if {@code view} implement the
+     * {@code TintableBackgroundView} interface.
+     */
+    public static void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
+        IMPL.setBackgroundTintMode(view, mode);
+    }
     // TODO: getters for various view properties (rotation, etc)
+
+    /**
+     * Enable or disable nested scrolling for this view.
+     *
+     * <p>If this property is set to true the view will be permitted to initiate nested
+     * scrolling operations with a compatible parent view in the current hierarchy. If this
+     * view does not implement nested scrolling this will have no effect. Disabling nested scrolling
+     * while a nested scroll is in progress has the effect of
+     * {@link #stopNestedScroll(View) stopping} the nested scroll.</p>
+     *
+     * @param enabled true to enable nested scrolling, false to disable
+     *
+     * @see #isNestedScrollingEnabled(View)
+     */
+    public static void setNestedScrollingEnabled(View view, boolean enabled) {
+        IMPL.setNestedScrollingEnabled(view, enabled);
+    }
+
+    /**
+     * Returns true if nested scrolling is enabled for this view.
+     *
+     * <p>If nested scrolling is enabled and this View class implementation supports it,
+     * this view will act as a nested scrolling child view when applicable, forwarding data
+     * about the scroll operation in progress to a compatible and cooperating nested scrolling
+     * parent.</p>
+     *
+     * @return true if nested scrolling is enabled
+     *
+     * @see #setNestedScrollingEnabled(View, boolean)
+     */
+    public static boolean isNestedScrollingEnabled(View view) {
+        return IMPL.isNestedScrollingEnabled(view);
+    }
+
+    /**
+     * Begin a nestable scroll operation along the given axes.
+     *
+     * <p>A view starting a nested scroll promises to abide by the following contract:</p>
+     *
+     * <p>The view will call startNestedScroll upon initiating a scroll operation. In the case
+     * of a touch scroll this corresponds to the initial {@link MotionEvent#ACTION_DOWN}.
+     * In the case of touch scrolling the nested scroll will be terminated automatically in
+     * the same manner as {@link ViewParent#requestDisallowInterceptTouchEvent(boolean)}.
+     * In the event of programmatic scrolling the caller must explicitly call
+     * {@link #stopNestedScroll(View)} to indicate the end of the nested scroll.</p>
+     *
+     * <p>If <code>startNestedScroll</code> returns true, a cooperative parent was found.
+     * If it returns false the caller may ignore the rest of this contract until the next scroll.
+     * Calling startNestedScroll while a nested scroll is already in progress will return true.</p>
+     *
+     * <p>At each incremental step of the scroll the caller should invoke
+     * {@link #dispatchNestedPreScroll(View, int, int, int[], int[]) dispatchNestedPreScroll}
+     * once it has calculated the requested scrolling delta. If it returns true the nested scrolling
+     * parent at least partially consumed the scroll and the caller should adjust the amount it
+     * scrolls by.</p>
+     *
+     * <p>After applying the remainder of the scroll delta the caller should invoke
+     * {@link #dispatchNestedScroll(View, int, int, int, int, int[]) dispatchNestedScroll}, passing
+     * both the delta consumed and the delta unconsumed. A nested scrolling parent may treat
+     * these values differently. See
+     * {@link NestedScrollingParent#onNestedScroll(View, int, int, int, int)}.
+     * </p>
+     *
+     * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}
+     *             and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}.
+     * @return true if a cooperative parent was found and nested scrolling has been enabled for
+     *         the current gesture.
+     *
+     * @see #stopNestedScroll(View)
+     * @see #dispatchNestedPreScroll(View, int, int, int[], int[])
+     * @see #dispatchNestedScroll(View, int, int, int, int, int[])
+     */
+    public static boolean startNestedScroll(View view, int axes) {
+        return IMPL.startNestedScroll(view, axes);
+    }
+
+    /**
+     * Stop a nested scroll in progress.
+     *
+     * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p>
+     *
+     * @see #startNestedScroll(View, int)
+     */
+    public static void stopNestedScroll(View view) {
+        IMPL.stopNestedScroll(view);
+    }
+
+    /**
+     * Returns true if this view has a nested scrolling parent.
+     *
+     * <p>The presence of a nested scrolling parent indicates that this view has initiated
+     * a nested scroll and it was accepted by an ancestor view further up the view hierarchy.</p>
+     *
+     * @return whether this view has a nested scrolling parent
+     */
+    public static boolean hasNestedScrollingParent(View view) {
+        return IMPL.hasNestedScrollingParent(view);
+    }
+
+    /**
+     * Dispatch one step of a nested scroll in progress.
+     *
+     * <p>Implementations of views that support nested scrolling should call this to report
+     * info about a scroll in progress to the current nested scrolling parent. If a nested scroll
+     * is not currently in progress or nested scrolling is not
+     * {@link #isNestedScrollingEnabled(View) enabled} for this view this method does nothing.</p>
+     *
+     * <p>Compatible View implementations should also call
+     * {@link #dispatchNestedPreScroll(View, int, int, int[], int[]) dispatchNestedPreScroll} before
+     * consuming a component of the scroll event themselves.</p>
+     *
+     * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step
+     * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step
+     * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view
+     * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view
+     * @param offsetInWindow Optional. If not null, on return this will contain the offset
+     *                       in local view coordinates of this view from before this operation
+     *                       to after it completes. View implementations may use this to adjust
+     *                       expected input coordinate tracking.
+     * @return true if the event was dispatched, false if it could not be dispatched.
+     * @see #dispatchNestedPreScroll(View, int, int, int[], int[])
+     */
+    public static boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
+        return IMPL.dispatchNestedScroll(view, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                offsetInWindow);
+    }
+
+    /**
+     * Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
+     *
+     * <p>Nested pre-scroll events are to nested scroll events what touch intercept is to touch.
+     * <code>dispatchNestedPreScroll</code> offers an opportunity for the parent view in a nested
+     * scrolling operation to consume some or all of the scroll operation before the child view
+     * consumes it.</p>
+     *
+     * @param dx Horizontal scroll distance in pixels
+     * @param dy Vertical scroll distance in pixels
+     * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx
+     *                 and consumed[1] the consumed dy.
+     * @param offsetInWindow Optional. If not null, on return this will contain the offset
+     *                       in local view coordinates of this view from before this operation
+     *                       to after it completes. View implementations may use this to adjust
+     *                       expected input coordinate tracking.
+     * @return true if the parent consumed some or all of the scroll delta
+     * @see #dispatchNestedScroll(View, int, int, int, int, int[])
+     */
+    public static boolean dispatchNestedPreScroll(View view, int dx, int dy, int[] consumed,
+            int[] offsetInWindow) {
+        return IMPL.dispatchNestedPreScroll(view, dx, dy, consumed, offsetInWindow);
+    }
+
+    /**
+     * Dispatch a fling to a nested scrolling parent.
+     *
+     * <p>This method should be used to indicate that a nested scrolling child has detected
+     * suitable conditions for a fling. Generally this means that a touch scroll has ended with a
+     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+     * along a scrollable axis.</p>
+     *
+     * <p>If a nested scrolling child view would normally fling but it is at the edge of
+     * its own content, it can use this method to delegate the fling to its nested scrolling
+     * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
+     *
+     * @param velocityX Horizontal fling velocity in pixels per second
+     * @param velocityY Vertical fling velocity in pixels per second
+     * @param consumed true if the child consumed the fling, false otherwise
+     * @return true if the nested scrolling parent consumed or otherwise reacted to the fling
+     */
+    public static boolean dispatchNestedFling(View view, float velocityX, float velocityY,
+            boolean consumed) {
+        return IMPL.dispatchNestedFling(view, velocityX, velocityY, consumed);
+    }
+
+    /**
+     * Dispatch a fling to a nested scrolling parent before it is processed by this view.
+     *
+     * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
+     * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
+     * offsets an opportunity for the parent view in a nested fling to fully consume the fling
+     * before the child view consumes it. If this method returns <code>true</code>, a nested
+     * parent view consumed the fling and this view should not scroll as a result.</p>
+     *
+     * <p>For a better user experience, only one view in a nested scrolling chain should consume
+     * the fling at a time. If a parent view consumed the fling this method will return false.
+     * Custom view implementations should account for this in two ways:</p>
+     *
+     * <ul>
+     *     <li>If a custom view is paged and needs to settle to a fixed page-point, do not
+     *     call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
+     *     position regardless.</li>
+     *     <li>If a nested parent does consume the fling, this view should not scroll at all,
+     *     even to settle back to a valid idle position.</li>
+     * </ul>
+     *
+     * <p>Views should also not offer fling velocities to nested parent views along an axis
+     * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
+     * should not offer a horizontal fling velocity to its parents since scrolling along that
+     * axis is not permitted and carrying velocity along that motion does not make sense.</p>
+     *
+     * @param velocityX Horizontal fling velocity in pixels per second
+     * @param velocityY Vertical fling velocity in pixels per second
+     * @return true if a nested scrolling parent consumed the fling
+     */
+    public static boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
+        return IMPL.dispatchNestedPreFling(view, velocityX, velocityY);
+    }
+
+    /**
+     * Returns true if {@code view} has been through at least one layout since it
+     * was last attached to or detached from a window.
+     */
+    public static boolean isLaidOut(View view) {
+        return IMPL.isLaidOut(view);
+    }
+
+    /**
+     * The visual z position of this view, in pixels. This is equivalent to the
+     * {@link #setTranslationZ(View, float) translationZ} property plus the current
+     * {@link #getElevation(View) elevation} property.
+     *
+     * @return The visual z position of this view, in pixels.
+     */
+    public static float getZ(View view) {
+        return IMPL.getZ(view);
+    }
 }
diff --git a/v4/java/android/support/v4/view/ViewGroupCompat.java b/v4/java/android/support/v4/view/ViewGroupCompat.java
index 9bf7046..2254c4a 100644
--- a/v4/java/android/support/v4/view/ViewGroupCompat.java
+++ b/v4/java/android/support/v4/view/ViewGroupCompat.java
@@ -44,14 +44,14 @@
     public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
 
     interface ViewGroupCompatImpl {
-        public boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,
+        boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,
                 AccessibilityEvent event);
-
-        public void setMotionEventSplittingEnabled(ViewGroup group, boolean split);
-        public int getLayoutMode(ViewGroup group);
-        public void setLayoutMode(ViewGroup group, int mode);
-        public void setTransitionGroup(ViewGroup group, boolean isTransitionGroup);
-        public boolean isTransitionGroup(ViewGroup group);
+        void setMotionEventSplittingEnabled(ViewGroup group, boolean split);
+        int getLayoutMode(ViewGroup group);
+        void setLayoutMode(ViewGroup group, int mode);
+        void setTransitionGroup(ViewGroup group, boolean isTransitionGroup);
+        boolean isTransitionGroup(ViewGroup group);
+        int getNestedScrollAxes(ViewGroup group);
     }
 
     static class ViewGroupCompatStubImpl implements ViewGroupCompatImpl {
@@ -82,6 +82,14 @@
         public boolean isTransitionGroup(ViewGroup group) {
             return false;
         }
+
+        @Override
+        public int getNestedScrollAxes(ViewGroup group) {
+            if (group instanceof NestedScrollingParent) {
+                return ((NestedScrollingParent) group).getNestedScrollAxes();
+            }
+            return ViewCompat.SCROLL_AXIS_NONE;
+        }
     }
 
     static class ViewGroupCompatHCImpl extends ViewGroupCompatStubImpl {
@@ -111,15 +119,20 @@
         }
     }
 
-    static class ViewGroupCompatApi21Impl extends ViewGroupCompatJellybeanMR2Impl {
+    static class ViewGroupCompatLollipopImpl extends ViewGroupCompatJellybeanMR2Impl {
         @Override
         public void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-            ViewGroupCompatApi21.setTransitionGroup(group, isTransitionGroup);
+            ViewGroupCompatLollipop.setTransitionGroup(group, isTransitionGroup);
         }
 
         @Override
         public boolean isTransitionGroup(ViewGroup group) {
-            return ViewGroupCompatApi21.isTransitionGroup(group);
+            return ViewGroupCompatLollipop.isTransitionGroup(group);
+        }
+
+        @Override
+        public int getNestedScrollAxes(ViewGroup group) {
+            return ViewGroupCompatLollipop.getNestedScrollAxes(group);
         }
     }
 
@@ -127,7 +140,7 @@
     static {
         final int version = Build.VERSION.SDK_INT;
         if (version >= 21) {
-            IMPL = new ViewGroupCompatApi21Impl();
+            IMPL = new ViewGroupCompatLollipopImpl();
         } else if (version >= 18) {
             IMPL = new ViewGroupCompatJellybeanMR2Impl();
         } else if (version >= 14) {
@@ -235,4 +248,20 @@
     public static boolean isTransitionGroup(ViewGroup group) {
         return IMPL.isTransitionGroup(group);
     }
+
+    /**
+     * Return the current axes of nested scrolling for this ViewGroup.
+     *
+     * <p>A ViewGroup returning something other than {@link ViewCompat#SCROLL_AXIS_NONE} is
+     * currently acting as a nested scrolling parent for one or more descendant views in
+     * the hierarchy.</p>
+     *
+     * @return Flags indicating the current axes of nested scrolling
+     * @see ViewCompat#SCROLL_AXIS_HORIZONTAL
+     * @see ViewCompat#SCROLL_AXIS_VERTICAL
+     * @see ViewCompat#SCROLL_AXIS_NONE
+     */
+    public static int getNestedScrollAxes(ViewGroup group) {
+        return IMPL.getNestedScrollAxes(group);
+    }
 }
diff --git a/v4/java/android/support/v4/view/ViewPager.java b/v4/java/android/support/v4/view/ViewPager.java
index 163649a..dc20cb6 100644
--- a/v4/java/android/support/v4/view/ViewPager.java
+++ b/v4/java/android/support/v4/view/ViewPager.java
@@ -28,6 +28,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.support.annotation.CallSuper;
 import android.support.annotation.DrawableRes;
 import android.support.v4.os.ParcelableCompat;
 import android.support.v4.os.ParcelableCompatCreatorCallbacks;
@@ -167,7 +168,6 @@
 
     private boolean mIsBeingDragged;
     private boolean mIsUnableToDrag;
-    private boolean mIgnoreGutter;
     private int mDefaultGutterSize;
     private int mGutterSize;
     private int mTouchSlop;
@@ -1672,6 +1672,7 @@
      * @param offset Value from [0, 1) indicating the offset from the page at position.
      * @param offsetPixels Value in pixels indicating the offset from position.
      */
+    @CallSuper
     protected void onPageScrolled(int position, float offset, int offsetPixels) {
         // Offset any decor views if needed - keep them on-screen at all times.
         if (mDecorChildCount > 0) {
@@ -1749,6 +1750,9 @@
             int y = mScroller.getCurrY();
             if (oldX != x || oldY != y) {
                 scrollTo(x, y);
+                if (x != oldX) {
+                    pageScrolled(x);
+                }
             }
         }
         mPopulatePending = false;
diff --git a/v4/java/android/support/v4/view/ViewParentCompat.java b/v4/java/android/support/v4/view/ViewParentCompat.java
index 87cbb9b..b54a358 100644
--- a/v4/java/android/support/v4/view/ViewParentCompat.java
+++ b/v4/java/android/support/v4/view/ViewParentCompat.java
@@ -18,7 +18,10 @@
 
 import android.content.Context;
 import android.os.Build;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -32,6 +35,17 @@
     interface ViewParentCompatImpl {
         public boolean requestSendAccessibilityEvent(
                 ViewParent parent, View child, AccessibilityEvent event);
+        boolean onStartNestedScroll(ViewParent parent, View child, View target,
+                int nestedScrollAxes);
+        void onNestedScrollAccepted(ViewParent parent, View child, View target,
+                int nestedScrollAxes);
+        void onStopNestedScroll(ViewParent parent, View target);
+        void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed);
+        void onNestedPreScroll(ViewParent parent, View target, int dx, int dy, int[] consumed);
+        boolean onNestedFling(ViewParent parent, View target, float velocityX, float velocityY,
+                boolean consumed);
+        boolean onNestedPreFling(ViewParent parent, View target, float velocityX, float velocityY);
     }
 
     static class ViewParentCompatStubImpl implements ViewParentCompatImpl {
@@ -47,6 +61,69 @@
             manager.sendAccessibilityEvent(event);
             return true;
         }
+
+        @Override
+        public boolean onStartNestedScroll(ViewParent parent, View child, View target,
+                int nestedScrollAxes) {
+            if (parent instanceof NestedScrollingParent) {
+                return ((NestedScrollingParent) parent).onStartNestedScroll(child, target,
+                        nestedScrollAxes);
+            }
+            return false;
+        }
+
+        @Override
+        public void onNestedScrollAccepted(ViewParent parent, View child, View target,
+                int nestedScrollAxes) {
+            if (parent instanceof NestedScrollingParent) {
+                ((NestedScrollingParent) parent).onNestedScrollAccepted(child, target,
+                        nestedScrollAxes);
+            }
+        }
+
+        @Override
+        public void onStopNestedScroll(ViewParent parent, View target) {
+            if (parent instanceof NestedScrollingParent) {
+                ((NestedScrollingParent) parent).onStopNestedScroll(target);
+            }
+        }
+
+        @Override
+        public void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed) {
+            if (parent instanceof NestedScrollingParent) {
+                ((NestedScrollingParent) parent).onNestedScroll(target, dxConsumed, dyConsumed,
+                        dxUnconsumed, dyUnconsumed);
+            }
+        }
+
+        @Override
+        public void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
+                int[] consumed) {
+            if (parent instanceof NestedScrollingParent) {
+                ((NestedScrollingParent) parent).onNestedPreScroll(target, dx, dy, consumed);
+            }
+        }
+
+        @Override
+        public boolean onNestedFling(ViewParent parent, View target, float velocityX,
+                float velocityY, boolean consumed) {
+            if (parent instanceof NestedScrollingParent) {
+                return ((NestedScrollingParent) parent).onNestedFling(target, velocityX, velocityY,
+                        consumed);
+            }
+            return false;
+        }
+
+        @Override
+        public boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
+                float velocityY) {
+            if (parent instanceof NestedScrollingParent) {
+                return ((NestedScrollingParent) parent).onNestedPreFling(target, velocityX,
+                        velocityY);
+            }
+            return false;
+        }
     }
 
     static class ViewParentCompatICSImpl extends ViewParentCompatStubImpl {
@@ -57,10 +134,59 @@
         }
     }
 
+    static class ViewParentCompatLollipopImpl extends ViewParentCompatICSImpl {
+        @Override
+        public boolean onStartNestedScroll(ViewParent parent, View child, View target,
+                int nestedScrollAxes) {
+            return ViewParentCompatLollipop.onStartNestedScroll(parent, child, target,
+                    nestedScrollAxes);
+        }
+
+        @Override
+        public void onNestedScrollAccepted(ViewParent parent, View child, View target,
+                int nestedScrollAxes) {
+            ViewParentCompatLollipop.onNestedScrollAccepted(parent, child, target,
+                    nestedScrollAxes);
+        }
+
+        @Override
+        public void onStopNestedScroll(ViewParent parent, View target) {
+            ViewParentCompatLollipop.onStopNestedScroll(parent, target);
+        }
+
+        @Override
+        public void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed) {
+            ViewParentCompatLollipop.onNestedScroll(parent, target, dxConsumed, dyConsumed,
+                    dxUnconsumed, dyUnconsumed);
+        }
+
+        @Override
+        public void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
+                int[] consumed) {
+            ViewParentCompatLollipop.onNestedPreScroll(parent, target, dx, dy, consumed);
+        }
+
+        @Override
+        public boolean onNestedFling(ViewParent parent, View target, float velocityX,
+                float velocityY, boolean consumed) {
+            return ViewParentCompatLollipop.onNestedFling(parent, target, velocityX, velocityY,
+                    consumed);
+        }
+
+        @Override
+        public boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
+                float velocityY) {
+            return ViewParentCompatLollipop.onNestedPreFling(parent, target, velocityX, velocityY);
+        }
+    }
+
     static final ViewParentCompatImpl IMPL;
     static {
         final int version = Build.VERSION.SDK_INT;
-        if (version >= 14) {
+        if (version >= 21) {
+            IMPL = new ViewParentCompatLollipopImpl();
+        } else if (version >= 14) {
             IMPL = new ViewParentCompatICSImpl();
         } else {
             IMPL = new ViewParentCompatStubImpl();
@@ -95,4 +221,168 @@
             ViewParent parent, View child, AccessibilityEvent event) {
         return IMPL.requestSendAccessibilityEvent(parent, child, event);
     }
+
+    /**
+     * React to a descendant view initiating a nestable scroll operation, claiming the
+     * nested scroll operation if appropriate.
+     *
+     * <p>This method will be called in response to a descendant view invoking
+     * {@link ViewCompat#startNestedScroll(View, int)}. Each parent up the view hierarchy will be
+     * given an opportunity to respond and claim the nested scrolling operation by returning
+     * <code>true</code>.</p>
+     *
+     * <p>This method may be overridden by ViewParent implementations to indicate when the view
+     * is willing to support a nested scrolling operation that is about to begin. If it returns
+     * true, this ViewParent will become the target view's nested scrolling parent for the duration
+     * of the scroll operation in progress. When the nested scroll is finished this ViewParent
+     * will receive a call to {@link #onStopNestedScroll(ViewParent, View)}.
+     * </p>
+     *
+     * @param child Direct child of this ViewParent containing target
+     * @param target View that initiated the nested scroll
+     * @param nestedScrollAxes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
+     * @return true if this ViewParent accepts the nested scroll operation
+     */
+    public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
+            int nestedScrollAxes) {
+        return IMPL.onStartNestedScroll(parent, child, target, nestedScrollAxes);
+    }
+
+    /**
+     * React to the successful claiming of a nested scroll operation.
+     *
+     * <p>This method will be called after
+     * {@link #onStartNestedScroll(ViewParent, View, View, int) onStartNestedScroll} returns true.
+     * It offers an opportunity for the view and its superclasses to perform initial configuration
+     * for the nested scroll. Implementations of this method should always call their superclass's
+     * implementation of this method if one is present.</p>
+     *
+     * @param child Direct child of this ViewParent containing target
+     * @param target View that initiated the nested scroll
+     * @param nestedScrollAxes Flags consisting of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
+     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
+     * @see #onStartNestedScroll(ViewParent, View, View, int)
+     * @see #onStopNestedScroll(ViewParent, View)
+     */
+    public static void onNestedScrollAccepted(ViewParent parent, View child, View target,
+            int nestedScrollAxes) {
+        IMPL.onNestedScrollAccepted(parent, child, target, nestedScrollAxes);
+    }
+
+    /**
+     * React to a nested scroll operation ending.
+     *
+     * <p>Perform cleanup after a nested scrolling operation.
+     * This method will be called when a nested scroll stops, for example when a nested touch
+     * scroll ends with a {@link MotionEvent#ACTION_UP} or {@link MotionEvent#ACTION_CANCEL} event.
+     * Implementations of this method should always call their superclass's implementation of this
+     * method if one is present.</p>
+     *
+     * @param target View that initiated the nested scroll
+     */
+    public static void onStopNestedScroll(ViewParent parent, View target) {
+        IMPL.onStopNestedScroll(parent, target);
+    }
+
+    /**
+     * React to a nested scroll in progress.
+     *
+     * <p>This method will be called when the ViewParent's current nested scrolling child view
+     * dispatches a nested scroll event. To receive calls to this method the ViewParent must have
+     * previously returned <code>true</code> for a call to
+     * {@link #onStartNestedScroll(ViewParent, View, View, int)}.</p>
+     *
+     * <p>Both the consumed and unconsumed portions of the scroll distance are reported to the
+     * ViewParent. An implementation may choose to use the consumed portion to match or chase scroll
+     * position of multiple child elements, for example. The unconsumed portion may be used to
+     * allow continuous dragging of multiple scrolling or draggable elements, such as scrolling
+     * a list within a vertical drawer where the drawer begins dragging once the edge of inner
+     * scrolling content is reached.</p>
+     *
+     * @param target The descendent view controlling the nested scroll
+     * @param dxConsumed Horizontal scroll distance in pixels already consumed by target
+     * @param dyConsumed Vertical scroll distance in pixels already consumed by target
+     * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by target
+     * @param dyUnconsumed Vertical scroll distance in pixels not consumed by target
+     */
+    public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,
+            int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
+        IMPL.onNestedScroll(parent, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
+    }
+
+    /**
+     * React to a nested scroll in progress before the target view consumes a portion of the scroll.
+     *
+     * <p>When working with nested scrolling often the parent view may want an opportunity
+     * to consume the scroll before the nested scrolling child does. An example of this is a
+     * drawer that contains a scrollable list. The user will want to be able to scroll the list
+     * fully into view before the list itself begins scrolling.</p>
+     *
+     * <p><code>onNestedPreScroll</code> is called when a nested scrolling child invokes
+     * {@link ViewCompat#dispatchNestedPreScroll(View, int, int, int[], int[])}. The implementation
+     * should report how any pixels of the scroll reported by dx, dy were consumed in the
+     * <code>consumed</code> array. Index 0 corresponds to dx and index 1 corresponds to dy.
+     * This parameter will never be null. Initial values for consumed[0] and consumed[1]
+     * will always be 0.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param dx Horizontal scroll distance in pixels
+     * @param dy Vertical scroll distance in pixels
+     * @param consumed Output. The horizontal and vertical scroll distance consumed by this parent
+     */
+    public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
+            int[] consumed) {
+        IMPL.onNestedPreScroll(parent, target, dx, dy, consumed);
+    }
+
+    /**
+     * Request a fling from a nested scroll.
+     *
+     * <p>This method signifies that a nested scrolling child has detected suitable conditions
+     * for a fling. Generally this means that a touch scroll has ended with a
+     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+     * along a scrollable axis.</p>
+     *
+     * <p>If a nested scrolling child view would normally fling but it is at the edge of
+     * its own content, it can use this method to delegate the fling to its nested scrolling
+     * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param velocityX Horizontal velocity in pixels per second
+     * @param velocityY Vertical velocity in pixels per second
+     * @param consumed true if the child consumed the fling, false otherwise
+     * @return true if this parent consumed or otherwise reacted to the fling
+     */
+    public static boolean onNestedFling(ViewParent parent, View target, float velocityX,
+            float velocityY, boolean consumed) {
+        return IMPL.onNestedFling(parent, target, velocityX, velocityY, consumed);
+    }
+
+    /**
+     * React to a nested fling before the target view consumes it.
+     *
+     * <p>This method siginfies that a nested scrolling child has detected a fling with the given
+     * velocity along each axis. Generally this means that a touch scroll has ended with a
+     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+     * along a scrollable axis.</p>
+     *
+     * <p>If a nested scrolling parent is consuming motion as part of a
+     * {@link #onNestedPreScroll(ViewParent, View, int, int, int[]) pre-scroll}, it may be
+     * appropriate for it to also consume the pre-fling to complete that same motion. By returning
+     * <code>true</code> from this method, the parent indicates that the child should not
+     * fling its own internal content as well.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param velocityX Horizontal velocity in pixels per second
+     * @param velocityY Vertical velocity in pixels per second
+     * @return true if this parent consumed the fling ahead of the target view
+     */
+    public static boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
+            float velocityY) {
+        return IMPL.onNestedPreFling(parent, target, velocityX, velocityY);
+    }
+
 }
diff --git a/v4/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java b/v4/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
index 055598f..1b239a7 100644
--- a/v4/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/v4/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -279,6 +279,12 @@
         public AccessibilityNodeInfoCompat getTraversalAfter(Object info);
         public void setTraversalAfter(Object info, View view);
         public void setTraversalAfter(Object info, View root, int virtualDescendantId);
+        public void setContentInvalid(Object info, boolean contentInvalid);
+        public boolean isContentInvalid(Object info);
+        public void setError(Object info, CharSequence error);
+        public CharSequence getError(Object info);
+        public void setLabelFor(Object info, View labeled);
+        public void setLabelFor(Object info, View root, int virtualDescendantId);
     }
 
     static class AccessibilityNodeInfoStubImpl implements AccessibilityNodeInfoImpl {
@@ -732,6 +738,32 @@
         @Override
         public void setTraversalAfter(Object info, View root, int virtualDescendantId) {
         }
+
+        @Override
+        public void setContentInvalid(Object info, boolean contentInvalid) {
+        }
+
+        @Override
+        public boolean isContentInvalid(Object info) {
+            return false;
+        }
+
+        @Override
+        public void setError(Object info, CharSequence error) {
+        }
+
+        @Override
+        public CharSequence getError(Object info) {
+            return null;
+        }
+
+        @Override
+        public void setLabelFor(Object info, View labeled) {
+        }
+
+        @Override
+        public void setLabelFor(Object info, View root, int virtualDescendantId) {
+        }
     }
 
     static class AccessibilityNodeInfoIcsImpl extends AccessibilityNodeInfoStubImpl {
@@ -1140,6 +1172,16 @@
         public void setCollectionItemInfo(Object info, Object collectionItemInfo) {
             AccessibilityNodeInfoCompatKitKat.setCollectionItemInfo(info, collectionItemInfo);
         }
+
+        @Override
+        public void setContentInvalid(Object info, boolean contentInvalid) {
+            AccessibilityNodeInfoCompatKitKat.setContentInvalid(info, contentInvalid);
+        }
+
+        @Override
+        public boolean isContentInvalid(Object info) {
+            return AccessibilityNodeInfoCompatKitKat.isContentInvalid(info);
+        }
     }
 
     static class AccessibilityNodeInfoApi21Impl extends AccessibilityNodeInfoKitKatImpl {
@@ -1186,6 +1228,26 @@
         public boolean isCollectionItemSelected(Object info) {
             return AccessibilityNodeInfoCompatApi21.CollectionItemInfo.isSelected(info);
         }
+
+        @Override
+        public CharSequence getError(Object info) {
+            return AccessibilityNodeInfoCompatApi21.getError(info);
+        }
+
+        @Override
+        public void setError(Object info, CharSequence error) {
+            AccessibilityNodeInfoCompatApi21.setError(info, error);
+        }
+
+        @Override
+        public void setLabelFor(Object info, View labeled) {
+            AccessibilityNodeInfoCompatApi21.setLabelFor(info, labeled);
+        }
+
+        @Override
+        public void setLabelFor(Object info, View root, int virtualDescendantId) {
+            AccessibilityNodeInfoCompatApi21.setLabelFor(info, root, virtualDescendantId);
+        }
     }
 
     static class AccessibilityNodeInfoApi22Impl extends AccessibilityNodeInfoApi21Impl {
@@ -2531,6 +2593,83 @@
         }
     }
 
+    /**
+     * Sets if the content of this node is invalid. For example,
+     * a date is not well-formed.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param contentInvalid If the node content is invalid.
+     */
+    public void setContentInvalid(boolean contentInvalid) {
+        IMPL.setContentInvalid(mInfo, contentInvalid);
+    }
+
+    /**
+     * Gets if the content of this node is invalid. For example,
+     * a date is not well-formed.
+     *
+     * @return If the node content is invalid.
+     */
+    public boolean isContentInvalid() {
+        return IMPL.isContentInvalid(mInfo);
+    }
+
+    /**
+     * Sets the error text of this node.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param error The error text.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setError(CharSequence error) {
+        IMPL.setError(mInfo, error);
+    }
+
+    /**
+     * Gets the error text of this node.
+     *
+     * @return The error text.
+     */
+    public CharSequence getError() {
+        return IMPL.getError(mInfo);
+    }
+
+    /**
+     * Sets the view for which the view represented by this info serves as a
+     * label for accessibility purposes.
+     *
+     * @param labeled The view for which this info serves as a label.
+     */
+    public void setLabelFor(View labeled) {
+        IMPL.setLabelFor(mInfo, labeled);
+    }
+
+    /**
+     * Sets the view for which the view represented by this info serves as a
+     * label for accessibility purposes. If <code>virtualDescendantId</code>
+     * is {@link View#NO_ID} the root is set as the labeled.
+     * <p>
+     * A virtual descendant is an imaginary View that is reported as a part of the view
+     * hierarchy for accessibility purposes. This enables custom views that draw complex
+     * content to report themselves as a tree of virtual views, thus conveying their
+     * logical structure.
+     * </p>
+     *
+     * @param root The root whose virtual descendant serves as a label.
+     * @param virtualDescendantId The id of the virtual descendant.
+     */
+    public void setLabelFor(View root, int virtualDescendantId) {
+        IMPL.setLabelFor(mInfo, root, virtualDescendantId);
+    }
 
     @Override
     public int hashCode() {
diff --git a/v4/java/android/support/v4/view/animation/FastOutLinearInInterpolator.java b/v4/java/android/support/v4/view/animation/FastOutLinearInInterpolator.java
new file mode 100644
index 0000000..1f39aa8
--- /dev/null
+++ b/v4/java/android/support/v4/view/animation/FastOutLinearInInterpolator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.animation;
+
+/**
+ * Interpolator corresponding to {@link android.R.interpolator#fast_out_linear_in}.
+ *
+ * Uses a lookup table for the Bezier curve from (0,0) to (1,1) with control points:
+ * P0 (0, 0)
+ * P1 (0.4, 0)
+ * P2 (1.0, 1.0)
+ * P3 (1.0, 1.0)
+ */
+public class FastOutLinearInInterpolator extends LookupTableInterpolator {
+
+    /**
+     * Lookup table values sampled with x at regular intervals between 0 and 1 for a total of
+     * 201 points.
+     */
+    private static final float[] VALUES = new float[] {
+            0.0000f, 0.0001f, 0.0002f, 0.0005f, 0.0008f, 0.0013f, 0.0018f,
+            0.0024f, 0.0032f, 0.0040f, 0.0049f, 0.0059f, 0.0069f, 0.0081f,
+            0.0093f, 0.0106f, 0.0120f, 0.0135f, 0.0151f, 0.0167f, 0.0184f,
+            0.0201f, 0.0220f, 0.0239f, 0.0259f, 0.0279f, 0.0300f, 0.0322f,
+            0.0345f, 0.0368f, 0.0391f, 0.0416f, 0.0441f, 0.0466f, 0.0492f,
+            0.0519f, 0.0547f, 0.0574f, 0.0603f, 0.0632f, 0.0662f, 0.0692f,
+            0.0722f, 0.0754f, 0.0785f, 0.0817f, 0.0850f, 0.0884f, 0.0917f,
+            0.0952f, 0.0986f, 0.1021f, 0.1057f, 0.1093f, 0.1130f, 0.1167f,
+            0.1205f, 0.1243f, 0.1281f, 0.1320f, 0.1359f, 0.1399f, 0.1439f,
+            0.1480f, 0.1521f, 0.1562f, 0.1604f, 0.1647f, 0.1689f, 0.1732f,
+            0.1776f, 0.1820f, 0.1864f, 0.1909f, 0.1954f, 0.1999f, 0.2045f,
+            0.2091f, 0.2138f, 0.2184f, 0.2232f, 0.2279f, 0.2327f, 0.2376f,
+            0.2424f, 0.2473f, 0.2523f, 0.2572f, 0.2622f, 0.2673f, 0.2723f,
+            0.2774f, 0.2826f, 0.2877f, 0.2929f, 0.2982f, 0.3034f, 0.3087f,
+            0.3141f, 0.3194f, 0.3248f, 0.3302f, 0.3357f, 0.3412f, 0.3467f,
+            0.3522f, 0.3578f, 0.3634f, 0.3690f, 0.3747f, 0.3804f, 0.3861f,
+            0.3918f, 0.3976f, 0.4034f, 0.4092f, 0.4151f, 0.4210f, 0.4269f,
+            0.4329f, 0.4388f, 0.4448f, 0.4508f, 0.4569f, 0.4630f, 0.4691f,
+            0.4752f, 0.4814f, 0.4876f, 0.4938f, 0.5000f, 0.5063f, 0.5126f,
+            0.5189f, 0.5252f, 0.5316f, 0.5380f, 0.5444f, 0.5508f, 0.5573f,
+            0.5638f, 0.5703f, 0.5768f, 0.5834f, 0.5900f, 0.5966f, 0.6033f,
+            0.6099f, 0.6166f, 0.6233f, 0.6301f, 0.6369f, 0.6436f, 0.6505f,
+            0.6573f, 0.6642f, 0.6710f, 0.6780f, 0.6849f, 0.6919f, 0.6988f,
+            0.7059f, 0.7129f, 0.7199f, 0.7270f, 0.7341f, 0.7413f, 0.7484f,
+            0.7556f, 0.7628f, 0.7700f, 0.7773f, 0.7846f, 0.7919f, 0.7992f,
+            0.8066f, 0.8140f, 0.8214f, 0.8288f, 0.8363f, 0.8437f, 0.8513f,
+            0.8588f, 0.8664f, 0.8740f, 0.8816f, 0.8892f, 0.8969f, 0.9046f,
+            0.9124f, 0.9201f, 0.9280f, 0.9358f, 0.9437f, 0.9516f, 0.9595f,
+            0.9675f, 0.9755f, 0.9836f, 0.9918f, 1.0000f
+    };
+
+    public FastOutLinearInInterpolator() {
+        super(VALUES);
+    }
+}
diff --git a/v4/java/android/support/v4/view/animation/FastOutSlowInInterpolator.java b/v4/java/android/support/v4/view/animation/FastOutSlowInInterpolator.java
new file mode 100644
index 0000000..a21d131
--- /dev/null
+++ b/v4/java/android/support/v4/view/animation/FastOutSlowInInterpolator.java
@@ -0,0 +1,70 @@
+/*
+ * 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.animation;
+
+/**
+ * Interpolator corresponding to {@link android.R.interpolator#fast_out_slow_in}.
+ *
+ * Uses a lookup table for the Bezier curve from (0,0) to (1,1) with control points:
+ * P0 (0, 0)
+ * P1 (0.4, 0)
+ * P2 (0.2, 1.0)
+ * P3 (1.0, 1.0)
+ */
+public class FastOutSlowInInterpolator extends LookupTableInterpolator {
+
+    /**
+     * Lookup table values sampled with x at regular intervals between 0 and 1 for a total of
+     * 201 points.
+     */
+    private static final float[] VALUES = new float[] {
+            0.0000f, 0.0001f, 0.0002f, 0.0005f, 0.0009f, 0.0014f, 0.0020f,
+            0.0027f, 0.0036f, 0.0046f, 0.0058f, 0.0071f, 0.0085f, 0.0101f,
+            0.0118f, 0.0137f, 0.0158f, 0.0180f, 0.0205f, 0.0231f, 0.0259f,
+            0.0289f, 0.0321f, 0.0355f, 0.0391f, 0.0430f, 0.0471f, 0.0514f,
+            0.0560f, 0.0608f, 0.0660f, 0.0714f, 0.0771f, 0.0830f, 0.0893f,
+            0.0959f, 0.1029f, 0.1101f, 0.1177f, 0.1257f, 0.1339f, 0.1426f,
+            0.1516f, 0.1610f, 0.1707f, 0.1808f, 0.1913f, 0.2021f, 0.2133f,
+            0.2248f, 0.2366f, 0.2487f, 0.2611f, 0.2738f, 0.2867f, 0.2998f,
+            0.3131f, 0.3265f, 0.3400f, 0.3536f, 0.3673f, 0.3810f, 0.3946f,
+            0.4082f, 0.4217f, 0.4352f, 0.4485f, 0.4616f, 0.4746f, 0.4874f,
+            0.5000f, 0.5124f, 0.5246f, 0.5365f, 0.5482f, 0.5597f, 0.5710f,
+            0.5820f, 0.5928f, 0.6033f, 0.6136f, 0.6237f, 0.6335f, 0.6431f,
+            0.6525f, 0.6616f, 0.6706f, 0.6793f, 0.6878f, 0.6961f, 0.7043f,
+            0.7122f, 0.7199f, 0.7275f, 0.7349f, 0.7421f, 0.7491f, 0.7559f,
+            0.7626f, 0.7692f, 0.7756f, 0.7818f, 0.7879f, 0.7938f, 0.7996f,
+            0.8053f, 0.8108f, 0.8162f, 0.8215f, 0.8266f, 0.8317f, 0.8366f,
+            0.8414f, 0.8461f, 0.8507f, 0.8551f, 0.8595f, 0.8638f, 0.8679f,
+            0.8720f, 0.8760f, 0.8798f, 0.8836f, 0.8873f, 0.8909f, 0.8945f,
+            0.8979f, 0.9013f, 0.9046f, 0.9078f, 0.9109f, 0.9139f, 0.9169f,
+            0.9198f, 0.9227f, 0.9254f, 0.9281f, 0.9307f, 0.9333f, 0.9358f,
+            0.9382f, 0.9406f, 0.9429f, 0.9452f, 0.9474f, 0.9495f, 0.9516f,
+            0.9536f, 0.9556f, 0.9575f, 0.9594f, 0.9612f, 0.9629f, 0.9646f,
+            0.9663f, 0.9679f, 0.9695f, 0.9710f, 0.9725f, 0.9739f, 0.9753f,
+            0.9766f, 0.9779f, 0.9791f, 0.9803f, 0.9815f, 0.9826f, 0.9837f,
+            0.9848f, 0.9858f, 0.9867f, 0.9877f, 0.9885f, 0.9894f, 0.9902f,
+            0.9910f, 0.9917f, 0.9924f, 0.9931f, 0.9937f, 0.9944f, 0.9949f,
+            0.9955f, 0.9960f, 0.9964f, 0.9969f, 0.9973f, 0.9977f, 0.9980f,
+            0.9984f, 0.9986f, 0.9989f, 0.9991f, 0.9993f, 0.9995f, 0.9997f,
+            0.9998f, 0.9999f, 0.9999f, 1.0000f, 1.0000f
+    };
+
+    public FastOutSlowInInterpolator() {
+        super(VALUES);
+    }
+
+}
diff --git a/v4/java/android/support/v4/view/animation/LinearOutSlowInInterpolator.java b/v4/java/android/support/v4/view/animation/LinearOutSlowInInterpolator.java
new file mode 100644
index 0000000..41f4cd6
--- /dev/null
+++ b/v4/java/android/support/v4/view/animation/LinearOutSlowInInterpolator.java
@@ -0,0 +1,70 @@
+/*
+ * 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.animation;
+
+/**
+ * Interpolator corresponding to {@link android.R.interpolator#linear_out_slow_in}.
+ *
+ * Uses a lookup table for the Bezier curve from (0,0) to (1,1) with control points:
+ * P0 (0, 0)
+ * P1 (0, 0)
+ * P2 (0.2, 1.0)
+ * P3 (1.0, 1.0)
+ */
+public class LinearOutSlowInInterpolator extends LookupTableInterpolator {
+
+    /**
+     * Lookup table values sampled with x at regular intervals between 0 and 1 for a total of
+     * 201 points.
+     */
+    private static final float[] VALUES = new float[] {
+            0.0000f, 0.0222f, 0.0424f, 0.0613f, 0.0793f, 0.0966f, 0.1132f,
+            0.1293f, 0.1449f, 0.1600f, 0.1747f, 0.1890f, 0.2029f, 0.2165f,
+            0.2298f, 0.2428f, 0.2555f, 0.2680f, 0.2802f, 0.2921f, 0.3038f,
+            0.3153f, 0.3266f, 0.3377f, 0.3486f, 0.3592f, 0.3697f, 0.3801f,
+            0.3902f, 0.4002f, 0.4100f, 0.4196f, 0.4291f, 0.4385f, 0.4477f,
+            0.4567f, 0.4656f, 0.4744f, 0.4831f, 0.4916f, 0.5000f, 0.5083f,
+            0.5164f, 0.5245f, 0.5324f, 0.5402f, 0.5479f, 0.5555f, 0.5629f,
+            0.5703f, 0.5776f, 0.5847f, 0.5918f, 0.5988f, 0.6057f, 0.6124f,
+            0.6191f, 0.6257f, 0.6322f, 0.6387f, 0.6450f, 0.6512f, 0.6574f,
+            0.6635f, 0.6695f, 0.6754f, 0.6812f, 0.6870f, 0.6927f, 0.6983f,
+            0.7038f, 0.7093f, 0.7147f, 0.7200f, 0.7252f, 0.7304f, 0.7355f,
+            0.7406f, 0.7455f, 0.7504f, 0.7553f, 0.7600f, 0.7647f, 0.7694f,
+            0.7740f, 0.7785f, 0.7829f, 0.7873f, 0.7917f, 0.7959f, 0.8002f,
+            0.8043f, 0.8084f, 0.8125f, 0.8165f, 0.8204f, 0.8243f, 0.8281f,
+            0.8319f, 0.8356f, 0.8392f, 0.8429f, 0.8464f, 0.8499f, 0.8534f,
+            0.8568f, 0.8601f, 0.8634f, 0.8667f, 0.8699f, 0.8731f, 0.8762f,
+            0.8792f, 0.8823f, 0.8852f, 0.8882f, 0.8910f, 0.8939f, 0.8967f,
+            0.8994f, 0.9021f, 0.9048f, 0.9074f, 0.9100f, 0.9125f, 0.9150f,
+            0.9174f, 0.9198f, 0.9222f, 0.9245f, 0.9268f, 0.9290f, 0.9312f,
+            0.9334f, 0.9355f, 0.9376f, 0.9396f, 0.9416f, 0.9436f, 0.9455f,
+            0.9474f, 0.9492f, 0.9510f, 0.9528f, 0.9545f, 0.9562f, 0.9579f,
+            0.9595f, 0.9611f, 0.9627f, 0.9642f, 0.9657f, 0.9672f, 0.9686f,
+            0.9700f, 0.9713f, 0.9726f, 0.9739f, 0.9752f, 0.9764f, 0.9776f,
+            0.9787f, 0.9798f, 0.9809f, 0.9820f, 0.9830f, 0.9840f, 0.9849f,
+            0.9859f, 0.9868f, 0.9876f, 0.9885f, 0.9893f, 0.9900f, 0.9908f,
+            0.9915f, 0.9922f, 0.9928f, 0.9934f, 0.9940f, 0.9946f, 0.9951f,
+            0.9956f, 0.9961f, 0.9966f, 0.9970f, 0.9974f, 0.9977f, 0.9981f,
+            0.9984f, 0.9987f, 0.9989f, 0.9992f, 0.9994f, 0.9995f, 0.9997f,
+            0.9998f, 0.9999f, 0.9999f, 1.0000f, 1.0000f
+    };
+
+    public LinearOutSlowInInterpolator() {
+        super(VALUES);
+    }
+
+}
diff --git a/v4/java/android/support/v4/view/animation/LookupTableInterpolator.java b/v4/java/android/support/v4/view/animation/LookupTableInterpolator.java
new file mode 100644
index 0000000..c234177
--- /dev/null
+++ b/v4/java/android/support/v4/view/animation/LookupTableInterpolator.java
@@ -0,0 +1,57 @@
+/*
+ * 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.animation;
+
+import android.view.animation.Interpolator;
+
+/**
+ * An {@link Interpolator} that uses a lookup table to compute an interpolation based on a
+ * given input.
+ */
+abstract class LookupTableInterpolator implements Interpolator {
+
+    private final float[] mValues;
+    private final float mStepSize;
+
+    public LookupTableInterpolator(float[] values) {
+        mValues = values;
+        mStepSize = 1f / (mValues.length - 1);
+    }
+
+    @Override
+    public float getInterpolation(float input) {
+        if (input >= 1.0f) {
+            return 1.0f;
+        }
+        if (input <= 0f) {
+            return 0f;
+        }
+
+        // Calculate index - We use min with length - 2 to avoid IndexOutOfBoundsException when
+        // we lerp (linearly interpolate) in the return statement
+        int position = Math.min((int) (input * (mValues.length - 1)), mValues.length - 2);
+
+        // Calculate values to account for small offsets as the lookup table has discrete values
+        float quantized = position * mStepSize;
+        float diff = input - quantized;
+        float weight = diff / mStepSize;
+
+        // Linearly interpolate between the table values
+        return mValues[position] + weight * (mValues[position + 1] - mValues[position]);
+    }
+
+}
diff --git a/v4/java/android/support/v4/widget/BakedBezierInterpolator.java b/v4/java/android/support/v4/widget/BakedBezierInterpolator.java
deleted file mode 100644
index 892813c..0000000
--- a/v4/java/android/support/v4/widget/BakedBezierInterpolator.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2013 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.widget;
-
-import android.view.animation.Interpolator;
-
-/**
- * A pre-baked bezier-curved interpolator for indeterminate progress animations.
- */
-final class BakedBezierInterpolator implements Interpolator {
-    private static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator();
-
-    public final static BakedBezierInterpolator getInstance() {
-        return INSTANCE;
-    }
-
-    /**
-     * Use getInstance instead of instantiating.
-     */
-    private BakedBezierInterpolator() {
-        super();
-    }
-
-    /**
-     * Lookup table values.
-     * Generated using a Bezier curve from (0,0) to (1,1) with control points:
-     * P0 (0,0)
-     * P1 (0.4, 0)
-     * P2 (0.2, 1.0)
-     * P3 (1.0, 1.0)
-     *
-     * Values sampled with x at regular intervals between 0 and 1.
-     */
-    private static final float[] VALUES = new float[] {
-        0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
-        0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f,
-        0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f,
-        0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f,
-        0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f,
-        0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f,
-        0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f,
-        0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f,
-        0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f,
-        0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
-    };
-
-    private static final float STEP_SIZE = 1.0f / (VALUES.length - 1);
-
-    @Override
-    public float getInterpolation(float input) {
-        if (input >= 1.0f) {
-            return 1.0f;
-        }
-
-        if (input <= 0f) {
-            return 0f;
-        }
-
-        int position = Math.min(
-                (int)(input * (VALUES.length - 1)),
-                VALUES.length - 2);
-
-        float quantized = position * STEP_SIZE;
-        float difference = input - quantized;
-        float weight = difference / STEP_SIZE;
-
-        return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]);
-    }
-
-}
diff --git a/v4/java/android/support/v4/widget/DrawerLayout.java b/v4/java/android/support/v4/widget/DrawerLayout.java
index 8204ec7..d939b35 100644
--- a/v4/java/android/support/v4/widget/DrawerLayout.java
+++ b/v4/java/android/support/v4/widget/DrawerLayout.java
@@ -29,6 +29,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.support.annotation.ColorInt;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
@@ -417,7 +418,7 @@
      *
      * @param color Color to use in 0xAARRGGBB format.
      */
-    public void setScrimColor(int color) {
+    public void setScrimColor(@ColorInt int color) {
         mScrimColor = color;
         invalidate();
     }
@@ -1012,6 +1013,7 @@
      */
     public void setStatusBarBackground(Drawable bg) {
         mStatusBarBackground = bg;
+        invalidate();
     }
 
     /**
@@ -1031,6 +1033,7 @@
      */
     public void setStatusBarBackground(int resId) {
         mStatusBarBackground = resId != 0 ? ContextCompat.getDrawable(getContext(), resId) : null;
+        invalidate();
     }
 
     /**
@@ -1040,8 +1043,9 @@
      * @param color Color to use as a background drawable to draw behind the status bar
      *              in 0xAARRGGBB format.
      */
-    public void setStatusBarBackgroundColor(int color) {
+    public void setStatusBarBackgroundColor(@ColorInt int color) {
         mStatusBarBackground = new ColorDrawable(color);
+        invalidate();
     }
 
     @Override
diff --git a/v4/java/android/support/v4/widget/EdgeEffectCompat.java b/v4/java/android/support/v4/widget/EdgeEffectCompat.java
index 79b413a..51da4cd 100644
--- a/v4/java/android/support/v4/widget/EdgeEffectCompat.java
+++ b/v4/java/android/support/v4/widget/EdgeEffectCompat.java
@@ -50,6 +50,7 @@
         public boolean onRelease(Object edgeEffect);
         public boolean onAbsorb(Object edgeEffect, int velocity);
         public boolean draw(Object edgeEffect, Canvas canvas);
+        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement);
     }
 
     /**
@@ -85,6 +86,10 @@
         public boolean draw(Object edgeEffect, Canvas canvas) {
             return false;
         }
+
+        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
+            return false;
+        }
     }
 
     static class EdgeEffectIcsImpl implements EdgeEffectImpl {
@@ -119,6 +124,16 @@
         public boolean draw(Object edgeEffect, Canvas canvas) {
             return EdgeEffectCompatIcs.draw(edgeEffect, canvas);
         }
+
+        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
+            return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance);
+        }
+    }
+
+    static class EdgeEffectLollipopImpl extends EdgeEffectIcsImpl {
+        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
+            return EdgeEffectCompatLollipop.onPull(edgeEffect, deltaDistance, displacement);
+        }
     }
 
     /**
@@ -172,12 +187,31 @@
      *                      1.f (full length of the view) or negative values to express change
      *                      back toward the edge reached to initiate the effect.
      * @return true if the host view should call invalidate, false if it should not.
+     * @deprecated use {@link #onPull(float, float)}
      */
     public boolean onPull(float deltaDistance) {
         return IMPL.onPull(mEdgeEffect, deltaDistance);
     }
 
     /**
+     * A view should call this when content is pulled away from an edge by the user.
+     * This will update the state of the current visual effect and its associated animation.
+     * The host view should always {@link android.view.View#invalidate()} if this method
+     * returns true and draw the results accordingly.
+     *
+     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+     *                      1.f (full length of the view) or negative values to express change
+     *                      back toward the edge reached to initiate the effect.
+     * @param displacement The displacement from the starting side of the effect of the point
+     *                     initiating the pull. In the case of touch this is the finger position.
+     *                     Values may be from 0-1.
+     * @return true if the host view should call invalidate, false if it should not.
+     */
+    public boolean onPull(float deltaDistance, float displacement) {
+        return IMPL.onPull(mEdgeEffect, deltaDistance, displacement);
+    }
+
+    /**
      * Call when the object is released after being pulled.
      * This will begin the "decay" phase of the effect. After calling this method
      * the host view should {@link android.view.View#invalidate()} if this method
diff --git a/v4/java/android/support/v4/widget/ExploreByTouchHelper.java b/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
index 7adbc6f..64f6634 100644
--- a/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
+++ b/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
@@ -45,11 +45,11 @@
  * and managing accessibility focus. This class does not currently support
  * hierarchies of logical items.
  * <p>
- * This should be applied to the parent view using
- * {@link ViewCompat#setAccessibilityDelegate}:
+ * Clients should override abstract methods on this class and attach it to the
+ * host view using {@link ViewCompat#setAccessibilityDelegate}:
  *
  * <pre>
- * mAccessHelper = ExploreByTouchHelper.create(someView, mAccessHelperCallback);
+ * mAccessHelper = new MyExploreByTouchHelper(someView);
  * ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
  * </pre>
  */
@@ -57,6 +57,9 @@
     /** Virtual node identifier value for invalid nodes. */
     public static final int INVALID_ID = Integer.MIN_VALUE;
 
+    /** Virtual node identifier value for the host view's node. */
+    public static final int HOST_ID = View.NO_ID;
+
     /** Default class name used for virtual views. */
     private static final String DEFAULT_CLASS_NAME = View.class.getName();
 
@@ -191,7 +194,7 @@
      * parent view.
      */
     public void invalidateRoot() {
-        invalidateVirtualView(View.NO_ID);
+        invalidateVirtualView(HOST_ID);
     }
 
     /**
@@ -243,7 +246,7 @@
 
     /**
      * Constructs and returns an {@link AccessibilityEvent} for the specified
-     * virtual view id, which includes the host view ({@link View#NO_ID}).
+     * virtual view id, which includes the host view ({@link #HOST_ID}).
      *
      * @param virtualViewId The virtual view id for the item for which to
      *            construct an event.
@@ -253,7 +256,7 @@
      */
     private AccessibilityEvent createEvent(int virtualViewId, int eventType) {
         switch (virtualViewId) {
-            case View.NO_ID:
+            case HOST_ID:
                 return createEventForHost(eventType);
             default:
                 return createEventForChild(virtualViewId, eventType);
@@ -309,7 +312,7 @@
     /**
      * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
      * specified virtual view id, which includes the host view
-     * ({@link View#NO_ID}).
+     * ({@link #HOST_ID}).
      *
      * @param virtualViewId The virtual view id for the item for which to
      *            construct a node.
@@ -318,7 +321,7 @@
      */
     private AccessibilityNodeInfoCompat createNode(int virtualViewId) {
         switch (virtualViewId) {
-            case View.NO_ID:
+            case HOST_ID:
                 return createNodeForHost();
             default:
                 return createNodeForChild(virtualViewId);
@@ -335,6 +338,9 @@
         final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain(mView);
         ViewCompat.onInitializeAccessibilityNodeInfo(mView, node);
 
+        // Allow the client to populate the host node.
+        onPopulateNodeForHost(node);
+
         // Add the virtual descendants.
         final LinkedList<Integer> virtualViewIds = new LinkedList<Integer>();
         getVisibleVirtualViews(virtualViewIds);
@@ -439,7 +445,7 @@
 
     private boolean performAction(int virtualViewId, int action, Bundle arguments) {
         switch (virtualViewId) {
-            case View.NO_ID:
+            case HOST_ID:
                 return performActionForHost(action, arguments);
             default:
                 return performActionForChild(virtualViewId, action, arguments);
@@ -542,7 +548,15 @@
         }
         // TODO: Check virtual view visibility.
         if (!isAccessibilityFocused(virtualViewId)) {
+            // Clear focus from the previously focused view, if applicable.
+            if (mFocusedVirtualViewId != INVALID_ID) {
+                sendEventForVirtualView(mFocusedVirtualViewId,
+                        AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+            }
+
+            // Set focus on the new view.
             mFocusedVirtualViewId = virtualViewId;
+
             // TODO: Only invalidate virtual view bounds.
             mView.invalidate();
             sendEventForVirtualView(virtualViewId,
@@ -577,7 +591,7 @@
      * @param x The view-relative x coordinate
      * @param y The view-relative y coordinate
      * @return virtual view identifier for the logical item under
-     *         coordinates (x,y) or {@link View#NO_ID} if there is no item at
+     *         coordinates (x,y) or {@link #HOST_ID} if there is no item at
      *         the given coordinates
      */
     protected abstract int getVirtualViewAt(float x, float y);
@@ -683,6 +697,17 @@
             int virtualViewId, AccessibilityNodeInfoCompat node);
 
     /**
+     * Populates an {@link AccessibilityNodeInfoCompat} with information
+     * about the host view.
+     * <p>
+     * The following required fields are automatically populated by the
+     * helper class and may not be overridden:
+     */
+    public void onPopulateNodeForHost(AccessibilityNodeInfoCompat node) {
+        // Default implementation is no-op.
+    }
+
+    /**
      * Performs the specified accessibility action on the item associated
      * with the virtual view identifier. See
      * {@link AccessibilityNodeInfoCompat#performAction(int, Bundle)} for
diff --git a/v4/java/android/support/v4/widget/NestedScrollView.java b/v4/java/android/support/v4/widget/NestedScrollView.java
new file mode 100644
index 0000000..eb24814
--- /dev/null
+++ b/v4/java/android/support/v4/widget/NestedScrollView.java
@@ -0,0 +1,1862 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.AccessibilityDelegateCompat;
+import android.support.v4.view.InputDeviceCompat;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.NestedScrollingChild;
+import android.support.v4.view.NestedScrollingChildHelper;
+import android.support.v4.view.NestedScrollingParent;
+import android.support.v4.view.NestedScrollingParentHelper;
+import android.support.v4.view.VelocityTrackerCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.accessibility.AccessibilityEventCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.FocusFinder;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+import android.widget.ScrollView;
+
+import java.util.List;
+
+/**
+ * NestedScrollView is just like {@link android.widget.ScrollView}, but it supports acting
+ * as both a nested scrolling parent and child on both new and old versions of Android.
+ * Nested scrolling is enabled by default.
+ */
+public class NestedScrollView extends FrameLayout implements NestedScrollingParent,
+        NestedScrollingChild {
+    static final int ANIMATED_SCROLL_GAP = 250;
+
+    static final float MAX_SCROLL_FACTOR = 0.5f;
+
+    private static final String TAG = "NestedScrollView";
+
+    private long mLastScroll;
+
+    private final Rect mTempRect = new Rect();
+    private ScrollerCompat mScroller;
+    private EdgeEffectCompat mEdgeGlowTop;
+    private EdgeEffectCompat mEdgeGlowBottom;
+
+    /**
+     * Position of the last motion event.
+     */
+    private int mLastMotionY;
+
+    /**
+     * True when the layout has changed but the traversal has not come through yet.
+     * Ideally the view hierarchy would keep track of this for us.
+     */
+    private boolean mIsLayoutDirty = true;
+    private boolean mIsLaidOut = false;
+
+    /**
+     * The child to give focus to in the event that a child has requested focus while the
+     * layout is dirty. This prevents the scroll from being wrong if the child has not been
+     * laid out before requesting focus.
+     */
+    private View mChildToScrollTo = null;
+
+    /**
+     * True if the user is currently dragging this ScrollView around. This is
+     * not the same as 'is being flinged', which can be checked by
+     * mScroller.isFinished() (flinging begins when the user lifts his finger).
+     */
+    private boolean mIsBeingDragged = false;
+
+    /**
+     * Determines speed during touch scrolling
+     */
+    private VelocityTracker mVelocityTracker;
+
+    /**
+     * When set to true, the scroll view measure its child to make it fill the currently
+     * visible area.
+     */
+    private boolean mFillViewport;
+
+    /**
+     * Whether arrow scrolling is animated.
+     */
+    private boolean mSmoothScrollingEnabled = true;
+
+    private int mTouchSlop;
+    private int mMinimumVelocity;
+    private int mMaximumVelocity;
+
+    /**
+     * ID of the active pointer. This is used to retain consistency during
+     * drags/flings if multiple pointers are used.
+     */
+    private int mActivePointerId = INVALID_POINTER;
+
+    /**
+     * Used during scrolling to retrieve the new offset within the window.
+     */
+    private final int[] mScrollOffset = new int[2];
+    private final int[] mScrollConsumed = new int[2];
+    private int mNestedYOffset;
+
+    /**
+     * Sentinel value for no current active pointer.
+     * Used by {@link #mActivePointerId}.
+     */
+    private static final int INVALID_POINTER = -1;
+
+    private SavedState mSavedState;
+
+    private static final AccessibilityDelegate ACCESSIBILITY_DELEGATE = new AccessibilityDelegate();
+
+    private static final int[] SCROLLVIEW_STYLEABLE = new int[] {
+            android.R.attr.fillViewport
+    };
+
+    private final NestedScrollingParentHelper mParentHelper;
+    private final NestedScrollingChildHelper mChildHelper;
+
+    private float mVerticalScrollFactor;
+
+    public NestedScrollView(Context context) {
+        this(context, null);
+    }
+
+    public NestedScrollView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initScrollView();
+
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0);
+
+        setFillViewport(a.getBoolean(0, false));
+
+        a.recycle();
+
+        mParentHelper = new NestedScrollingParentHelper(this);
+        mChildHelper = new NestedScrollingChildHelper(this);
+
+        // ...because why else would you be using this widget?
+        setNestedScrollingEnabled(true);
+
+        ViewCompat.setAccessibilityDelegate(this, ACCESSIBILITY_DELEGATE);
+    }
+
+    // NestedScrollingChild
+
+    @Override
+    public void setNestedScrollingEnabled(boolean enabled) {
+        mChildHelper.setNestedScrollingEnabled(enabled);
+    }
+
+    @Override
+    public boolean isNestedScrollingEnabled() {
+        return mChildHelper.isNestedScrollingEnabled();
+    }
+
+    @Override
+    public boolean startNestedScroll(int axes) {
+        return mChildHelper.startNestedScroll(axes);
+    }
+
+    @Override
+    public void stopNestedScroll() {
+        mChildHelper.stopNestedScroll();
+    }
+
+    @Override
+    public boolean hasNestedScrollingParent() {
+        return mChildHelper.hasNestedScrollingParent();
+    }
+
+    @Override
+    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
+            int dyUnconsumed, int[] offsetInWindow) {
+        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                offsetInWindow);
+    }
+
+    @Override
+    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
+        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
+    }
+
+    @Override
+    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
+        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
+    }
+
+    @Override
+    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
+        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
+    }
+
+    // NestedScrollingParent
+
+    @Override
+    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
+    }
+
+    @Override
+    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
+        mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
+        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
+    }
+
+    @Override
+    public void onStopNestedScroll(View target) {
+        stopNestedScroll();
+    }
+
+    @Override
+    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
+            int dyUnconsumed) {
+        final int oldScrollY = getScrollY();
+        scrollBy(0, dyUnconsumed);
+        final int myConsumed = getScrollY() - oldScrollY;
+        final int myUnconsumed = dyUnconsumed - myConsumed;
+        dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
+    }
+
+    @Override
+    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
+        // Do nothing
+    }
+
+    @Override
+    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+        if (!consumed) {
+            flingWithNestedDispatch((int) velocityY);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+        // Do nothing
+        return false;
+    }
+
+    @Override
+    public int getNestedScrollAxes() {
+        return mParentHelper.getNestedScrollAxes();
+    }
+
+    // ScrollView import
+
+    public boolean shouldDelayChildPressedState() {
+        return true;
+    }
+
+    @Override
+    protected float getTopFadingEdgeStrength() {
+        if (getChildCount() == 0) {
+            return 0.0f;
+        }
+
+        final int length = getVerticalFadingEdgeLength();
+        final int scrollY = getScrollY();
+        if (scrollY < length) {
+            return scrollY / (float) length;
+        }
+
+        return 1.0f;
+    }
+
+    @Override
+    protected float getBottomFadingEdgeStrength() {
+        if (getChildCount() == 0) {
+            return 0.0f;
+        }
+
+        final int length = getVerticalFadingEdgeLength();
+        final int bottomEdge = getHeight() - getPaddingBottom();
+        final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge;
+        if (span < length) {
+            return span / (float) length;
+        }
+
+        return 1.0f;
+    }
+
+    /**
+     * @return The maximum amount this scroll view will scroll in response to
+     *   an arrow event.
+     */
+    public int getMaxScrollAmount() {
+        return (int) (MAX_SCROLL_FACTOR * getHeight());
+    }
+
+    private void initScrollView() {
+        mScroller = new ScrollerCompat(getContext(), null);
+        setFocusable(true);
+        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
+        setWillNotDraw(false);
+        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+        mTouchSlop = configuration.getScaledTouchSlop();
+        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
+        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+    }
+
+    @Override
+    public void addView(View child) {
+        if (getChildCount() > 0) {
+            throw new IllegalStateException("ScrollView can host only one direct child");
+        }
+
+        super.addView(child);
+    }
+
+    @Override
+    public void addView(View child, int index) {
+        if (getChildCount() > 0) {
+            throw new IllegalStateException("ScrollView can host only one direct child");
+        }
+
+        super.addView(child, index);
+    }
+
+    @Override
+    public void addView(View child, ViewGroup.LayoutParams params) {
+        if (getChildCount() > 0) {
+            throw new IllegalStateException("ScrollView can host only one direct child");
+        }
+
+        super.addView(child, params);
+    }
+
+    @Override
+    public void addView(View child, int index, ViewGroup.LayoutParams params) {
+        if (getChildCount() > 0) {
+            throw new IllegalStateException("ScrollView can host only one direct child");
+        }
+
+        super.addView(child, index, params);
+    }
+
+    /**
+     * @return Returns true this ScrollView can be scrolled
+     */
+    private boolean canScroll() {
+        View child = getChildAt(0);
+        if (child != null) {
+            int childHeight = child.getHeight();
+            return getHeight() < childHeight + getPaddingTop() + getPaddingBottom();
+        }
+        return false;
+    }
+
+    /**
+     * Indicates whether this ScrollView's content is stretched to fill the viewport.
+     *
+     * @return True if the content fills the viewport, false otherwise.
+     *
+     * @attr ref android.R.styleable#ScrollView_fillViewport
+     */
+    public boolean isFillViewport() {
+        return mFillViewport;
+    }
+
+    /**
+     * Indicates this ScrollView whether it should stretch its content height to fill
+     * the viewport or not.
+     *
+     * @param fillViewport True to stretch the content's height to the viewport's
+     *        boundaries, false otherwise.
+     *
+     * @attr ref android.R.styleable#ScrollView_fillViewport
+     */
+    public void setFillViewport(boolean fillViewport) {
+        if (fillViewport != mFillViewport) {
+            mFillViewport = fillViewport;
+            requestLayout();
+        }
+    }
+
+    /**
+     * @return Whether arrow scrolling will animate its transition.
+     */
+    public boolean isSmoothScrollingEnabled() {
+        return mSmoothScrollingEnabled;
+    }
+
+    /**
+     * Set whether arrow scrolling will animate its transition.
+     * @param smoothScrollingEnabled whether arrow scrolling will animate its transition
+     */
+    public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) {
+        mSmoothScrollingEnabled = smoothScrollingEnabled;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        if (!mFillViewport) {
+            return;
+        }
+
+        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        if (heightMode == MeasureSpec.UNSPECIFIED) {
+            return;
+        }
+
+        if (getChildCount() > 0) {
+            final View child = getChildAt(0);
+            int height = getMeasuredHeight();
+            if (child.getMeasuredHeight() < height) {
+                final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+                int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
+                        getPaddingLeft() + getPaddingRight(), lp.width);
+                height -= getPaddingTop();
+                height -= getPaddingBottom();
+                int childHeightMeasureSpec =
+                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+
+                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+            }
+        }
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        // Let the focused view and/or our descendants get the key first
+        return super.dispatchKeyEvent(event) || executeKeyEvent(event);
+    }
+
+    /**
+     * You can call this function yourself to have the scroll view perform
+     * scrolling from a key event, just as if the event had been dispatched to
+     * it by the view hierarchy.
+     *
+     * @param event The key event to execute.
+     * @return Return true if the event was handled, else false.
+     */
+    public boolean executeKeyEvent(KeyEvent event) {
+        mTempRect.setEmpty();
+
+        if (!canScroll()) {
+            if (isFocused() && event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
+                View currentFocused = findFocus();
+                if (currentFocused == this) currentFocused = null;
+                View nextFocused = FocusFinder.getInstance().findNextFocus(this,
+                        currentFocused, View.FOCUS_DOWN);
+                return nextFocused != null
+                        && nextFocused != this
+                        && nextFocused.requestFocus(View.FOCUS_DOWN);
+            }
+            return false;
+        }
+
+        boolean handled = false;
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            switch (event.getKeyCode()) {
+                case KeyEvent.KEYCODE_DPAD_UP:
+                    if (!event.isAltPressed()) {
+                        handled = arrowScroll(View.FOCUS_UP);
+                    } else {
+                        handled = fullScroll(View.FOCUS_UP);
+                    }
+                    break;
+                case KeyEvent.KEYCODE_DPAD_DOWN:
+                    if (!event.isAltPressed()) {
+                        handled = arrowScroll(View.FOCUS_DOWN);
+                    } else {
+                        handled = fullScroll(View.FOCUS_DOWN);
+                    }
+                    break;
+                case KeyEvent.KEYCODE_SPACE:
+                    pageScroll(event.isShiftPressed() ? View.FOCUS_UP : View.FOCUS_DOWN);
+                    break;
+            }
+        }
+
+        return handled;
+    }
+
+    private boolean inChild(int x, int y) {
+        if (getChildCount() > 0) {
+            final int scrollY = getScrollY();
+            final View child = getChildAt(0);
+            return !(y < child.getTop() - scrollY
+                    || y >= child.getBottom() - scrollY
+                    || x < child.getLeft()
+                    || x >= child.getRight());
+        }
+        return false;
+    }
+
+    private void initOrResetVelocityTracker() {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        } else {
+            mVelocityTracker.clear();
+        }
+    }
+
+    private void initVelocityTrackerIfNotExists() {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+    }
+
+    private void recycleVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
+    @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        if (disallowIntercept) {
+            recycleVelocityTracker();
+        }
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
+    }
+
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        /*
+         * This method JUST determines whether we want to intercept the motion.
+         * If we return true, onMotionEvent will be called and we do the actual
+         * scrolling there.
+         */
+
+        /*
+        * Shortcut the most recurring case: the user is in the dragging
+        * state and he is moving his finger.  We want to intercept this
+        * motion.
+        */
+        final int action = ev.getAction();
+        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
+            return true;
+        }
+
+        /*
+         * Don't try to intercept touch if we can't scroll anyway.
+         */
+        if (getScrollY() == 0 && !ViewCompat.canScrollVertically(this, 1)) {
+            return false;
+        }
+
+        switch (action & MotionEventCompat.ACTION_MASK) {
+            case MotionEvent.ACTION_MOVE: {
+                /*
+                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
+                 * whether the user has moved far enough from his original down touch.
+                 */
+
+                /*
+                * Locally do absolute value. mLastMotionY is set to the y value
+                * of the down event.
+                */
+                final int activePointerId = mActivePointerId;
+                if (activePointerId == INVALID_POINTER) {
+                    // If we don't have a valid id, the touch down wasn't on content.
+                    break;
+                }
+
+                final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
+                if (pointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointerId=" + activePointerId
+                            + " in onInterceptTouchEvent");
+                    break;
+                }
+
+                final int y = (int) MotionEventCompat.getY(ev, pointerIndex);
+                final int yDiff = Math.abs(y - mLastMotionY);
+                if (yDiff > mTouchSlop
+                        && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) {
+                    mIsBeingDragged = true;
+                    mLastMotionY = y;
+                    initVelocityTrackerIfNotExists();
+                    mVelocityTracker.addMovement(ev);
+                    mNestedYOffset = 0;
+                    final ViewParent parent = getParent();
+                    if (parent != null) {
+                        parent.requestDisallowInterceptTouchEvent(true);
+                    }
+                }
+                break;
+            }
+
+            case MotionEvent.ACTION_DOWN: {
+                final int y = (int) ev.getY();
+                if (!inChild((int) ev.getX(), (int) y)) {
+                    mIsBeingDragged = false;
+                    recycleVelocityTracker();
+                    break;
+                }
+
+                /*
+                 * Remember location of down touch.
+                 * ACTION_DOWN always refers to pointer index 0.
+                 */
+                mLastMotionY = y;
+                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+
+                initOrResetVelocityTracker();
+                mVelocityTracker.addMovement(ev);
+                /*
+                * If being flinged and user touches the screen, initiate drag;
+                * otherwise don't.  mScroller.isFinished should be false when
+                * being flinged.
+                */
+                mIsBeingDragged = !mScroller.isFinished();
+                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
+                break;
+            }
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                /* Release the drag */
+                mIsBeingDragged = false;
+                mActivePointerId = INVALID_POINTER;
+                recycleVelocityTracker();
+                stopNestedScroll();
+                break;
+            case MotionEventCompat.ACTION_POINTER_UP:
+                onSecondaryPointerUp(ev);
+                break;
+        }
+
+        /*
+        * The only time we want to intercept motion events is if we are in the
+        * drag mode.
+        */
+        return mIsBeingDragged;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        initVelocityTrackerIfNotExists();
+
+        MotionEvent vtev = MotionEvent.obtain(ev);
+
+        final int actionMasked = MotionEventCompat.getActionMasked(ev);
+
+        if (actionMasked == MotionEvent.ACTION_DOWN) {
+            mNestedYOffset = 0;
+        }
+        vtev.offsetLocation(0, mNestedYOffset);
+
+        switch (actionMasked) {
+            case MotionEvent.ACTION_DOWN: {
+                if (getChildCount() == 0) {
+                    return false;
+                }
+                if ((mIsBeingDragged = !mScroller.isFinished())) {
+                    final ViewParent parent = getParent();
+                    if (parent != null) {
+                        parent.requestDisallowInterceptTouchEvent(true);
+                    }
+                }
+
+                /*
+                 * If being flinged and user touches, stop the fling. isFinished
+                 * will be false if being flinged.
+                 */
+                if (!mScroller.isFinished()) {
+                    mScroller.abortAnimation();
+                }
+
+                // Remember where the motion event started
+                mLastMotionY = (int) ev.getY();
+                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
+                break;
+            }
+            case MotionEvent.ACTION_MOVE:
+                final int activePointerIndex = MotionEventCompat.findPointerIndex(ev,
+                        mActivePointerId);
+                if (activePointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
+                    break;
+                }
+
+                final int y = (int) MotionEventCompat.getY(ev, activePointerIndex);
+                int deltaY = mLastMotionY - y;
+                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
+                    deltaY -= mScrollConsumed[1];
+                    vtev.offsetLocation(0, mScrollOffset[1]);
+                    mNestedYOffset += mScrollOffset[1];
+                }
+                if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
+                    final ViewParent parent = getParent();
+                    if (parent != null) {
+                        parent.requestDisallowInterceptTouchEvent(true);
+                    }
+                    mIsBeingDragged = true;
+                    if (deltaY > 0) {
+                        deltaY -= mTouchSlop;
+                    } else {
+                        deltaY += mTouchSlop;
+                    }
+                }
+                if (mIsBeingDragged) {
+                    // Scroll to follow the motion event
+                    mLastMotionY = y - mScrollOffset[1];
+
+                    final int oldY = getScrollY();
+                    final int range = getScrollRange();
+                    final int overscrollMode = ViewCompat.getOverScrollMode(this);
+                    boolean canOverscroll = overscrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
+                            (overscrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS &&
+                                    range > 0);
+
+                    // Calling overScrollByCompat will call onOverScrolled, which
+                    // calls onScrollChanged if applicable.
+                    if (overScrollByCompat(0, deltaY, 0, getScrollY(), 0, range, 0,
+                            0, true) && !hasNestedScrollingParent()) {
+                        // Break our velocity if we hit a scroll barrier.
+                        mVelocityTracker.clear();
+                    }
+
+                    final int scrolledDeltaY = getScrollY() - oldY;
+                    final int unconsumedY = deltaY - scrolledDeltaY;
+                    if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
+                        mLastMotionY -= mScrollOffset[1];
+                        vtev.offsetLocation(0, mScrollOffset[1]);
+                        mNestedYOffset += mScrollOffset[1];
+                    } else if (canOverscroll) {
+                        ensureGlows();
+                        final int pulledToY = oldY + deltaY;
+                        if (pulledToY < 0) {
+                            mEdgeGlowTop.onPull((float) deltaY / getHeight(),
+                                    MotionEventCompat.getX(ev, activePointerIndex) / getWidth());
+                            if (!mEdgeGlowBottom.isFinished()) {
+                                mEdgeGlowBottom.onRelease();
+                            }
+                        } else if (pulledToY > range) {
+                            mEdgeGlowBottom.onPull((float) deltaY / getHeight(),
+                                    1.f - MotionEventCompat.getX(ev, activePointerIndex)
+                                            / getWidth());
+                            if (!mEdgeGlowTop.isFinished()) {
+                                mEdgeGlowTop.onRelease();
+                            }
+                        }
+                        if (mEdgeGlowTop != null
+                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
+                            ViewCompat.postInvalidateOnAnimation(this);
+                        }
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                if (mIsBeingDragged) {
+                    final VelocityTracker velocityTracker = mVelocityTracker;
+                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                    int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker,
+                            mActivePointerId);
+
+                    if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+                        flingWithNestedDispatch(-initialVelocity);
+                    }
+
+                    mActivePointerId = INVALID_POINTER;
+                    endDrag();
+                }
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                if (mIsBeingDragged && getChildCount() > 0) {
+                    mActivePointerId = INVALID_POINTER;
+                    endDrag();
+                }
+                break;
+            case MotionEventCompat.ACTION_POINTER_DOWN: {
+                final int index = MotionEventCompat.getActionIndex(ev);
+                mLastMotionY = (int) MotionEventCompat.getY(ev, index);
+                mActivePointerId = MotionEventCompat.getPointerId(ev, index);
+                break;
+            }
+            case MotionEventCompat.ACTION_POINTER_UP:
+                onSecondaryPointerUp(ev);
+                mLastMotionY = (int) MotionEventCompat.getY(ev,
+                        MotionEventCompat.findPointerIndex(ev, mActivePointerId));
+                break;
+        }
+
+        if (mVelocityTracker != null) {
+            mVelocityTracker.addMovement(vtev);
+        }
+        vtev.recycle();
+        return true;
+    }
+
+    private void onSecondaryPointerUp(MotionEvent ev) {
+        final int pointerIndex = (ev.getAction() & MotionEventCompat.ACTION_POINTER_INDEX_MASK) >>
+                MotionEventCompat.ACTION_POINTER_INDEX_SHIFT;
+        final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+        if (pointerId == mActivePointerId) {
+            // This was our active pointer going up. Choose a new
+            // active pointer and adjust accordingly.
+            // TODO: Make this decision more intelligent.
+            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+            mLastMotionY = (int) MotionEventCompat.getY(ev, newPointerIndex);
+            mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
+            if (mVelocityTracker != null) {
+                mVelocityTracker.clear();
+            }
+        }
+    }
+
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        if ((MotionEventCompat.getSource(event) & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
+            switch (event.getAction()) {
+                case MotionEventCompat.ACTION_SCROLL: {
+                    if (!mIsBeingDragged) {
+                        final float vscroll = MotionEventCompat.getAxisValue(event,
+                                MotionEventCompat.AXIS_VSCROLL);
+                        if (vscroll != 0) {
+                            final int delta = (int) (vscroll * getVerticalScrollFactorCompat());
+                            final int range = getScrollRange();
+                            int oldScrollY = getScrollY();
+                            int newScrollY = oldScrollY - delta;
+                            if (newScrollY < 0) {
+                                newScrollY = 0;
+                            } else if (newScrollY > range) {
+                                newScrollY = range;
+                            }
+                            if (newScrollY != oldScrollY) {
+                                super.scrollTo(getScrollX(), newScrollY);
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private float getVerticalScrollFactorCompat() {
+        if (mVerticalScrollFactor == 0) {
+            TypedValue outValue = new TypedValue();
+            final Context context = getContext();
+            if (!context.getTheme().resolveAttribute(
+                    android.R.attr.listPreferredItemHeight, outValue, true)) {
+                throw new IllegalStateException(
+                        "Expected theme to define listPreferredItemHeight.");
+            }
+            mVerticalScrollFactor = outValue.getDimension(
+                    context.getResources().getDisplayMetrics());
+        }
+        return mVerticalScrollFactor;
+    }
+
+    protected void onOverScrolled(int scrollX, int scrollY,
+            boolean clampedX, boolean clampedY) {
+        super.scrollTo(scrollX, scrollY);
+    }
+
+    boolean overScrollByCompat(int deltaX, int deltaY,
+            int scrollX, int scrollY,
+            int scrollRangeX, int scrollRangeY,
+            int maxOverScrollX, int maxOverScrollY,
+            boolean isTouchEvent) {
+        final int overScrollMode = ViewCompat.getOverScrollMode(this);
+        final boolean canScrollHorizontal =
+                computeHorizontalScrollRange() > computeHorizontalScrollExtent();
+        final boolean canScrollVertical =
+                computeVerticalScrollRange() > computeVerticalScrollExtent();
+        final boolean overScrollHorizontal = overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
+                (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
+        final boolean overScrollVertical = overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
+                (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
+
+        int newScrollX = scrollX + deltaX;
+        if (!overScrollHorizontal) {
+            maxOverScrollX = 0;
+        }
+
+        int newScrollY = scrollY + deltaY;
+        if (!overScrollVertical) {
+            maxOverScrollY = 0;
+        }
+
+        // Clamp values if at the limits and record
+        final int left = -maxOverScrollX;
+        final int right = maxOverScrollX + scrollRangeX;
+        final int top = -maxOverScrollY;
+        final int bottom = maxOverScrollY + scrollRangeY;
+
+        boolean clampedX = false;
+        if (newScrollX > right) {
+            newScrollX = right;
+            clampedX = true;
+        } else if (newScrollX < left) {
+            newScrollX = left;
+            clampedX = true;
+        }
+
+        boolean clampedY = false;
+        if (newScrollY > bottom) {
+            newScrollY = bottom;
+            clampedY = true;
+        } else if (newScrollY < top) {
+            newScrollY = top;
+            clampedY = true;
+        }
+
+        onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
+
+        return clampedX || clampedY;
+    }
+
+    private int getScrollRange() {
+        int scrollRange = 0;
+        if (getChildCount() > 0) {
+            View child = getChildAt(0);
+            scrollRange = Math.max(0,
+                    child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop()));
+        }
+        return scrollRange;
+    }
+
+    /**
+     * <p>
+     * Finds the next focusable component that fits in the specified bounds.
+     * </p>
+     *
+     * @param topFocus look for a candidate is the one at the top of the bounds
+     *                 if topFocus is true, or at the bottom of the bounds if topFocus is
+     *                 false
+     * @param top      the top offset of the bounds in which a focusable must be
+     *                 found
+     * @param bottom   the bottom offset of the bounds in which a focusable must
+     *                 be found
+     * @return the next focusable component in the bounds or null if none can
+     *         be found
+     */
+    private View findFocusableViewInBounds(boolean topFocus, int top, int bottom) {
+
+        List<View> focusables = getFocusables(View.FOCUS_FORWARD);
+        View focusCandidate = null;
+
+        /*
+         * A fully contained focusable is one where its top is below the bound's
+         * top, and its bottom is above the bound's bottom. A partially
+         * contained focusable is one where some part of it is within the
+         * bounds, but it also has some part that is not within bounds.  A fully contained
+         * focusable is preferred to a partially contained focusable.
+         */
+        boolean foundFullyContainedFocusable = false;
+
+        int count = focusables.size();
+        for (int i = 0; i < count; i++) {
+            View view = focusables.get(i);
+            int viewTop = view.getTop();
+            int viewBottom = view.getBottom();
+
+            if (top < viewBottom && viewTop < bottom) {
+                /*
+                 * the focusable is in the target area, it is a candidate for
+                 * focusing
+                 */
+
+                final boolean viewIsFullyContained = (top < viewTop) &&
+                        (viewBottom < bottom);
+
+                if (focusCandidate == null) {
+                    /* No candidate, take this one */
+                    focusCandidate = view;
+                    foundFullyContainedFocusable = viewIsFullyContained;
+                } else {
+                    final boolean viewIsCloserToBoundary =
+                            (topFocus && viewTop < focusCandidate.getTop()) ||
+                                    (!topFocus && viewBottom > focusCandidate
+                                            .getBottom());
+
+                    if (foundFullyContainedFocusable) {
+                        if (viewIsFullyContained && viewIsCloserToBoundary) {
+                            /*
+                             * We're dealing with only fully contained views, so
+                             * it has to be closer to the boundary to beat our
+                             * candidate
+                             */
+                            focusCandidate = view;
+                        }
+                    } else {
+                        if (viewIsFullyContained) {
+                            /* Any fully contained view beats a partially contained view */
+                            focusCandidate = view;
+                            foundFullyContainedFocusable = true;
+                        } else if (viewIsCloserToBoundary) {
+                            /*
+                             * Partially contained view beats another partially
+                             * contained view if it's closer
+                             */
+                            focusCandidate = view;
+                        }
+                    }
+                }
+            }
+        }
+
+        return focusCandidate;
+    }
+
+    /**
+     * <p>Handles scrolling in response to a "page up/down" shortcut press. This
+     * method will scroll the view by one page up or down and give the focus
+     * to the topmost/bottommost component in the new visible area. If no
+     * component is a good candidate for focus, this scrollview reclaims the
+     * focus.</p>
+     *
+     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
+     *                  to go one page up or
+     *                  {@link android.view.View#FOCUS_DOWN} to go one page down
+     * @return true if the key event is consumed by this method, false otherwise
+     */
+    public boolean pageScroll(int direction) {
+        boolean down = direction == View.FOCUS_DOWN;
+        int height = getHeight();
+
+        if (down) {
+            mTempRect.top = getScrollY() + height;
+            int count = getChildCount();
+            if (count > 0) {
+                View view = getChildAt(count - 1);
+                if (mTempRect.top + height > view.getBottom()) {
+                    mTempRect.top = view.getBottom() - height;
+                }
+            }
+        } else {
+            mTempRect.top = getScrollY() - height;
+            if (mTempRect.top < 0) {
+                mTempRect.top = 0;
+            }
+        }
+        mTempRect.bottom = mTempRect.top + height;
+
+        return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
+    }
+
+    /**
+     * <p>Handles scrolling in response to a "home/end" shortcut press. This
+     * method will scroll the view to the top or bottom and give the focus
+     * to the topmost/bottommost component in the new visible area. If no
+     * component is a good candidate for focus, this scrollview reclaims the
+     * focus.</p>
+     *
+     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
+     *                  to go the top of the view or
+     *                  {@link android.view.View#FOCUS_DOWN} to go the bottom
+     * @return true if the key event is consumed by this method, false otherwise
+     */
+    public boolean fullScroll(int direction) {
+        boolean down = direction == View.FOCUS_DOWN;
+        int height = getHeight();
+
+        mTempRect.top = 0;
+        mTempRect.bottom = height;
+
+        if (down) {
+            int count = getChildCount();
+            if (count > 0) {
+                View view = getChildAt(count - 1);
+                mTempRect.bottom = view.getBottom() + getPaddingBottom();
+                mTempRect.top = mTempRect.bottom - height;
+            }
+        }
+
+        return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
+    }
+
+    /**
+     * <p>Scrolls the view to make the area defined by <code>top</code> and
+     * <code>bottom</code> visible. This method attempts to give the focus
+     * to a component visible in this area. If no component can be focused in
+     * the new visible area, the focus is reclaimed by this ScrollView.</p>
+     *
+     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
+     *                  to go upward, {@link android.view.View#FOCUS_DOWN} to downward
+     * @param top       the top offset of the new area to be made visible
+     * @param bottom    the bottom offset of the new area to be made visible
+     * @return true if the key event is consumed by this method, false otherwise
+     */
+    private boolean scrollAndFocus(int direction, int top, int bottom) {
+        boolean handled = true;
+
+        int height = getHeight();
+        int containerTop = getScrollY();
+        int containerBottom = containerTop + height;
+        boolean up = direction == View.FOCUS_UP;
+
+        View newFocused = findFocusableViewInBounds(up, top, bottom);
+        if (newFocused == null) {
+            newFocused = this;
+        }
+
+        if (top >= containerTop && bottom <= containerBottom) {
+            handled = false;
+        } else {
+            int delta = up ? (top - containerTop) : (bottom - containerBottom);
+            doScrollY(delta);
+        }
+
+        if (newFocused != findFocus()) newFocused.requestFocus(direction);
+
+        return handled;
+    }
+
+    /**
+     * Handle scrolling in response to an up or down arrow click.
+     *
+     * @param direction The direction corresponding to the arrow key that was
+     *                  pressed
+     * @return True if we consumed the event, false otherwise
+     */
+    public boolean arrowScroll(int direction) {
+
+        View currentFocused = findFocus();
+        if (currentFocused == this) currentFocused = null;
+
+        View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
+
+        final int maxJump = getMaxScrollAmount();
+
+        if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getHeight())) {
+            nextFocused.getDrawingRect(mTempRect);
+            offsetDescendantRectToMyCoords(nextFocused, mTempRect);
+            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
+            doScrollY(scrollDelta);
+            nextFocused.requestFocus(direction);
+        } else {
+            // no new focus
+            int scrollDelta = maxJump;
+
+            if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) {
+                scrollDelta = getScrollY();
+            } else if (direction == View.FOCUS_DOWN) {
+                if (getChildCount() > 0) {
+                    int daBottom = getChildAt(0).getBottom();
+                    int screenBottom = getScrollY() + getHeight() - getPaddingBottom();
+                    if (daBottom - screenBottom < maxJump) {
+                        scrollDelta = daBottom - screenBottom;
+                    }
+                }
+            }
+            if (scrollDelta == 0) {
+                return false;
+            }
+            doScrollY(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta);
+        }
+
+        if (currentFocused != null && currentFocused.isFocused()
+                && isOffScreen(currentFocused)) {
+            // previously focused item still has focus and is off screen, give
+            // it up (take it back to ourselves)
+            // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are
+            // sure to
+            // get it)
+            final int descendantFocusability = getDescendantFocusability();  // save
+            setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+            requestFocus();
+            setDescendantFocusability(descendantFocusability);  // restore
+        }
+        return true;
+    }
+
+    /**
+     * @return whether the descendant of this scroll view is scrolled off
+     *  screen.
+     */
+    private boolean isOffScreen(View descendant) {
+        return !isWithinDeltaOfScreen(descendant, 0, getHeight());
+    }
+
+    /**
+     * @return whether the descendant of this scroll view is within delta
+     *  pixels of being on the screen.
+     */
+    private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) {
+        descendant.getDrawingRect(mTempRect);
+        offsetDescendantRectToMyCoords(descendant, mTempRect);
+
+        return (mTempRect.bottom + delta) >= getScrollY()
+                && (mTempRect.top - delta) <= (getScrollY() + height);
+    }
+
+    /**
+     * Smooth scroll by a Y delta
+     *
+     * @param delta the number of pixels to scroll by on the Y axis
+     */
+    private void doScrollY(int delta) {
+        if (delta != 0) {
+            if (mSmoothScrollingEnabled) {
+                smoothScrollBy(0, delta);
+            } else {
+                scrollBy(0, delta);
+            }
+        }
+    }
+
+    /**
+     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
+     *
+     * @param dx the number of pixels to scroll by on the X axis
+     * @param dy the number of pixels to scroll by on the Y axis
+     */
+    public final void smoothScrollBy(int dx, int dy) {
+        if (getChildCount() == 0) {
+            // Nothing to do.
+            return;
+        }
+        long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
+        if (duration > ANIMATED_SCROLL_GAP) {
+            final int height = getHeight() - getPaddingBottom() - getPaddingTop();
+            final int bottom = getChildAt(0).getHeight();
+            final int maxY = Math.max(0, bottom - height);
+            final int scrollY = getScrollY();
+            dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
+
+            mScroller.startScroll(getScrollX(), scrollY, 0, dy);
+            ViewCompat.postInvalidateOnAnimation(this);
+        } else {
+            if (!mScroller.isFinished()) {
+                mScroller.abortAnimation();
+            }
+            scrollBy(dx, dy);
+        }
+        mLastScroll = AnimationUtils.currentAnimationTimeMillis();
+    }
+
+    /**
+     * Like {@link #scrollTo}, but scroll smoothly instead of immediately.
+     *
+     * @param x the position where to scroll on the X axis
+     * @param y the position where to scroll on the Y axis
+     */
+    public final void smoothScrollTo(int x, int y) {
+        smoothScrollBy(x - getScrollX(), y - getScrollY());
+    }
+
+    /**
+     * <p>The scroll range of a scroll view is the overall height of all of its
+     * children.</p>
+     */
+    @Override
+    protected int computeVerticalScrollRange() {
+        final int count = getChildCount();
+        final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop();
+        if (count == 0) {
+            return contentHeight;
+        }
+
+        int scrollRange = getChildAt(0).getBottom();
+        final int scrollY = getScrollY();
+        final int overscrollBottom = Math.max(0, scrollRange - contentHeight);
+        if (scrollY < 0) {
+            scrollRange -= scrollY;
+        } else if (scrollY > overscrollBottom) {
+            scrollRange += scrollY - overscrollBottom;
+        }
+
+        return scrollRange;
+    }
+
+    @Override
+    protected int computeVerticalScrollOffset() {
+        return Math.max(0, super.computeVerticalScrollOffset());
+    }
+
+    @Override
+    protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
+        ViewGroup.LayoutParams lp = child.getLayoutParams();
+
+        int childWidthMeasureSpec;
+        int childHeightMeasureSpec;
+
+        childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft()
+                + getPaddingRight(), lp.width);
+
+        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+
+        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+    }
+
+    @Override
+    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
+            int parentHeightMeasureSpec, int heightUsed) {
+        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+
+        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
+                getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
+                        + widthUsed, lp.width);
+        final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);
+
+        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+    }
+
+    @Override
+    public void computeScroll() {
+        if (mScroller.computeScrollOffset()) {
+            int oldX = getScrollX();
+            int oldY = getScrollY();
+            int x = mScroller.getCurrX();
+            int y = mScroller.getCurrY();
+
+            if (oldX != x || oldY != y) {
+                final int range = getScrollRange();
+                final int overscrollMode = ViewCompat.getOverScrollMode(this);
+                final boolean canOverscroll = overscrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
+                        (overscrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+
+                overScrollByCompat(x - oldX, y - oldY, oldX, oldY, 0, range,
+                        0, 0, false);
+
+                if (canOverscroll) {
+                    ensureGlows();
+                    if (y <= 0 && oldY > 0) {
+                        mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
+                    } else if (y >= range && oldY < range) {
+                        mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Scrolls the view to the given child.
+     *
+     * @param child the View to scroll to
+     */
+    private void scrollToChild(View child) {
+        child.getDrawingRect(mTempRect);
+
+        /* Offset from child's local coordinates to ScrollView coordinates */
+        offsetDescendantRectToMyCoords(child, mTempRect);
+
+        int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
+
+        if (scrollDelta != 0) {
+            scrollBy(0, scrollDelta);
+        }
+    }
+
+    /**
+     * If rect is off screen, scroll just enough to get it (or at least the
+     * first screen size chunk of it) on screen.
+     *
+     * @param rect      The rectangle.
+     * @param immediate True to scroll immediately without animation
+     * @return true if scrolling was performed
+     */
+    private boolean scrollToChildRect(Rect rect, boolean immediate) {
+        final int delta = computeScrollDeltaToGetChildRectOnScreen(rect);
+        final boolean scroll = delta != 0;
+        if (scroll) {
+            if (immediate) {
+                scrollBy(0, delta);
+            } else {
+                smoothScrollBy(0, delta);
+            }
+        }
+        return scroll;
+    }
+
+    /**
+     * Compute the amount to scroll in the Y direction in order to get
+     * a rectangle completely on the screen (or, if taller than the screen,
+     * at least the first screen size chunk of it).
+     *
+     * @param rect The rect.
+     * @return The scroll delta.
+     */
+    protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
+        if (getChildCount() == 0) return 0;
+
+        int height = getHeight();
+        int screenTop = getScrollY();
+        int screenBottom = screenTop + height;
+
+        int fadingEdge = getVerticalFadingEdgeLength();
+
+        // leave room for top fading edge as long as rect isn't at very top
+        if (rect.top > 0) {
+            screenTop += fadingEdge;
+        }
+
+        // leave room for bottom fading edge as long as rect isn't at very bottom
+        if (rect.bottom < getChildAt(0).getHeight()) {
+            screenBottom -= fadingEdge;
+        }
+
+        int scrollYDelta = 0;
+
+        if (rect.bottom > screenBottom && rect.top > screenTop) {
+            // need to move down to get it in view: move down just enough so
+            // that the entire rectangle is in view (or at least the first
+            // screen size chunk).
+
+            if (rect.height() > height) {
+                // just enough to get screen size chunk on
+                scrollYDelta += (rect.top - screenTop);
+            } else {
+                // get entire rect at bottom of screen
+                scrollYDelta += (rect.bottom - screenBottom);
+            }
+
+            // make sure we aren't scrolling beyond the end of our content
+            int bottom = getChildAt(0).getBottom();
+            int distanceToBottom = bottom - screenBottom;
+            scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
+
+        } else if (rect.top < screenTop && rect.bottom < screenBottom) {
+            // need to move up to get it in view: move up just enough so that
+            // entire rectangle is in view (or at least the first screen
+            // size chunk of it).
+
+            if (rect.height() > height) {
+                // screen size chunk
+                scrollYDelta -= (screenBottom - rect.bottom);
+            } else {
+                // entire rect at top
+                scrollYDelta -= (screenTop - rect.top);
+            }
+
+            // make sure we aren't scrolling any further than the top our content
+            scrollYDelta = Math.max(scrollYDelta, -getScrollY());
+        }
+        return scrollYDelta;
+    }
+
+    @Override
+    public void requestChildFocus(View child, View focused) {
+        if (!mIsLayoutDirty) {
+            scrollToChild(focused);
+        } else {
+            // The child may not be laid out yet, we can't compute the scroll yet
+            mChildToScrollTo = focused;
+        }
+        super.requestChildFocus(child, focused);
+    }
+
+
+    /**
+     * When looking for focus in children of a scroll view, need to be a little
+     * more careful not to give focus to something that is scrolled off screen.
+     *
+     * This is more expensive than the default {@link android.view.ViewGroup}
+     * implementation, otherwise this behavior might have been made the default.
+     */
+    @Override
+    protected boolean onRequestFocusInDescendants(int direction,
+            Rect previouslyFocusedRect) {
+
+        // convert from forward / backward notation to up / down / left / right
+        // (ugh).
+        if (direction == View.FOCUS_FORWARD) {
+            direction = View.FOCUS_DOWN;
+        } else if (direction == View.FOCUS_BACKWARD) {
+            direction = View.FOCUS_UP;
+        }
+
+        final View nextFocus = previouslyFocusedRect == null ?
+                FocusFinder.getInstance().findNextFocus(this, null, direction) :
+                FocusFinder.getInstance().findNextFocusFromRect(this,
+                        previouslyFocusedRect, direction);
+
+        if (nextFocus == null) {
+            return false;
+        }
+
+        if (isOffScreen(nextFocus)) {
+            return false;
+        }
+
+        return nextFocus.requestFocus(direction, previouslyFocusedRect);
+    }
+
+    @Override
+    public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
+            boolean immediate) {
+        // offset into coordinate space of this scroll view
+        rectangle.offset(child.getLeft() - child.getScrollX(),
+                child.getTop() - child.getScrollY());
+
+        return scrollToChildRect(rectangle, immediate);
+    }
+
+    @Override
+    public void requestLayout() {
+        mIsLayoutDirty = true;
+        super.requestLayout();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        mIsLayoutDirty = false;
+        // Give a child focus if it needs it
+        if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
+            scrollToChild(mChildToScrollTo);
+        }
+        mChildToScrollTo = null;
+
+        if (!mIsLaidOut) {
+            if (mSavedState != null) {
+                scrollTo(getScrollX(), mSavedState.scrollPosition);
+                mSavedState = null;
+            } // mScrollY default value is "0"
+
+            final int childHeight = (getChildCount() > 0) ? getChildAt(0).getMeasuredHeight() : 0;
+            final int scrollRange = Math.max(0,
+                    childHeight - (b - t - getPaddingBottom() - getPaddingTop()));
+
+            // Don't forget to clamp
+            if (getScrollY() > scrollRange) {
+                scrollTo(getScrollX(), scrollRange);
+            } else if (getScrollY() < 0) {
+                scrollTo(getScrollX(), 0);
+            }
+        }
+
+        // Calling this with the present values causes it to re-claim them
+        scrollTo(getScrollX(), getScrollY());
+        mIsLaidOut = true;
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        mIsLaidOut = false;
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+
+        View currentFocused = findFocus();
+        if (null == currentFocused || this == currentFocused)
+            return;
+
+        // If the currently-focused view was visible on the screen when the
+        // screen was at the old height, then scroll the screen to make that
+        // view visible with the new screen height.
+        if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) {
+            currentFocused.getDrawingRect(mTempRect);
+            offsetDescendantRectToMyCoords(currentFocused, mTempRect);
+            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
+            doScrollY(scrollDelta);
+        }
+    }
+
+    /**
+     * Return true if child is a descendant of parent, (or equal to the parent).
+     */
+    private static boolean isViewDescendantOf(View child, View parent) {
+        if (child == parent) {
+            return true;
+        }
+
+        final ViewParent theParent = child.getParent();
+        return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
+    }
+
+    /**
+     * Fling the scroll view
+     *
+     * @param velocityY The initial velocity in the Y direction. Positive
+     *                  numbers mean that the finger/cursor is moving down the screen,
+     *                  which means we want to scroll towards the top.
+     */
+    public void fling(int velocityY) {
+        if (getChildCount() > 0) {
+            int height = getHeight() - getPaddingBottom() - getPaddingTop();
+            int bottom = getChildAt(0).getHeight();
+
+            mScroller.fling(getScrollX(), getScrollY(), 0, velocityY, 0, 0, 0,
+                    Math.max(0, bottom - height), 0, height/2);
+
+            ViewCompat.postInvalidateOnAnimation(this);
+        }
+    }
+
+    private void flingWithNestedDispatch(int velocityY) {
+        final int scrollY = getScrollY();
+        final boolean canFling = (scrollY > 0 || velocityY > 0) &&
+                (scrollY < getScrollRange() || velocityY < 0);
+        if (!dispatchNestedPreFling(0, velocityY)) {
+            dispatchNestedFling(0, velocityY, canFling);
+            if (canFling) {
+                fling(velocityY);
+            }
+        }
+    }
+
+    private void endDrag() {
+        mIsBeingDragged = false;
+
+        recycleVelocityTracker();
+
+        if (mEdgeGlowTop != null) {
+            mEdgeGlowTop.onRelease();
+            mEdgeGlowBottom.onRelease();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This version also clamps the scrolling to the bounds of our child.
+     */
+    @Override
+    public void scrollTo(int x, int y) {
+        // we rely on the fact the View.scrollBy calls scrollTo.
+        if (getChildCount() > 0) {
+            View child = getChildAt(0);
+            x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth());
+            y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight());
+            if (x != getScrollX() || y != getScrollY()) {
+                super.scrollTo(x, y);
+            }
+        }
+    }
+
+    private void ensureGlows() {
+        if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {
+            if (mEdgeGlowTop == null) {
+                Context context = getContext();
+                mEdgeGlowTop = new EdgeEffectCompat(context);
+                mEdgeGlowBottom = new EdgeEffectCompat(context);
+            }
+        } else {
+            mEdgeGlowTop = null;
+            mEdgeGlowBottom = null;
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        if (mEdgeGlowTop != null) {
+            final int scrollY = getScrollY();
+            if (!mEdgeGlowTop.isFinished()) {
+                final int restoreCount = canvas.save();
+                final int width = getWidth() - getPaddingLeft() - getPaddingRight();
+
+                canvas.translate(getPaddingLeft(), Math.min(0, scrollY));
+                mEdgeGlowTop.setSize(width, getHeight());
+                if (mEdgeGlowTop.draw(canvas)) {
+                    ViewCompat.postInvalidateOnAnimation(this);
+                }
+                canvas.restoreToCount(restoreCount);
+            }
+            if (!mEdgeGlowBottom.isFinished()) {
+                final int restoreCount = canvas.save();
+                final int width = getWidth() - getPaddingLeft() - getPaddingRight();
+                final int height = getHeight();
+
+                canvas.translate(-width + getPaddingLeft(),
+                        Math.max(getScrollRange(), scrollY) + height);
+                canvas.rotate(180, width, 0);
+                mEdgeGlowBottom.setSize(width, height);
+                if (mEdgeGlowBottom.draw(canvas)) {
+                    ViewCompat.postInvalidateOnAnimation(this);
+                }
+                canvas.restoreToCount(restoreCount);
+            }
+        }
+    }
+
+    private static int clamp(int n, int my, int child) {
+        if (my >= child || n < 0) {
+            /* my >= child is this case:
+             *                    |--------------- me ---------------|
+             *     |------ child ------|
+             * or
+             *     |--------------- me ---------------|
+             *            |------ child ------|
+             * or
+             *     |--------------- me ---------------|
+             *                                  |------ child ------|
+             *
+             * n < 0 is this case:
+             *     |------ me ------|
+             *                    |-------- child --------|
+             *     |-- mScrollX --|
+             */
+            return 0;
+        }
+        if ((my+n) > child) {
+            /* this case:
+             *                    |------ me ------|
+             *     |------ child ------|
+             *     |-- mScrollX --|
+             */
+            return child-my;
+        }
+        return n;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+        mSavedState = ss;
+        requestLayout();
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState ss = new SavedState(superState);
+        ss.scrollPosition = getScrollY();
+        return ss;
+    }
+
+    static class SavedState extends BaseSavedState {
+        public int scrollPosition;
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        public SavedState(Parcel source) {
+            super(source);
+            scrollPosition = source.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(scrollPosition);
+        }
+
+        @Override
+        public String toString() {
+            return "HorizontalScrollView.SavedState{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " scrollPosition=" + scrollPosition + "}";
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR
+                = new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    static class AccessibilityDelegate extends AccessibilityDelegateCompat {
+        @Override
+        public boolean performAccessibilityAction(View host, int action, Bundle arguments) {
+            if (super.performAccessibilityAction(host, action, arguments)) {
+                return true;
+            }
+            final NestedScrollView nsvHost = (NestedScrollView) host;
+            if (!nsvHost.isEnabled()) {
+                return false;
+            }
+            switch (action) {
+                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
+                    final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
+                            - nsvHost.getPaddingTop();
+                    final int targetScrollY = Math.min(nsvHost.getScrollY() + viewportHeight,
+                            nsvHost.getScrollRange());
+                    if (targetScrollY != nsvHost.getScrollY()) {
+                        nsvHost.smoothScrollTo(0, targetScrollY);
+                        return true;
+                    }
+                }
+                return false;
+                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
+                    final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
+                            - nsvHost.getPaddingTop();
+                    final int targetScrollY = Math.max(nsvHost.getScrollY() - viewportHeight, 0);
+                    if (targetScrollY != nsvHost.getScrollY()) {
+                        nsvHost.smoothScrollTo(0, targetScrollY);
+                        return true;
+                    }
+                }
+                return false;
+            }
+            return false;
+        }
+
+        @Override
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+            super.onInitializeAccessibilityNodeInfo(host, info);
+            final NestedScrollView nsvHost = (NestedScrollView) host;
+            info.setClassName(ScrollView.class.getName());
+            if (nsvHost.isEnabled()) {
+                final int scrollRange = nsvHost.getScrollRange();
+                if (scrollRange > 0) {
+                    info.setScrollable(true);
+                    if (nsvHost.getScrollY() > 0) {
+                        info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
+                    }
+                    if (nsvHost.getScrollY() < scrollRange) {
+                        info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+            super.onInitializeAccessibilityEvent(host, event);
+            final NestedScrollView nsvHost = (NestedScrollView) host;
+            event.setClassName(ScrollView.class.getName());
+            final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
+            final boolean scrollable = nsvHost.getScrollRange() > 0;
+            record.setScrollable(scrollable);
+            record.setScrollX(nsvHost.getScrollX());
+            record.setScrollY(nsvHost.getScrollY());
+            record.setMaxScrollX(nsvHost.getScrollX());
+            record.setMaxScrollY(nsvHost.getScrollRange());
+        }
+    }
+}
diff --git a/v4/java/android/support/v4/widget/PopupWindowCompat.java b/v4/java/android/support/v4/widget/PopupWindowCompat.java
index be96a73..c765522 100644
--- a/v4/java/android/support/v4/widget/PopupWindowCompat.java
+++ b/v4/java/android/support/v4/widget/PopupWindowCompat.java
@@ -17,7 +17,6 @@
 package android.support.v4.widget;
 
 import android.view.View;
-import android.view.View.OnTouchListener;
 import android.widget.PopupWindow;
 
 /**
@@ -31,6 +30,8 @@
     interface PopupWindowImpl {
         public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
                 int gravity);
+        public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor);
+        public boolean getOverlapAnchor(PopupWindow popupWindow);
     }
 
     /**
@@ -42,6 +43,16 @@
                 int gravity) {
             popup.showAsDropDown(anchor, xoff, yoff);
         }
+
+        @Override
+        public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+            // noop
+        }
+
+        @Override
+        public boolean getOverlapAnchor(PopupWindow popupWindow) {
+            return false;
+        }
     }
 
     /**
@@ -55,13 +66,41 @@
         }
     }
 
+    static class Api21PopupWindowImpl extends KitKatPopupWindowImpl {
+        @Override
+        public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+            PopupWindowCompatApi21.setOverlapAnchor(popupWindow, overlapAnchor);
+        }
+
+        @Override
+        public boolean getOverlapAnchor(PopupWindow popupWindow) {
+            return PopupWindowCompatApi21.getOverlapAnchor(popupWindow);
+        }
+    }
+
+    static class Api23PopupWindowImpl extends Api21PopupWindowImpl {
+        @Override
+        public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+            PopupWindowCompatApi23.setOverlapAnchor(popupWindow, overlapAnchor);
+        }
+
+        @Override
+        public boolean getOverlapAnchor(PopupWindow popupWindow) {
+            return PopupWindowCompatApi23.getOverlapAnchor(popupWindow);
+        }
+    }
+
     /**
      * Select the correct implementation to use for the current platform.
      */
     static final PopupWindowImpl IMPL;
     static {
         final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 19) {
+        if (version >= 23) {
+            IMPL = new Api23PopupWindowImpl();
+        } else if (version >= 21) {
+            IMPL = new Api21PopupWindowImpl();
+        } else if (version >= 19) {
             IMPL = new KitKatPopupWindowImpl();
         } else {
             IMPL = new BasePopupWindowImpl();
@@ -92,4 +131,24 @@
             int gravity) {
         IMPL.showAsDropDown(popup, anchor, xoff, yoff, gravity);
     }
+
+    /**
+     * Sets whether the popup window should overlap its anchor view when
+     * displayed as a drop-down.
+     *
+     * @param overlapAnchor Whether the popup should overlap its anchor.
+     */
+    public static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+        IMPL.setOverlapAnchor(popupWindow, overlapAnchor);
+    }
+
+    /**
+     * Returns whether the popup window should overlap its anchor view when
+     * displayed as a drop-down.
+     *
+     * @return Whether the popup should overlap its anchor.
+     */
+    public static boolean getOverlapAnchor(PopupWindow popupWindow) {
+        return IMPL.getOverlapAnchor(popupWindow);
+    }
 }
diff --git a/v4/java/android/support/v4/widget/SlidingPaneLayout.java b/v4/java/android/support/v4/widget/SlidingPaneLayout.java
index 391ba99..c5db3ef 100644
--- a/v4/java/android/support/v4/widget/SlidingPaneLayout.java
+++ b/v4/java/android/support/v4/widget/SlidingPaneLayout.java
@@ -29,6 +29,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.ColorInt;
 import android.support.annotation.DrawableRes;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.MotionEventCompat;
@@ -297,13 +298,14 @@
      *
      * @param color An ARGB-packed color value
      */
-    public void setSliderFadeColor(int color) {
+    public void setSliderFadeColor(@ColorInt int color) {
         mSliderFadeColor = color;
     }
 
     /**
      * @return The ARGB-packed color value used to fade the sliding pane
      */
+    @ColorInt
     public int getSliderFadeColor() {
         return mSliderFadeColor;
     }
@@ -314,13 +316,14 @@
      *
      * @param color An ARGB-packed color value
      */
-    public void setCoveredFadeColor(int color) {
+    public void setCoveredFadeColor(@ColorInt int color) {
         mCoveredFadeColor = color;
     }
 
     /**
      * @return The ARGB-packed color value used to fade the fixed pane
      */
+    @ColorInt
     public int getCoveredFadeColor() {
         return mCoveredFadeColor;
     }
diff --git a/v4/java/android/support/v4/widget/Space.java b/v4/java/android/support/v4/widget/Space.java
new file mode 100644
index 0000000..4857479
--- /dev/null
+++ b/v4/java/android/support/v4/widget/Space.java
@@ -0,0 +1,84 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Space is a lightweight {@link View} subclass that may be used to create gaps between components
+ * in general purpose layouts.
+ */
+public class Space extends View {
+
+    public Space(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        if (getVisibility() == VISIBLE) {
+            setVisibility(INVISIBLE);
+        }
+    }
+
+    public Space(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public Space(Context context) {
+        this(context, null);
+    }
+
+    /**
+     * Draw nothing.
+     *
+     * @param canvas an unused parameter.
+     */
+    @Override
+    public void draw(Canvas canvas) {
+    }
+
+    /**
+     * Compare to: {@link View#getDefaultSize(int, int)}
+     * If mode is AT_MOST, return the child size instead of the parent size
+     * (unless it is too big).
+     */
+    private static int getDefaultSize2(int size, int measureSpec) {
+        int result = size;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        switch (specMode) {
+            case MeasureSpec.UNSPECIFIED:
+                result = size;
+                break;
+            case MeasureSpec.AT_MOST:
+                result = Math.min(size, specSize);
+                break;
+            case MeasureSpec.EXACTLY:
+                result = specSize;
+                break;
+        }
+        return result;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(
+                getDefaultSize2(getSuggestedMinimumWidth(), widthMeasureSpec),
+                getDefaultSize2(getSuggestedMinimumHeight(), heightMeasureSpec));
+    }
+}
\ No newline at end of file
diff --git a/v4/java/android/support/v4/widget/SwipeProgressBar.java b/v4/java/android/support/v4/widget/SwipeProgressBar.java
index ba60d89..2314a5e 100644
--- a/v4/java/android/support/v4/widget/SwipeProgressBar.java
+++ b/v4/java/android/support/v4/widget/SwipeProgressBar.java
@@ -21,6 +21,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.support.v4.view.ViewCompat;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.view.View;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -48,7 +49,7 @@
     private static final int FINISH_ANIMATION_DURATION_MS = 1000;
 
     // Interpolator for varying the speed of the animation.
-    private static final Interpolator INTERPOLATOR = BakedBezierInterpolator.getInstance();
+    private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
 
     private final Paint mPaint = new Paint();
     private final RectF mClipRect = new RectF();
diff --git a/v4/java/android/support/v4/widget/SwipeRefreshLayout.java b/v4/java/android/support/v4/widget/SwipeRefreshLayout.java
index 2e056e6..0618b06 100644
--- a/v4/java/android/support/v4/widget/SwipeRefreshLayout.java
+++ b/v4/java/android/support/v4/widget/SwipeRefreshLayout.java
@@ -19,7 +19,10 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.support.annotation.ColorInt;
+import android.support.annotation.ColorRes;
 import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ScrollingView;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -459,7 +462,7 @@
      *
      * @param colorRes Resource id of the color.
      */
-    public void setProgressBackgroundColorSchemeResource(int colorRes) {
+    public void setProgressBackgroundColorSchemeResource(@ColorRes int colorRes) {
         setProgressBackgroundColorSchemeColor(getResources().getColor(colorRes));
     }
 
@@ -468,7 +471,7 @@
      *
      * @param color
      */
-    public void setProgressBackgroundColorSchemeColor(int color) {
+    public void setProgressBackgroundColorSchemeColor(@ColorInt int color) {
         mCircleView.setBackgroundColor(color);
         mProgress.setBackgroundColor(color);
     }
@@ -477,7 +480,7 @@
      * @deprecated Use {@link #setColorSchemeResources(int...)}
      */
     @Deprecated
-    public void setColorScheme(int... colors) {
+    public void setColorScheme(@ColorInt int... colors) {
         setColorSchemeResources(colors);
     }
 
@@ -488,7 +491,7 @@
      *
      * @param colorResIds
      */
-    public void setColorSchemeResources(int... colorResIds) {
+    public void setColorSchemeResources(@ColorRes int... colorResIds) {
         final Resources res = getResources();
         int[] colorRes = new int[colorResIds.length];
         for (int i = 0; i < colorResIds.length; i++) {
@@ -504,6 +507,7 @@
      *
      * @param colors
      */
+    @ColorInt
     public void setColorSchemeColors(int... colors) {
         ensureTarget();
         mProgress.setColorSchemeColors(colors);
@@ -617,7 +621,7 @@
                         && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
                                 .getTop() < absListView.getPaddingTop());
             } else {
-                return mTarget.getScrollY() > 0;
+                return ViewCompat.canScrollVertically(mTarget, -1) || mTarget.getScrollY() > 0;
             }
         } else {
             return ViewCompat.canScrollVertically(mTarget, -1);
diff --git a/v4/java/android/support/v4/widget/TextViewCompat.java b/v4/java/android/support/v4/widget/TextViewCompat.java
new file mode 100644
index 0000000..c31196a
--- /dev/null
+++ b/v4/java/android/support/v4/widget/TextViewCompat.java
@@ -0,0 +1,203 @@
+/*
+ * 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.widget;
+
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.widget.TextView;
+
+/**
+ * Helper for accessing features in {@link TextView} introduced after API level
+ * 4 in a backwards compatible fashion.
+ */
+public class TextViewCompat {
+
+    // Hide constructor
+    private TextViewCompat() {
+    }
+
+    interface TextViewCompatImpl {
+
+        public void setCompoundDrawablesRelative(@NonNull TextView textView,
+                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+                @Nullable Drawable bottom);
+
+        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+                @Nullable Drawable bottom);
+
+        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+                int start, int top, int end, int bottom);
+
+    }
+
+    static class BaseTextViewCompatImpl implements TextViewCompatImpl {
+
+        @Override
+        public void setCompoundDrawablesRelative(@NonNull TextView textView,
+                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+                @Nullable Drawable bottom) {
+            textView.setCompoundDrawables(start, top, end, bottom);
+        }
+
+        @Override
+        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+                @Nullable Drawable bottom) {
+            textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
+        }
+
+        @Override
+        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+                int start, int top, int end, int bottom) {
+            textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
+        }
+
+    }
+
+    static class JbMr1TextViewCompatImpl extends BaseTextViewCompatImpl {
+
+        @Override
+        public void setCompoundDrawablesRelative(@NonNull TextView textView,
+                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+                @Nullable Drawable bottom) {
+            TextViewCompatJbMr1.setCompoundDrawablesRelative(textView, start, top, end, bottom);
+        }
+
+        @Override
+        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+                @Nullable Drawable bottom) {
+            TextViewCompatJbMr1.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
+                    start, top, end, bottom);
+        }
+
+        @Override
+        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+                int start, int top, int end, int bottom) {
+            TextViewCompatJbMr1.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
+                    start, top, end, bottom);
+        }
+
+    }
+
+    static class JbMr2TextViewCompatImpl extends JbMr1TextViewCompatImpl {
+
+        @Override
+        public void setCompoundDrawablesRelative(@NonNull TextView textView,
+                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+                @Nullable Drawable bottom) {
+            TextViewCompatJbMr2.setCompoundDrawablesRelative(textView, start, top, end, bottom);
+        }
+
+        @Override
+        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+                @Nullable Drawable bottom) {
+            TextViewCompatJbMr2
+                    .setCompoundDrawablesRelativeWithIntrinsicBounds(textView, start, top, end,
+                            bottom);
+        }
+
+        @Override
+        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+                int start, int top, int end, int bottom) {
+            TextViewCompatJbMr2.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
+                    start, top, end, bottom);
+        }
+    }
+
+    static final TextViewCompatImpl IMPL;
+
+    static {
+        final int version = Build.VERSION.SDK_INT;
+        if (version >= 18) {
+            IMPL = new JbMr2TextViewCompatImpl();
+        } else if (version >= 17) {
+            IMPL = new JbMr1TextViewCompatImpl();
+        } else {
+            IMPL = new BaseTextViewCompatImpl();
+        }
+    }
+
+    /**
+     * Sets the Drawables (if any) to appear to the start of, above, to the end
+     * of, and below the text. Use {@code null} if you do not want a Drawable
+     * there. The Drawables must already have had {@link Drawable#setBounds}
+     * called.
+     * <p/>
+     * Calling this method will overwrite any Drawables previously set using
+     * {@link TextView#setCompoundDrawables} or related methods.
+     *
+     * @param textView The TextView against which to invoke the method.
+     * @attr ref android.R.styleable#TextView_drawableStart
+     * @attr ref android.R.styleable#TextView_drawableTop
+     * @attr ref android.R.styleable#TextView_drawableEnd
+     * @attr ref android.R.styleable#TextView_drawableBottom
+     */
+    public static void setCompoundDrawablesRelative(@NonNull TextView textView,
+            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+            @Nullable Drawable bottom) {
+        IMPL.setCompoundDrawablesRelative(textView, start, top, end, bottom);
+    }
+
+    /**
+     * Sets the Drawables (if any) to appear to the start of, above, to the end
+     * of, and below the text. Use {@code null} if you do not want a Drawable
+     * there. The Drawables' bounds will be set to their intrinsic bounds.
+     * <p/>
+     * Calling this method will overwrite any Drawables previously set using
+     * {@link TextView#setCompoundDrawables} or related methods.
+     *
+     * @param textView The TextView against which to invoke the method.
+     * @attr ref android.R.styleable#TextView_drawableStart
+     * @attr ref android.R.styleable#TextView_drawableTop
+     * @attr ref android.R.styleable#TextView_drawableEnd
+     * @attr ref android.R.styleable#TextView_drawableBottom
+     */
+    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+            @Nullable Drawable bottom) {
+        IMPL.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, start, top, end, bottom);
+    }
+
+    /**
+     * Sets the Drawables (if any) to appear to the start of, above, to the end
+     * of, and below the text. Use 0 if you do not want a Drawable there. The
+     * Drawables' bounds will be set to their intrinsic bounds.
+     * <p/>
+     * Calling this method will overwrite any Drawables previously set using
+     * {@link TextView#setCompoundDrawables} or related methods.
+     *
+     * @param textView The TextView against which to invoke the method.
+     * @param start    Resource identifier of the start Drawable.
+     * @param top      Resource identifier of the top Drawable.
+     * @param end      Resource identifier of the end Drawable.
+     * @param bottom   Resource identifier of the bottom Drawable.
+     * @attr ref android.R.styleable#TextView_drawableStart
+     * @attr ref android.R.styleable#TextView_drawableTop
+     * @attr ref android.R.styleable#TextView_drawableEnd
+     * @attr ref android.R.styleable#TextView_drawableBottom
+     */
+    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+            int start, int top, int end, int bottom) {
+        IMPL.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, start, top, end, bottom);
+    }
+
+}
diff --git a/v4/java/android/support/v4/widget/ViewDragHelper.java b/v4/java/android/support/v4/widget/ViewDragHelper.java
index c6bebd3..3f4e571 100644
--- a/v4/java/android/support/v4/widget/ViewDragHelper.java
+++ b/v4/java/android/support/v4/widget/ViewDragHelper.java
@@ -1003,6 +1003,8 @@
             }
 
             case MotionEvent.ACTION_MOVE: {
+                if (mInitialMotionX == null || mInitialMotionY == null) break;
+
                 // First to cross a touch slop over a draggable view wins. Also report edge drags.
                 final int pointerCount = MotionEventCompat.getPointerCount(ev);
                 for (int i = 0; i < pointerCount; i++) {
diff --git a/v4/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java b/v4/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
index 6832b42..4865a8b 100644
--- a/v4/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
+++ b/v4/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
@@ -59,4 +59,8 @@
     public static int getWindowSystemUiVisibility(View view) {
         return view.getWindowSystemUiVisibility();
     }
+
+    public static boolean isPaddingRelative(View view) {
+        return view.isPaddingRelative();
+    }
 }
diff --git a/v4/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java b/v4/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
new file mode 100644
index 0000000..7c073c6
--- /dev/null
+++ b/v4/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
@@ -0,0 +1,49 @@
+/*
+ * 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.widget;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.widget.TextView;
+
+class TextViewCompatJbMr1 {
+
+    public static void setCompoundDrawablesRelative(@NonNull TextView textView,
+            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+            @Nullable Drawable bottom) {
+        boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        textView.setCompoundDrawables(rtl ? end : start, top, rtl ? start : end, bottom);
+    }
+
+    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+            @Nullable Drawable bottom) {
+        boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        textView.setCompoundDrawablesWithIntrinsicBounds(rtl ? end : start, top, rtl ? start : end,
+                bottom);
+    }
+
+    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+            int start, int top, int end, int bottom) {
+        boolean rtl = textView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        textView.setCompoundDrawablesWithIntrinsicBounds(rtl ? end : start, top, rtl ? start : end,
+                bottom);
+    }
+
+}
diff --git a/v4/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java b/v4/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
new file mode 100644
index 0000000..ec9fe61
--- /dev/null
+++ b/v4/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
@@ -0,0 +1,43 @@
+/*
+ * 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.widget;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.widget.TextView;
+
+class TextViewCompatJbMr2 {
+
+    public static void setCompoundDrawablesRelative(@NonNull TextView textView,
+            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+            @Nullable Drawable bottom) {
+        textView.setCompoundDrawablesRelative(start, top, end, bottom);
+    }
+
+    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
+            @Nullable Drawable bottom) {
+        textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
+    }
+
+    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
+            int start, int top, int end, int bottom) {
+        textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
+    }
+
+}
diff --git a/v4/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java b/v4/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java
new file mode 100644
index 0000000..ef05746
--- /dev/null
+++ b/v4/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 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.content;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+
+class ContentResolverCompatJellybean {
+    public static Cursor query(ContentResolver resolver, Uri uri, String[] projection,
+            String selection, String[] selectionArgs, String sortOrder,
+            Object cancellationSignalObj) {
+        return resolver.query(uri, projection, selection, selectionArgs, sortOrder,
+                (android.os.CancellationSignal)cancellationSignalObj);
+    }
+}
diff --git a/v4/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java b/v4/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java
new file mode 100644
index 0000000..6029286
--- /dev/null
+++ b/v4/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 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.os;
+
+class CancellationSignalCompatJellybean {
+    public static Object create() {
+        return new android.os.CancellationSignal();
+    }
+
+    public static void cancel(Object cancellationSignalObj) {
+        ((android.os.CancellationSignal)cancellationSignalObj).cancel();
+    }
+}
diff --git a/v4/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java b/v4/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
index 9767c27..b42c86b 100644
--- a/v4/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
+++ b/v4/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.graphics.drawable;
 
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 
 /**
@@ -29,4 +31,11 @@
     public static boolean isAutoMirrored(Drawable drawable) {
         return drawable.isAutoMirrored();
     }
+
+    public static Drawable wrapForTinting(Drawable drawable) {
+        if (!(drawable instanceof DrawableWrapperKitKat)) {
+            return new DrawableWrapperKitKat(drawable);
+        }
+        return drawable;
+    }
 }
diff --git a/v4/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java b/v4/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java
new file mode 100644
index 0000000..dc70c6e
--- /dev/null
+++ b/v4/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java
@@ -0,0 +1,36 @@
+/*
+ * 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.graphics.drawable;
+
+import android.graphics.drawable.Drawable;
+
+class DrawableWrapperKitKat extends DrawableWrapperHoneycomb {
+
+    DrawableWrapperKitKat(Drawable drawable) {
+        super(drawable);
+    }
+
+    @Override
+    public void setAutoMirrored(boolean mirrored) {
+        mDrawable.setAutoMirrored(mirrored);
+    }
+
+    @Override
+    public boolean isAutoMirrored() {
+        return mDrawable.isAutoMirrored();
+    }
+}
diff --git a/v4/kitkat/android/support/v4/view/ViewCompatKitKat.java b/v4/kitkat/android/support/v4/view/ViewCompatKitKat.java
index bbafe4c..eb067bc 100644
--- a/v4/kitkat/android/support/v4/view/ViewCompatKitKat.java
+++ b/v4/kitkat/android/support/v4/view/ViewCompatKitKat.java
@@ -21,7 +21,7 @@
 /**
  * KitKat-specific View API implementation.
  */
-public class ViewCompatKitKat {
+class ViewCompatKitKat {
     public static int getAccessibilityLiveRegion(View view) {
         return view.getAccessibilityLiveRegion();
     }
@@ -29,4 +29,8 @@
     public static void setAccessibilityLiveRegion(View view, int mode) {
         view.setAccessibilityLiveRegion(mode);
     }
+
+    public static boolean isLaidOut(View view) {
+        return view.isLaidOut();
+    }
 }
diff --git a/v4/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java b/v4/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
index 16af9bd..32fb86f 100644
--- a/v4/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
+++ b/v4/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
@@ -63,6 +63,14 @@
                 columnSpan, heading);
     }
 
+    public static void setContentInvalid(Object info, boolean contentInvalid) {
+        ((AccessibilityNodeInfo) info).setContentInvalid(contentInvalid);
+    }
+
+    public static boolean isContentInvalid(Object info) {
+        return ((AccessibilityNodeInfo) info).isContentInvalid();
+    }
+
     static class CollectionInfo {
         static int getColumnCount(Object info) {
             return ((AccessibilityNodeInfo.CollectionInfo) info).getColumnCount();
diff --git a/v7/appcompat/res/anim/abc_popup_enter.xml b/v7/appcompat/res/anim/abc_popup_enter.xml
new file mode 100644
index 0000000..91664da
--- /dev/null
+++ b/v7/appcompat/res/anim/abc_popup_enter.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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:shareInterpolator="false" >
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+           android:interpolator="@android:anim/decelerate_interpolator"
+           android:duration="@integer/abc_config_activityShortDur" />
+</set>
\ No newline at end of file
diff --git a/v7/appcompat/res/anim/abc_popup_exit.xml b/v7/appcompat/res/anim/abc_popup_exit.xml
new file mode 100644
index 0000000..db7e807
--- /dev/null
+++ b/v7/appcompat/res/anim/abc_popup_exit.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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:shareInterpolator="false" >
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+           android:interpolator="@android:anim/decelerate_interpolator"
+           android:duration="@integer/abc_config_activityShortDur" />
+</set>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/switch_thumb_material_dark.xml b/v7/appcompat/res/color/switch_thumb_material_dark.xml
new file mode 100644
index 0000000..6153382
--- /dev/null
+++ b/v7/appcompat/res/color/switch_thumb_material_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/switch_thumb_disabled_material_dark"/>
+    <item android:color="@color/switch_thumb_normal_material_dark"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/switch_thumb_material_light.xml b/v7/appcompat/res/color/switch_thumb_material_light.xml
new file mode 100644
index 0000000..94d7114
--- /dev/null
+++ b/v7/appcompat/res/color/switch_thumb_material_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/switch_thumb_disabled_material_light"/>
+    <item android:color="@color/switch_thumb_normal_material_light"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ab_share_pack_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_ab_share_pack_mtrl_alpha.9.png
index b07da0c..4d9f861 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ab_share_pack_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ab_share_pack_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_000.png b/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_000.png
index 7a9e9bd..9911008 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_000.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_015.png b/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_015.png
index 874edbf..69ff9dd 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_015.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_015.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_000.png b/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_000.png
index 0d3e1e7..9218981 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_000.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_015.png b/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_015.png
index a8c390e..a588576 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_015.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_015.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_rating_star_off_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_btn_rating_star_off_mtrl_alpha.png
index 51a895d..b184dbc 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_rating_star_off_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_btn_rating_star_off_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_rating_star_on_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_btn_rating_star_on_mtrl_alpha.png
index 2f59488..6549c52 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_rating_star_on_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_btn_rating_star_on_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/v7/appcompat/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00001.9.png
index 8e7b62f..705e0d9 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00001.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/v7/appcompat/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00012.9.png
index adcb9e9..b692710 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00012.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_cab_background_top_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_cab_background_top_mtrl_alpha.9.png
index e51ef28..2264398 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_cab_background_top_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
index 6c36eae..f61e8e3 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_clear_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_clear_mtrl_alpha.png
index 82459ea..0fd1556 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_clear_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_clear_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_commit_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_commit_search_api_mtrl_alpha.png
index 47263ea..65ccd8f 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_commit_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_commit_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_go_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_go_search_api_mtrl_alpha.png
index aa23c59..b9ff1db 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_go_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_go_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
index 03b1aac..70eb073 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_cut_mtrl_alpha.png
index 4c17541..e78bcaf 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
index 675f3ee..9a87820 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_paste_mtrl_am_alpha.png
index a30dc06..8610c50 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_paste_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_selectall_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_selectall_mtrl_alpha.png
index 413b220..2d971a9 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_selectall_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_share_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_share_mtrl_alpha.png
index 06e7a17..ee40812 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_share_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_share_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_search_api_mtrl_alpha.png
index f7382d3..b9baa0c 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_voice_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_voice_search_api_mtrl_alpha.png
index eefd59e..a87d2cd 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_voice_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_ic_voice_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_list_divider_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_list_divider_mtrl_alpha.9.png
index 2fa6d7e..1e571f5 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_list_divider_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_list_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_list_focused_holo.9.png b/v7/appcompat/res/drawable-hdpi/abc_list_focused_holo.9.png
index 5552708..c09ec90 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_list_focused_holo.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_list_focused_holo.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_list_longpressed_holo.9.png b/v7/appcompat/res/drawable-hdpi/abc_list_longpressed_holo.9.png
index 4ea7afa..62fbd2c 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_list_longpressed_holo.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_list_longpressed_holo.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_list_pressed_holo_dark.9.png b/v7/appcompat/res/drawable-hdpi/abc_list_pressed_holo_dark.9.png
index 596accb..2f6ef91 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_list_pressed_holo_dark.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_list_pressed_holo_dark.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_list_pressed_holo_light.9.png b/v7/appcompat/res/drawable-hdpi/abc_list_pressed_holo_light.9.png
index 2054530..863ce95 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_list_pressed_holo_light.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_list_pressed_holo_light.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_list_selector_disabled_holo_dark.9.png b/v7/appcompat/res/drawable-hdpi/abc_list_selector_disabled_holo_dark.9.png
index f6fd30d..b6d4677 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_list_selector_disabled_holo_dark.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_list_selector_disabled_holo_dark.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_list_selector_disabled_holo_light.9.png b/v7/appcompat/res/drawable-hdpi/abc_list_selector_disabled_holo_light.9.png
index ca8e9a2..e01c739 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_list_selector_disabled_holo_light.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_list_selector_disabled_holo_light.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_menu_hardkey_panel_mtrl_mult.9.png b/v7/appcompat/res/drawable-hdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
index 76a5c53..a6b3696 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_popup_background_mtrl_mult.9.png b/v7/appcompat/res/drawable-hdpi/abc_popup_background_mtrl_mult.9.png
index 385734e..9d8451a 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_popup_background_mtrl_mult.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_popup_background_mtrl_mult.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_spinner_mtrl_am_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_spinner_mtrl_am_alpha.9.png
index de7ac29..9de0263 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_spinner_mtrl_am_alpha.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_switch_track_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_switch_track_mtrl_alpha.9.png
index 0ebe65e..56436a1 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_switch_track_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_tab_indicator_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_tab_indicator_mtrl_alpha.9.png
index 21b2135..4b0b10a 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_tab_indicator_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_tab_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_text_cursor_mtrl_alpha.9.png
new file mode 100644
index 0000000..5e0bf84
--- /dev/null
+++ b/v7/appcompat/res/drawable-hdpi/abc_text_cursor_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_textfield_activated_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_textfield_activated_mtrl_alpha.9.png
index b9a81be..5b13bc1 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_textfield_activated_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_textfield_default_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_textfield_default_mtrl_alpha.9.png
index 3682629..0078bf6 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_textfield_default_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_textfield_search_activated_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_textfield_search_activated_mtrl_alpha.9.png
index ce577e5..a74ab26 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_textfield_search_activated_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_textfield_search_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_textfield_search_default_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_textfield_search_default_mtrl_alpha.9.png
index 7c305ab..6282df4 100644
--- a/v7/appcompat/res/drawable-hdpi/abc_textfield_search_default_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-hdpi/abc_textfield_search_default_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
index dcdd03b..2e1062f 100644
--- a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
index 5338f02..a262b0c 100644
--- a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_cut_mtrl_alpha.png
index fd27a0f..9ed43ca 100644
--- a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_spinner_mtrl_am_alpha.9.png b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_spinner_mtrl_am_alpha.9.png
index d6e0b99..4cd8a27 100644
--- a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_spinner_mtrl_am_alpha.9.png
+++ b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_ab_back_mtrl_am_alpha.png
index 482e142..e300b7c 100644
--- a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
index 5aaad7e..05b1e11 100644
--- a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_cut_mtrl_alpha.png
index c0246b3..aa7b323 100644
--- a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_spinner_mtrl_am_alpha.9.png b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_spinner_mtrl_am_alpha.9.png
index 74160c3..d02a5da 100644
--- a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_spinner_mtrl_am_alpha.9.png
+++ b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png
index 753496a..a188f2f 100644
--- a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
index 8a4e22e..e95ba94 100644
--- a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
index 6944267..87bf8d3 100644
--- a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_spinner_mtrl_am_alpha.9.png b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_spinner_mtrl_am_alpha.9.png
index 2d63334..b097e48 100644
--- a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_spinner_mtrl_am_alpha.9.png
+++ b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
index 2b308bf..de37158 100644
--- a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
index 9b5be20..ac86165 100644
--- a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
index 07d0a5d..8b2adf6 100644
--- a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_spinner_mtrl_am_alpha.9.png b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_spinner_mtrl_am_alpha.9.png
index bd1029d..0b89504 100644
--- a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_spinner_mtrl_am_alpha.9.png
+++ b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
index 33f6587..7dc6934 100644
--- a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
index a5015c6..884cd12 100644
--- a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
index 2f12fc0..90fe333 100644
--- a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_spinner_mtrl_am_alpha.9.png b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_spinner_mtrl_am_alpha.9.png
index b164173..930630d 100644
--- a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_spinner_mtrl_am_alpha.9.png
+++ b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ab_share_pack_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_ab_share_pack_mtrl_alpha.9.png
index f31730d..fa0ed8f 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ab_share_pack_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ab_share_pack_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_000.png b/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_000.png
index 70793c4..7a9fcbc 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_000.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_015.png b/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_015.png
index 8aa1be2..3b052e5 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_015.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_015.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_000.png b/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_000.png
index 54ef480..96a8693 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_000.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_015.png b/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_015.png
index 4f8a162..827d634 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_015.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_015.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_rating_star_off_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_btn_rating_star_off_mtrl_alpha.png
index d38aed2..0908475 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_rating_star_off_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_btn_rating_star_off_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_rating_star_on_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_btn_rating_star_on_mtrl_alpha.png
index 87dade3..a5a437f 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_rating_star_on_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_btn_rating_star_on_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/v7/appcompat/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00001.9.png
index 03d3dfb..b2191da 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00001.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/v7/appcompat/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00012.9.png
index 6635830..2a94e6e 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00012.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_cab_background_top_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_cab_background_top_mtrl_alpha.9.png
index ae8cccd..038e000 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_cab_background_top_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_ab_back_mtrl_am_alpha.png
index 6674351..8043d4c 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_clear_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_clear_mtrl_alpha.png
index bbc43b1..e80681a 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_clear_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_clear_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_commit_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_commit_search_api_mtrl_alpha.png
index 42ac8ca..9603e76 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_commit_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_commit_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_go_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_go_search_api_mtrl_alpha.png
index b5f6176..44c1423 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_go_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_go_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
index 6aa238c..80c0695 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_cut_mtrl_alpha.png
index aa4f1c2..3966d6a 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
index 1d8ad18..017e45e 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_paste_mtrl_am_alpha.png
index d40353c..ec0cff4 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_paste_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_selectall_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_selectall_mtrl_alpha.png
index 488d1ab..966938b 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_selectall_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_share_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_share_mtrl_alpha.png
index 2cabe7b..d05f969 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_share_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_share_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_search_api_mtrl_alpha.png
index 0fb57b2..451818c 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_voice_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_voice_search_api_mtrl_alpha.png
index fca776f..a216da1 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_voice_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_ic_voice_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_list_divider_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_list_divider_mtrl_alpha.9.png
index 070bdbf..1e571f5 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_list_divider_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_list_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_list_focused_holo.9.png b/v7/appcompat/res/drawable-mdpi/abc_list_focused_holo.9.png
index 00f05d8..addb54a 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_list_focused_holo.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_list_focused_holo.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_list_longpressed_holo.9.png b/v7/appcompat/res/drawable-mdpi/abc_list_longpressed_holo.9.png
index 3bf8e03..5fcd5b2 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_list_longpressed_holo.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_list_longpressed_holo.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_list_pressed_holo_dark.9.png b/v7/appcompat/res/drawable-mdpi/abc_list_pressed_holo_dark.9.png
index fd0e8d7..251b989 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_list_pressed_holo_dark.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_list_pressed_holo_dark.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_list_pressed_holo_light.9.png b/v7/appcompat/res/drawable-mdpi/abc_list_pressed_holo_light.9.png
index 061904c..01efec0 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_list_pressed_holo_light.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_list_pressed_holo_light.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_list_selector_disabled_holo_dark.9.png b/v7/appcompat/res/drawable-mdpi/abc_list_selector_disabled_holo_dark.9.png
index 92da2f0..f1d1b61 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_list_selector_disabled_holo_dark.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_list_selector_disabled_holo_dark.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_list_selector_disabled_holo_light.9.png b/v7/appcompat/res/drawable-mdpi/abc_list_selector_disabled_holo_light.9.png
index 42cb646..10851f6 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_list_selector_disabled_holo_light.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_list_selector_disabled_holo_light.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_menu_hardkey_panel_mtrl_mult.9.png b/v7/appcompat/res/drawable-mdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
index 02b25f0..91b0cb8 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_popup_background_mtrl_mult.9.png b/v7/appcompat/res/drawable-mdpi/abc_popup_background_mtrl_mult.9.png
index e920499..5f55cd5 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_popup_background_mtrl_mult.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_popup_background_mtrl_mult.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_spinner_mtrl_am_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_spinner_mtrl_am_alpha.9.png
index bbf5928..ed75cb8 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_spinner_mtrl_am_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_switch_track_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_switch_track_mtrl_alpha.9.png
index 4918d33..fcd81de 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_switch_track_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_tab_indicator_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_tab_indicator_mtrl_alpha.9.png
index b69529c..12b0a79 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_tab_indicator_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_tab_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_text_cursor_mtrl_alpha.9.png
new file mode 100644
index 0000000..36348a8
--- /dev/null
+++ b/v7/appcompat/res/drawable-mdpi/abc_text_cursor_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_textfield_activated_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_textfield_activated_mtrl_alpha.9.png
index f3d06fe..3ffa251 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_textfield_activated_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_textfield_default_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_textfield_default_mtrl_alpha.9.png
index f0e7db8..0eb61f1 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_textfield_default_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_textfield_search_activated_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_textfield_search_activated_mtrl_alpha.9.png
index d7faacf..0c766f3 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_textfield_search_activated_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_textfield_search_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_textfield_search_default_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_textfield_search_default_mtrl_alpha.9.png
index 0a36039..4f66d7a 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_textfield_search_default_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_textfield_search_default_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-tvdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/v7/appcompat/res/drawable-tvdpi/abc_btn_switch_to_on_mtrl_00001.9.png
index d675993..530ca45 100644
--- a/v7/appcompat/res/drawable-tvdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ b/v7/appcompat/res/drawable-tvdpi/abc_btn_switch_to_on_mtrl_00001.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-tvdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/v7/appcompat/res/drawable-tvdpi/abc_btn_switch_to_on_mtrl_00012.9.png
index 8da42fe..b527495 100644
--- a/v7/appcompat/res/drawable-tvdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ b/v7/appcompat/res/drawable-tvdpi/abc_btn_switch_to_on_mtrl_00012.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ab_share_pack_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_ab_share_pack_mtrl_alpha.9.png
index 8337ffe..6284eaa 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ab_share_pack_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ab_share_pack_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_000.png
index 9244174..4902520 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_000.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_015.png
index 5f40d73..59a683a 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_015.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_015.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_000.png
index d068dbe..03bf49c 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_000.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_015.png
index 9924496..342323b 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_015.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_015.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_rating_star_off_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_rating_star_off_mtrl_alpha.png
index 33ec44c..c0333f9 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_rating_star_off_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_btn_rating_star_off_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_rating_star_on_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_rating_star_on_mtrl_alpha.png
index 0166d70..2f29c39 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_rating_star_on_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_btn_rating_star_on_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
index 8a648b8..0d22743 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
index 435ce21..17fc083 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_cab_background_top_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_cab_background_top_mtrl_alpha.9.png
index ed8d341..600178a 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_cab_background_top_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png
index 27bdcb7..c465e82 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_clear_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_clear_mtrl_alpha.png
index 84968ee..76e07f0 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_clear_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_clear_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_commit_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_commit_search_api_mtrl_alpha.png
index c10a1b7..1015e1f 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_commit_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_commit_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_go_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_go_search_api_mtrl_alpha.png
index bd80981..b3fa6bc 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_go_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_go_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
index a9e6cc5..c8a6d25 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
index ce5d4a7..3c5e683 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
index bb9d84d..f87733a 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
index 9f9cb3b..9aabc43 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_selectall_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_selectall_mtrl_alpha.png
index 53d0814..c039c8e 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_selectall_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_share_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_share_mtrl_alpha.png
index 0750fcc..b57ee19 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_share_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_share_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_search_api_mtrl_alpha.png
index 05cfab7..76f2696 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_voice_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_voice_search_api_mtrl_alpha.png
index b7d8dc7..d0385ba 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_voice_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_ic_voice_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_list_divider_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_list_divider_mtrl_alpha.9.png
index 0d2836d..1e571f5 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_list_divider_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_list_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_list_focused_holo.9.png b/v7/appcompat/res/drawable-xhdpi/abc_list_focused_holo.9.png
index b545f8e..67c25ae 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_list_focused_holo.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_list_focused_holo.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_list_longpressed_holo.9.png b/v7/appcompat/res/drawable-xhdpi/abc_list_longpressed_holo.9.png
index eda10e6..17c34a1 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_list_longpressed_holo.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_list_longpressed_holo.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_list_pressed_holo_dark.9.png b/v7/appcompat/res/drawable-xhdpi/abc_list_pressed_holo_dark.9.png
index 29037a0..988548a 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_list_pressed_holo_dark.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_list_pressed_holo_dark.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_list_pressed_holo_light.9.png b/v7/appcompat/res/drawable-xhdpi/abc_list_pressed_holo_light.9.png
index f4af926..15fcf6a 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_list_pressed_holo_light.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_list_pressed_holo_light.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_list_selector_disabled_holo_dark.9.png b/v7/appcompat/res/drawable-xhdpi/abc_list_selector_disabled_holo_dark.9.png
index 88726b6..65275b3 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_list_selector_disabled_holo_dark.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_list_selector_disabled_holo_dark.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_list_selector_disabled_holo_light.9.png b/v7/appcompat/res/drawable-xhdpi/abc_list_selector_disabled_holo_light.9.png
index c6a7d4d..5b58e76 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_list_selector_disabled_holo_light.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_list_selector_disabled_holo_light.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_menu_hardkey_panel_mtrl_mult.9.png b/v7/appcompat/res/drawable-xhdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
index 4fda867..c67a5dd 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_popup_background_mtrl_mult.9.png b/v7/appcompat/res/drawable-xhdpi/abc_popup_background_mtrl_mult.9.png
index a081ceb..b5dd854 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_popup_background_mtrl_mult.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_popup_background_mtrl_mult.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_spinner_mtrl_am_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_spinner_mtrl_am_alpha.9.png
index d4bd169..bcf6b7f 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_spinner_mtrl_am_alpha.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_switch_track_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_switch_track_mtrl_alpha.9.png
index fd47f15..cd1396b 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_switch_track_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_tab_indicator_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_tab_indicator_mtrl_alpha.9.png
index 5610d8c..2242d2f 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_tab_indicator_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_tab_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_text_cursor_mtrl_alpha.9.png
new file mode 100644
index 0000000..666b10a
--- /dev/null
+++ b/v7/appcompat/res/drawable-xhdpi/abc_text_cursor_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_textfield_activated_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_textfield_activated_mtrl_alpha.9.png
index 7174b67..8ff3a83 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_textfield_activated_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_textfield_default_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_textfield_default_mtrl_alpha.9.png
index 46dad22..e7e693a 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_textfield_default_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_textfield_search_activated_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_textfield_search_activated_mtrl_alpha.9.png
index 33c1035..819171a 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_textfield_search_activated_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_textfield_search_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_textfield_search_default_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_textfield_search_default_mtrl_alpha.9.png
index 0226f84..4def8c8 100644
--- a/v7/appcompat/res/drawable-xhdpi/abc_textfield_search_default_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xhdpi/abc_textfield_search_default_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ab_share_pack_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_ab_share_pack_mtrl_alpha.9.png
index 469f736..4eae28f 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ab_share_pack_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ab_share_pack_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_000.png
index 0d544d9..accf80e 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_000.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_015.png
index 810a029..8c82ec3 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_015.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_015.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_000.png
index c9af24b..8fc0a9b 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_000.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_015.png
index db1d93a..92b712e 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_015.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_015.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_rating_star_off_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_rating_star_off_mtrl_alpha.png
index 4b49faf..78bbeba 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_rating_star_off_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_btn_rating_star_off_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_rating_star_on_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_rating_star_on_mtrl_alpha.png
index 561d9ef..c4ba8e6 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_rating_star_on_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_btn_rating_star_on_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
index b149e47..133de52 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
index 00fb83e..ff3b596 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_cab_background_top_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_cab_background_top_mtrl_alpha.9.png
index 1dd64b9..f6d2f32 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_cab_background_top_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
index c2d6a54..39178bf 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_clear_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_clear_mtrl_alpha.png
index 24a194f..f54f4f9 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_clear_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_clear_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_commit_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_commit_search_api_mtrl_alpha.png
index fc1b8b4..65cf0c1 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_commit_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_commit_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_go_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_go_search_api_mtrl_alpha.png
index 8e1ab5b..d041623 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_go_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_go_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
index 5fc17a4..9dff893 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
index 11a9f97..a1f8c33 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
index cada2fb..28a3bbf 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
index 556c30d..29a4e52 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_selectall_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
index f0a0b73..162ab98 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_share_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_share_mtrl_alpha.png
index 7e181d1..a1866ba 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_share_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_share_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_search_api_mtrl_alpha.png
index 6f60bd3..d967ae7 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_voice_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_voice_search_api_mtrl_alpha.png
index 658c5a5..5baef9f 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_voice_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_ic_voice_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_list_divider_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_list_divider_mtrl_alpha.9.png
index b8ac46d..987b2bc 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_list_divider_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_list_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_list_focused_holo.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_list_focused_holo.9.png
index 76cad17..8b050e8 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_list_focused_holo.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_list_focused_holo.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_list_longpressed_holo.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_list_longpressed_holo.9.png
index 8f436ea..00e370a 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_list_longpressed_holo.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_list_longpressed_holo.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_list_pressed_holo_dark.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_list_pressed_holo_dark.9.png
index d4952ea..719c7b5 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_list_pressed_holo_dark.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_list_pressed_holo_dark.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_list_pressed_holo_light.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_list_pressed_holo_light.9.png
index 1352a17..75bd580 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_list_pressed_holo_light.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_list_pressed_holo_light.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_list_selector_disabled_holo_dark.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_list_selector_disabled_holo_dark.9.png
index 175b82c..9cc3666 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_list_selector_disabled_holo_dark.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_list_selector_disabled_holo_dark.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_list_selector_disabled_holo_light.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_list_selector_disabled_holo_light.9.png
index aad8a46..224a081 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_list_selector_disabled_holo_light.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_list_selector_disabled_holo_light.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_menu_hardkey_panel_mtrl_mult.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
index f5c18d0..2ab970d 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_menu_hardkey_panel_mtrl_mult.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_popup_background_mtrl_mult.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_popup_background_mtrl_mult.9.png
index fb7d715..ee4bfe7 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_popup_background_mtrl_mult.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_popup_background_mtrl_mult.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_spinner_mtrl_am_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_spinner_mtrl_am_alpha.9.png
index 2e7bc12..6940b60 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_spinner_mtrl_am_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_switch_track_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_switch_track_mtrl_alpha.9.png
index 3e3174d..96bec46 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_switch_track_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_tab_indicator_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_tab_indicator_mtrl_alpha.9.png
index 248f4f8..eeb74c8 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_tab_indicator_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_tab_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_text_cursor_mtrl_alpha.9.png
new file mode 100644
index 0000000..08ee2b4
--- /dev/null
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_text_cursor_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_textfield_activated_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_textfield_activated_mtrl_alpha.9.png
index 661d5f0..4d3d3a4 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_textfield_activated_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_textfield_default_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_textfield_default_mtrl_alpha.9.png
index d7696c3..c5acb84 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_textfield_default_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_textfield_search_activated_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_textfield_search_activated_mtrl_alpha.9.png
index b6efff3..30328ae 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_textfield_search_activated_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_textfield_search_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_textfield_search_default_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_textfield_search_default_mtrl_alpha.9.png
index 2b253fb..d4f3650 100644
--- a/v7/appcompat/res/drawable-xxhdpi/abc_textfield_search_default_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_textfield_search_default_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_000.png
index 5dd0e5b..4dc870e 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_000.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_015.png
index f0ff1a7..4e18de2 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_015.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_015.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_000.png
index adef871..5fa3266 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_000.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_015.png
index 44028af..c11cb2e 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_015.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_015.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
index d3f2a9a..e075ab8 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
index a3caefb..25274ee 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
index 70c2040..16b0f1d 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_clear_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_clear_mtrl_alpha.png
index 7252208..7b2a480 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_clear_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_clear_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
index 2a6f6ba..fe93d87 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
index 13cc0fd..4b2d05a 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
index e232cf7..16e9e14 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
index 8e9041f..129d30f 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_selectall_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
index 66fc42f..fa6ab02 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_share_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_share_mtrl_alpha.png
index 279afe3..77318c7 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_share_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_share_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_search_api_mtrl_alpha.png
index c873e9b..098c25a 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_voice_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_voice_search_api_mtrl_alpha.png
index fe00ae5..76c4eeb 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_voice_search_api_mtrl_alpha.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_voice_search_api_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_spinner_mtrl_am_alpha.9.png b/v7/appcompat/res/drawable-xxxhdpi/abc_spinner_mtrl_am_alpha.9.png
index 1086e9d..6b8bc0a 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_spinner_mtrl_am_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_switch_track_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxxhdpi/abc_switch_track_mtrl_alpha.9.png
index 1e4a74c..c2393ab 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_switch_track_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_tab_indicator_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxxhdpi/abc_tab_indicator_mtrl_alpha.9.png
index 5813179..929be19 100644
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_tab_indicator_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_tab_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable/abc_btn_borderless_material.xml b/v7/appcompat/res/drawable/abc_btn_borderless_material.xml
new file mode 100644
index 0000000..f389460
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_btn_borderless_material.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:drawable="@drawable/abc_btn_default_mtrl_shape"/>
+    <item android:state_pressed="true" android:drawable="@drawable/abc_btn_default_mtrl_shape"/>
+    <item android:drawable="@android:color/transparent"/>
+</selector>
+
diff --git a/v7/appcompat/res/drawable/abc_cab_background_top_material.xml b/v7/appcompat/res/drawable/abc_cab_background_top_material.xml
index 68b7634..f20add7 100644
--- a/v7/appcompat/res/drawable/abc_cab_background_top_material.xml
+++ b/v7/appcompat/res/drawable/abc_cab_background_top_material.xml
@@ -14,8 +14,7 @@
      limitations under the License.
 -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
-            android:paddingMode="stack">
-    <item android:drawable="@drawable/abc_cab_background_internal_bg" />
-    <item android:drawable="@drawable/abc_cab_background_top_mtrl_alpha" />
-</layer-list>
\ No newline at end of file
+<!-- This is a dummy drawable so that we can refer to the drawable ID -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@android:color/white"/>
+</shape>
diff --git a/v7/appcompat/res/drawable/abc_dialog_material_background_dark.xml b/v7/appcompat/res/drawable/abc_dialog_material_background_dark.xml
new file mode 100644
index 0000000..41c4a6f
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_dialog_material_background_dark.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+       android:insetLeft="16dp"
+       android:insetTop="16dp"
+       android:insetRight="16dp"
+       android:insetBottom="16dp">
+    <shape android:shape="rectangle">
+        <corners android:radius="2dp" />
+        <solid android:color="@color/background_floating_material_dark" />
+    </shape>
+</inset>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_dialog_material_background_light.xml b/v7/appcompat/res/drawable/abc_dialog_material_background_light.xml
new file mode 100644
index 0000000..248b13a
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_dialog_material_background_light.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+       android:insetLeft="16dp"
+       android:insetTop="16dp"
+       android:insetRight="16dp"
+       android:insetBottom="16dp">
+    <shape android:shape="rectangle">
+        <corners android:radius="2dp" />
+        <solid android:color="@color/background_floating_material_light" />
+    </shape>
+</inset>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_edit_text_material.xml b/v7/appcompat/res/drawable/abc_edit_text_material.xml
index 754ab18..46c4e91 100644
--- a/v7/appcompat/res/drawable/abc_edit_text_material.xml
+++ b/v7/appcompat/res/drawable/abc_edit_text_material.xml
@@ -15,16 +15,15 @@
 -->
 
 <inset xmlns:android="http://schemas.android.com/apk/res/android"
-       android:insetLeft="@dimen/abc_control_inset_material"
-       android:insetTop="@dimen/abc_control_inset_material"
-       android:insetBottom="@dimen/abc_control_inset_material"
-       android:insetRight="@dimen/abc_control_inset_material">
+       android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
+       android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
+       android:insetTop="@dimen/abc_edit_text_inset_top_material"
+       android:insetBottom="@dimen/abc_edit_text_inset_bottom_material">
 
     <selector>
-        <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
-        <item android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
-        <item android:state_enabled="true" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
-        <item android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
+        <item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
+        <item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
+        <item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
     </selector>
 
 </inset>
diff --git a/v7/appcompat/res/layout/abc_alert_dialog_material.xml b/v7/appcompat/res/layout/abc_alert_dialog_material.xml
new file mode 100644
index 0000000..86d59f7
--- /dev/null
+++ b/v7/appcompat/res/layout/abc_alert_dialog_material.xml
@@ -0,0 +1,148 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/parentPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+    <LinearLayout
+            android:id="@+id/topPanel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+        <LinearLayout
+                android:id="@+id/title_template"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:gravity="center_vertical|start"
+                android:paddingLeft="?attr/dialogPreferredPadding"
+                android:paddingRight="?attr/dialogPreferredPadding"
+                android:paddingTop="@dimen/abc_dialog_padding_top_material">
+
+            <ImageView
+                    android:id="@android:id/icon"
+                    android:layout_width="32dip"
+                    android:layout_height="32dip"
+                    android:layout_marginEnd="8dip"
+                    android:scaleType="fitCenter"
+                    android:src="@null"/>
+
+            <android.support.v7.internal.widget.DialogTitle
+                    android:id="@+id/alertTitle"
+                    style="?attr/android:windowTitleStyle"
+                    android:singleLine="true"
+                    android:ellipsize="end"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:textAlignment="viewStart"/>
+
+        </LinearLayout>
+        <!-- If the client uses a customTitle, it will be added here. -->
+    </LinearLayout>
+
+    <FrameLayout
+            android:id="@+id/contentPanel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:minHeight="48dp">
+
+        <ScrollView
+                android:id="@+id/scrollView"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:clipToPadding="false">
+
+            <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="vertical">
+
+                <TextView
+                        android:id="@android:id/message"
+                        style="@style/TextAppearance.AppCompat.Subhead"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:paddingLeft="?attr/dialogPreferredPadding"
+                        android:paddingTop="@dimen/abc_dialog_padding_top_material"
+                        android:paddingRight="?attr/dialogPreferredPadding"/>
+
+                <View
+                        android:id="@+id/textSpacerNoButtons"
+                        android:visibility="gone"
+                        android:layout_width="0dp"
+                        android:layout_height="@dimen/abc_dialog_padding_top_material"/>
+            </LinearLayout>
+        </ScrollView>
+
+    </FrameLayout>
+
+    <FrameLayout
+            android:id="@+id/customPanel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:minHeight="48dp">
+
+        <FrameLayout
+                android:id="@+id/custom"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+    </FrameLayout>
+
+    <LinearLayout
+            android:id="@+id/buttonPanel"
+            style="?attr/buttonBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layoutDirection="locale"
+            android:orientation="horizontal"
+            android:paddingLeft="12dp"
+            android:paddingRight="12dp"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:gravity="bottom">
+
+        <Button
+                android:id="@android:id/button3"
+                style="?attr/buttonBarNeutralButtonStyle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+
+        <android.support.v4.widget.Space
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:visibility="invisible"/>
+
+        <Button
+                android:id="@android:id/button2"
+                style="?attr/buttonBarNegativeButtonStyle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+
+        <Button
+                android:id="@android:id/button1"
+                style="?attr/buttonBarPositiveButtonStyle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/abc_dialog_title_material.xml b/v7/appcompat/res/layout/abc_dialog_title_material.xml
new file mode 100644
index 0000000..068b9e9
--- /dev/null
+++ b/v7/appcompat/res/layout/abc_dialog_title_material.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<!--
+This is an optimized layout for a screen, with the minimum set of features
+enabled.
+-->
+
+<android.support.v7.internal.widget.FitWindowsLinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:orientation="vertical"
+        android:fitsSystemWindows="true">
+
+    <TextView
+            android:id="@+id/title"
+            style="?android:attr/windowTitleStyle"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAlignment="viewStart"
+            android:paddingLeft="?attr/dialogPreferredPadding"
+            android:paddingRight="?attr/dialogPreferredPadding"
+            android:paddingTop="@dimen/abc_dialog_padding_top_material"/>
+
+    <include
+            layout="@layout/abc_screen_content_include"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"/>
+
+</android.support.v7.internal.widget.FitWindowsLinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/abc_select_dialog_material.xml b/v7/appcompat/res/layout/abc_select_dialog_material.xml
new file mode 100644
index 0000000..12bcbf1
--- /dev/null
+++ b/v7/appcompat/res/layout/abc_select_dialog_material.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<!--
+    This layout file is used by the AlertDialog when displaying a list of items.
+    This layout file is inflated and used as the ListView to display the items.
+    Assign an ID so its state will be saved/restored.
+-->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+      android:id="@+id/select_dialog_listview"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:cacheColorHint="@null"
+      android:divider="?attr/listDividerAlertDialog"
+      android:scrollbars="vertical"
+      android:overScrollMode="ifContentScrolls"
+      android:fadingEdge="none"
+      android:paddingTop="@dimen/abc_dialog_list_padding_vertical_material"
+      android:paddingBottom="@dimen/abc_dialog_list_padding_vertical_material"
+      android:clipToPadding="false"
+      style="@style/Widget.AppCompat.ListView" />
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/select_dialog_item_material.xml b/v7/appcompat/res/layout/select_dialog_item_material.xml
new file mode 100644
index 0000000..677b178
--- /dev/null
+++ b/v7/appcompat/res/layout/select_dialog_item_material.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<!--
+    This layout file is used by the AlertDialog when displaying a list of items.
+    This layout file is inflated and used as the TextView to display individual
+    items.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?attr/listPreferredItemHeightSmall"
+    android:textAppearance="?attr/textAppearanceListItemSmall"
+    android:textColor="?attr/textColorAlertDialogListItem"
+    android:gravity="center_vertical"
+    android:paddingLeft="?attr/listPreferredItemPaddingLeft"
+    android:paddingRight="?attr/listPreferredItemPaddingRight"
+    android:ellipsize="marquee" />
diff --git a/v7/appcompat/res/layout/select_dialog_multichoice_material.xml b/v7/appcompat/res/layout/select_dialog_multichoice_material.xml
new file mode 100644
index 0000000..8f75bd3
--- /dev/null
+++ b/v7/appcompat/res/layout/select_dialog_multichoice_material.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?attr/listPreferredItemHeightSmall"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:textColor="?attr/textColorAlertDialogListItem"
+    android:gravity="center_vertical"
+    android:paddingLeft="?attr/dialogPreferredPadding"
+    android:paddingRight="?attr/dialogPreferredPadding"
+    android:checkMark="?android:attr/listChoiceIndicatorMultiple"
+    android:ellipsize="marquee" />
diff --git a/v7/appcompat/res/layout/select_dialog_singlechoice_material.xml b/v7/appcompat/res/layout/select_dialog_singlechoice_material.xml
new file mode 100644
index 0000000..3c2264c
--- /dev/null
+++ b/v7/appcompat/res/layout/select_dialog_singlechoice_material.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?attr/listPreferredItemHeightSmall"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:textColor="?attr/textColorAlertDialogListItem"
+    android:gravity="center_vertical"
+    android:paddingLeft="?attr/dialogPreferredPadding"
+    android:paddingRight="?attr/dialogPreferredPadding"
+    android:checkMark="?android:attr/listChoiceIndicatorSingle"
+    android:ellipsize="marquee" />
diff --git a/v7/appcompat/res/values-af/strings.xml b/v7/appcompat/res/values-af/strings.xml
index 563258e..549ab76 100644
--- a/v7/appcompat/res/values-af/strings.xml
+++ b/v7/appcompat/res/values-af/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Soek"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Soek …"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Soeknavraag"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Vee navraag uit"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Dien navraag in"</string>
diff --git a/v7/appcompat/res/values-am/strings.xml b/v7/appcompat/res/values-am/strings.xml
index 8621f43..9bcea17 100644
--- a/v7/appcompat/res/values-am/strings.xml
+++ b/v7/appcompat/res/values-am/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s፣ %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s፣ %2$s፣ %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"ፍለጋ"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"ፈልግ…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"የፍለጋ ጥያቄ"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"መጠይቅ አጽዳ"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"መጠይቅ ያስረክቡ"</string>
diff --git a/v7/appcompat/res/values-ar/strings.xml b/v7/appcompat/res/values-ar/strings.xml
index 1f9167c..4ed5f59 100644
--- a/v7/appcompat/res/values-ar/strings.xml
+++ b/v7/appcompat/res/values-ar/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s، %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s، %2$s، %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"بحث"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"بحث…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"طلب البحث"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"محو طلب البحث"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"إرسال طلب البحث"</string>
diff --git a/v7/appcompat/res/values-bg/strings.xml b/v7/appcompat/res/values-bg/strings.xml
index e35b172..74963a2 100644
--- a/v7/appcompat/res/values-bg/strings.xml
+++ b/v7/appcompat/res/values-bg/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"„%1$s“ – %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"„%1$s“, „%2$s“ – %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Търсене"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Търсете…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Заявка за търсене"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Изчистване на заявката"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Изпращане на заявката"</string>
diff --git a/v7/appcompat/res/values-bn-rBD/strings.xml b/v7/appcompat/res/values-bn-rBD/strings.xml
index 79b78ca..93a5997 100644
--- a/v7/appcompat/res/values-bn-rBD/strings.xml
+++ b/v7/appcompat/res/values-bn-rBD/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"অনুসন্ধান করুন"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"অনুসন্ধান..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"ক্যোয়ারী অনুসন্ধান করুন"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ক্যোয়ারী সাফ করুন"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ক্যোয়ারী জমা দিন"</string>
diff --git a/v7/appcompat/res/values-ca/strings.xml b/v7/appcompat/res/values-ca/strings.xml
index d989428..97789f5 100644
--- a/v7/appcompat/res/values-ca/strings.xml
+++ b/v7/appcompat/res/values-ca/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Cerca"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Cerca..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de cerca"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Esborra la consulta"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Envia la consulta"</string>
diff --git a/v7/appcompat/res/values-cs/strings.xml b/v7/appcompat/res/values-cs/strings.xml
index aff38a1..9c3b2b0 100644
--- a/v7/appcompat/res/values-cs/strings.xml
+++ b/v7/appcompat/res/values-cs/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s – %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s – %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Hledat"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Vyhledat…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Vyhledávací dotaz"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Smazat dotaz"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Odeslat dotaz"</string>
diff --git a/v7/appcompat/res/values-da/strings.xml b/v7/appcompat/res/values-da/strings.xml
index c06c2e9..fda0c24 100644
--- a/v7/appcompat/res/values-da/strings.xml
+++ b/v7/appcompat/res/values-da/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Søg"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Søg…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Søgeforespørgsel"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Ryd forespørgslen"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Indsend forespørgslen"</string>
diff --git a/v7/appcompat/res/values-de/strings.xml b/v7/appcompat/res/values-de/strings.xml
index 3ac2579..2905d60 100644
--- a/v7/appcompat/res/values-de/strings.xml
+++ b/v7/appcompat/res/values-de/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s: %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s: %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Suchen"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Suchen…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Suchanfrage"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Suchanfrage löschen"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Suchanfrage senden"</string>
diff --git a/v7/appcompat/res/values-el/strings.xml b/v7/appcompat/res/values-el/strings.xml
index f0acb4d..779c83f 100644
--- a/v7/appcompat/res/values-el/strings.xml
+++ b/v7/appcompat/res/values-el/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Αναζήτηση"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Αναζήτηση…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Ερώτημα αναζήτησης"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Διαγραφή ερωτήματος"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Υποβολή ερωτήματος"</string>
diff --git a/v7/appcompat/res/values-en-rGB/strings.xml b/v7/appcompat/res/values-en-rGB/strings.xml
index f0f3beb..a85156e 100644
--- a/v7/appcompat/res/values-en-rGB/strings.xml
+++ b/v7/appcompat/res/values-en-rGB/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Search"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Search…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Search query"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Clear query"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Submit query"</string>
diff --git a/v7/appcompat/res/values-en-rIN/strings.xml b/v7/appcompat/res/values-en-rIN/strings.xml
index f0f3beb..a85156e 100644
--- a/v7/appcompat/res/values-en-rIN/strings.xml
+++ b/v7/appcompat/res/values-en-rIN/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Search"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Search…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Search query"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Clear query"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Submit query"</string>
diff --git a/v7/appcompat/res/values-es-rUS/strings.xml b/v7/appcompat/res/values-es-rUS/strings.xml
index 3be6353..b8488e1 100644
--- a/v7/appcompat/res/values-es-rUS/strings.xml
+++ b/v7/appcompat/res/values-es-rUS/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Búsqueda"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Buscar…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de búsqueda"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Eliminar la consulta"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
diff --git a/v7/appcompat/res/values-es/strings.xml b/v7/appcompat/res/values-es/strings.xml
index fc51934..70ea32d 100644
--- a/v7/appcompat/res/values-es/strings.xml
+++ b/v7/appcompat/res/values-es/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Buscar"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Buscar…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Borrar consulta"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
diff --git a/v7/appcompat/res/values-et-rEE/strings.xml b/v7/appcompat/res/values-et-rEE/strings.xml
index 79b7036..cf4deac 100644
--- a/v7/appcompat/res/values-et-rEE/strings.xml
+++ b/v7/appcompat/res/values-et-rEE/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Otsing"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Otsige …"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Otsingupäring"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Päringu tühistamine"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Päringu esitamine"</string>
diff --git a/v7/appcompat/res/values-eu-rES/strings.xml b/v7/appcompat/res/values-eu-rES/strings.xml
index 38b0018..dddc924 100644
--- a/v7/appcompat/res/values-eu-rES/strings.xml
+++ b/v7/appcompat/res/values-eu-rES/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Bilatu"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Bilatu…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Bilaketa-kontsulta"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Garbitu kontsulta"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Bidali kontsulta"</string>
diff --git a/v7/appcompat/res/values-fa/strings.xml b/v7/appcompat/res/values-fa/strings.xml
index a813372..3e85c47 100644
--- a/v7/appcompat/res/values-fa/strings.xml
+++ b/v7/appcompat/res/values-fa/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"‏%1$s‏، %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"‏%1$s‏، %2$s‏، %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"جستجو"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"جستجو…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"عبارت جستجو"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"پاک کردن عبارت جستجو"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ارسال عبارت جستجو"</string>
diff --git a/v7/appcompat/res/values-fi/strings.xml b/v7/appcompat/res/values-fi/strings.xml
index dc56b79..f706ebe 100644
--- a/v7/appcompat/res/values-fi/strings.xml
+++ b/v7/appcompat/res/values-fi/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Haku"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Haku…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Hakulauseke"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Tyhjennä kysely"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Lähetä kysely"</string>
diff --git a/v7/appcompat/res/values-fr-rCA/strings.xml b/v7/appcompat/res/values-fr-rCA/strings.xml
index d401a01..979bfa5 100644
--- a/v7/appcompat/res/values-fr-rCA/strings.xml
+++ b/v7/appcompat/res/values-fr-rCA/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Rechercher"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Recherche en cours..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Requête de recherche"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Effacer la requête"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Envoyer la requête"</string>
diff --git a/v7/appcompat/res/values-fr/strings.xml b/v7/appcompat/res/values-fr/strings.xml
index 5cb6f18..df851d3 100644
--- a/v7/appcompat/res/values-fr/strings.xml
+++ b/v7/appcompat/res/values-fr/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Rechercher"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Rechercher…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Requête de recherche"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Effacer la requête"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Envoyer la requête"</string>
diff --git a/v7/appcompat/res/values-gl-rES/strings.xml b/v7/appcompat/res/values-gl-rES/strings.xml
index 76ce675..618aec0 100644
--- a/v7/appcompat/res/values-gl-rES/strings.xml
+++ b/v7/appcompat/res/values-gl-rES/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Buscar"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Buscar…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de busca"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Borrar consulta"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
diff --git a/v7/appcompat/res/values-h720dp/dimens.xml b/v7/appcompat/res/values-h720dp/dimens.xml
new file mode 100644
index 0000000..09c43f0
--- /dev/null
+++ b/v7/appcompat/res/values-h720dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Dialog button bar height -->
+    <dimen name="abc_alert_dialog_button_bar_height">54dip</dimen>
+</resources>
diff --git a/v7/appcompat/res/values-hi/strings.xml b/v7/appcompat/res/values-hi/strings.xml
index 7287d1a..2ee69d9 100644
--- a/v7/appcompat/res/values-hi/strings.xml
+++ b/v7/appcompat/res/values-hi/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"खोजें"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"खोजा जा रहा है…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"खोज क्वेरी"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"क्‍वेरी साफ़ करें"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"क्वेरी सबमिट करें"</string>
diff --git a/v7/appcompat/res/values-hr/strings.xml b/v7/appcompat/res/values-hr/strings.xml
index 7ee2a03..7e8968f 100644
--- a/v7/appcompat/res/values-hr/strings.xml
+++ b/v7/appcompat/res/values-hr/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Pretraživanje"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Pretražite…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Upit za pretraživanje"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Izbriši upit"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Pošalji upit"</string>
diff --git a/v7/appcompat/res/values-hu/strings.xml b/v7/appcompat/res/values-hu/strings.xml
index 4f9294a..7fe27d2 100644
--- a/v7/appcompat/res/values-hu/strings.xml
+++ b/v7/appcompat/res/values-hu/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Keresés"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Keresés…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Keresési lekérdezés"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Lekérdezés törlése"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Lekérdezés küldése"</string>
diff --git a/v7/appcompat/res/values-hy-rAM/strings.xml b/v7/appcompat/res/values-hy-rAM/strings.xml
index b75fe59..47c29a5 100644
--- a/v7/appcompat/res/values-hy-rAM/strings.xml
+++ b/v7/appcompat/res/values-hy-rAM/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Որոնել"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Որոնում..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Որոնման հարցում"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Մաքրել հարցումը"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Ուղարկել հարցումը"</string>
diff --git a/v7/appcompat/res/values-in/strings.xml b/v7/appcompat/res/values-in/strings.xml
index b25f01f..d102ba6 100644
--- a/v7/appcompat/res/values-in/strings.xml
+++ b/v7/appcompat/res/values-in/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Telusuri"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Telusuri..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Kueri penelusuran"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Hapus kueri"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Kirim kueri"</string>
diff --git a/v7/appcompat/res/values-is-rIS/strings.xml b/v7/appcompat/res/values-is-rIS/strings.xml
index 83baf81..a205e8b 100644
--- a/v7/appcompat/res/values-is-rIS/strings.xml
+++ b/v7/appcompat/res/values-is-rIS/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Leita"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Leita…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Leitarfyrirspurn"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Hreinsa fyrirspurn"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Senda fyrirspurn"</string>
diff --git a/v7/appcompat/res/values-it/strings.xml b/v7/appcompat/res/values-it/strings.xml
index 21802b3..71cb54f 100644
--- a/v7/appcompat/res/values-it/strings.xml
+++ b/v7/appcompat/res/values-it/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Cerca"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Cerca…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Query di ricerca"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Cancella query"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Invia query"</string>
diff --git a/v7/appcompat/res/values-iw/strings.xml b/v7/appcompat/res/values-iw/strings.xml
index 08c814e..c5ef730 100644
--- a/v7/appcompat/res/values-iw/strings.xml
+++ b/v7/appcompat/res/values-iw/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"‏%1$s‏, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"‏%1$s‏, %2$s‏, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"חפש"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"חפש…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"שאילתת חיפוש"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"מחק שאילתה"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"שלח שאילתה"</string>
diff --git a/v7/appcompat/res/values-ja/strings.xml b/v7/appcompat/res/values-ja/strings.xml
index d74c342..736d454 100644
--- a/v7/appcompat/res/values-ja/strings.xml
+++ b/v7/appcompat/res/values-ja/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s、%2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s、%2$s、%3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"検索"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"検索…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"検索キーワード"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"検索キーワードを削除"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"検索キーワードを送信"</string>
diff --git a/v7/appcompat/res/values-ka-rGE/strings.xml b/v7/appcompat/res/values-ka-rGE/strings.xml
index 7aeeaea..2341e3d 100644
--- a/v7/appcompat/res/values-ka-rGE/strings.xml
+++ b/v7/appcompat/res/values-ka-rGE/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"ძიება"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"ძიება..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"ძიების მოთხოვნა"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"მოთხოვნის გასუფთავება"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"მოთხოვნის გადაგზავნა"</string>
diff --git a/v7/appcompat/res/values-kk-rKZ/strings.xml b/v7/appcompat/res/values-kk-rKZ/strings.xml
index 3bc7ec0..1af9a5f 100644
--- a/v7/appcompat/res/values-kk-rKZ/strings.xml
+++ b/v7/appcompat/res/values-kk-rKZ/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Іздеу"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Іздеу…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Сұрақты іздеу"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Сұрақты жою"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Сұрақты жіберу"</string>
diff --git a/v7/appcompat/res/values-km-rKH/strings.xml b/v7/appcompat/res/values-km-rKH/strings.xml
index 32b8f68..5fda61a 100644
--- a/v7/appcompat/res/values-km-rKH/strings.xml
+++ b/v7/appcompat/res/values-km-rKH/strings.xml
@@ -24,11 +24,12 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"ស្វែងរក"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"ស្វែងរក…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"ស្វែងរក​សំណួរ"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"សម្អាត​សំណួរ"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ដាក់​​​ស្នើ​សំណួរ"</string>
     <string name="abc_searchview_description_voice" msgid="893419373245838918">"ការស្វែងរក​សំឡេង"</string>
-    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"ជ្រើស​កម្មវិធី"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"ជ្រើស​កម្មវិធី​​"</string>
     <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"មើល​ទាំងអស់"</string>
     <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"ចែករំលែក​ជាមួយ %s"</string>
     <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"ចែករំលែក​ជាមួយ"</string>
diff --git a/v7/appcompat/res/values-kn-rIN/strings.xml b/v7/appcompat/res/values-kn-rIN/strings.xml
index 5a93d5a..669c21c 100644
--- a/v7/appcompat/res/values-kn-rIN/strings.xml
+++ b/v7/appcompat/res/values-kn-rIN/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"ಹುಡುಕು"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"ಹುಡುಕಿ…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"ಪ್ರಶ್ನೆಯನ್ನು ಹುಡುಕಿ"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ಪ್ರಶ್ನೆಯನ್ನು ತೆರವುಗೊಳಿಸು"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ಪ್ರಶ್ನೆಯನ್ನು ಸಲ್ಲಿಸು"</string>
diff --git a/v7/appcompat/res/values-ko/strings.xml b/v7/appcompat/res/values-ko/strings.xml
index 9955a58..ad83225 100644
--- a/v7/appcompat/res/values-ko/strings.xml
+++ b/v7/appcompat/res/values-ko/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"검색"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"검색..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"검색어"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"검색어 삭제"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"검색어 보내기"</string>
diff --git a/v7/appcompat/res/values-ky-rKG/strings.xml b/v7/appcompat/res/values-ky-rKG/strings.xml
index c3cc6f7..b9f0bb1 100644
--- a/v7/appcompat/res/values-ky-rKG/strings.xml
+++ b/v7/appcompat/res/values-ky-rKG/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Издөө"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Издөө…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Издөө талаптары"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Талаптарды тазалоо"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Талап жөнөтүү"</string>
diff --git a/v7/appcompat/res/values-lo-rLA/strings.xml b/v7/appcompat/res/values-lo-rLA/strings.xml
index cc4170f..45f830f 100644
--- a/v7/appcompat/res/values-lo-rLA/strings.xml
+++ b/v7/appcompat/res/values-lo-rLA/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"ຊອກຫາ"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"ຊອກຫາ"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"ຊອກຫາ"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ລຶບຂໍ້ຄວາມຊອກຫາ"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ສົ່ງການຊອກຫາ"</string>
diff --git a/v7/appcompat/res/values-lt/strings.xml b/v7/appcompat/res/values-lt/strings.xml
index e5d8975..27713a7 100644
--- a/v7/appcompat/res/values-lt/strings.xml
+++ b/v7/appcompat/res/values-lt/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Paieška"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Ieškoti..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Paieškos užklausa"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Išvalyti užklausą"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Pateikti užklausą"</string>
diff --git a/v7/appcompat/res/values-lv/strings.xml b/v7/appcompat/res/values-lv/strings.xml
index 9f1a0f0..986e8eb 100644
--- a/v7/appcompat/res/values-lv/strings.xml
+++ b/v7/appcompat/res/values-lv/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s: %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s: %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Meklēt"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Meklējiet…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Meklēšanas vaicājums"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Notīrīt vaicājumu"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Iesniegt vaicājumu"</string>
diff --git a/v7/appcompat/res/values-mk-rMK/strings.xml b/v7/appcompat/res/values-mk-rMK/strings.xml
index 5df2278..916809a 100644
--- a/v7/appcompat/res/values-mk-rMK/strings.xml
+++ b/v7/appcompat/res/values-mk-rMK/strings.xml
@@ -26,6 +26,7 @@
     <skip />
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Пребарај"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Пребарување…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Пребарај барање"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Исчисти барање"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Поднеси барање"</string>
diff --git a/v7/appcompat/res/values-ml-rIN/strings.xml b/v7/appcompat/res/values-ml-rIN/strings.xml
index 3a0f82c..c122ed5 100644
--- a/v7/appcompat/res/values-ml-rIN/strings.xml
+++ b/v7/appcompat/res/values-ml-rIN/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"തിരയൽ"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"തിരയുക…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"തിരയൽ അന്വേഷണം"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"അന്വേഷണം മായ്‌ക്കുക"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"അന്വേഷണം സമർപ്പിക്കുക"</string>
diff --git a/v7/appcompat/res/values-mn-rMN/strings.xml b/v7/appcompat/res/values-mn-rMN/strings.xml
index 073f0df..eadbb9f 100644
--- a/v7/appcompat/res/values-mn-rMN/strings.xml
+++ b/v7/appcompat/res/values-mn-rMN/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Хайх"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Хайх..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Хайх асуулга"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Асуулгыг цэвэрлэх"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Асуулгыг илгээх"</string>
diff --git a/v7/appcompat/res/values-mr-rIN/strings.xml b/v7/appcompat/res/values-mr-rIN/strings.xml
index 3177012..a2fb945 100644
--- a/v7/appcompat/res/values-mr-rIN/strings.xml
+++ b/v7/appcompat/res/values-mr-rIN/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"शोध"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"शोधा…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"शोध क्वेरी"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"क्‍वेरी स्‍पष्‍ट करा"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"क्वेरी सबमिट करा"</string>
diff --git a/v7/appcompat/res/values-ms-rMY/strings.xml b/v7/appcompat/res/values-ms-rMY/strings.xml
index d77560f..f42fe3d 100644
--- a/v7/appcompat/res/values-ms-rMY/strings.xml
+++ b/v7/appcompat/res/values-ms-rMY/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Cari"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Cari…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Pertanyaan carian"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Kosongkan pertanyaan"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Serah pertanyaan"</string>
diff --git a/v7/appcompat/res/values-my-rMM/strings.xml b/v7/appcompat/res/values-my-rMM/strings.xml
index 38602a2..6313bcb 100644
--- a/v7/appcompat/res/values-my-rMM/strings.xml
+++ b/v7/appcompat/res/values-my-rMM/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s၊ %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s ၊ %2$s ၊ %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"ရှာဖွေရန်"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"ရှာဖွေပါ..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"ရှာစရာ အချက်အလက်နေရာ"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ရှာစရာ အချက်အလက်များ ရှင်းလင်းရန်"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ရှာဖွေစရာ အချက်အလက်ကို အတည်ပြုရန်"</string>
diff --git a/v7/appcompat/res/values-nb/strings.xml b/v7/appcompat/res/values-nb/strings.xml
index 0cf77a1..6e50a58 100644
--- a/v7/appcompat/res/values-nb/strings.xml
+++ b/v7/appcompat/res/values-nb/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s – %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s – %2$s – %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Søk"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Søk …"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Søkeord"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Slett søket"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Utfør søket"</string>
diff --git a/v7/appcompat/res/values-ne-rNP/strings.xml b/v7/appcompat/res/values-ne-rNP/strings.xml
index dee552a..5b804d8 100644
--- a/v7/appcompat/res/values-ne-rNP/strings.xml
+++ b/v7/appcompat/res/values-ne-rNP/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"खोज्नुहोस्"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"खोज्नुहोस्..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"जिज्ञासाको खोज गर्नुहोस्"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"प्रश्‍न हटाउनुहोस्"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"जिज्ञासा पेस गर्नुहोस्"</string>
diff --git a/v7/appcompat/res/values-nl/strings.xml b/v7/appcompat/res/values-nl/strings.xml
index cd67586..61546df 100644
--- a/v7/appcompat/res/values-nl/strings.xml
+++ b/v7/appcompat/res/values-nl/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Zoeken"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Zoeken…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Zoekopdracht"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Zoekopdracht wissen"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Zoekopdracht verzenden"</string>
diff --git a/v7/appcompat/res/values-pl/strings.xml b/v7/appcompat/res/values-pl/strings.xml
index 7245cad..9d99e92 100644
--- a/v7/appcompat/res/values-pl/strings.xml
+++ b/v7/appcompat/res/values-pl/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Szukaj"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Szukaj…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Wyszukiwane hasło"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Wyczyść zapytanie"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Wyślij zapytanie"</string>
diff --git a/v7/appcompat/res/values-pt-rPT/strings.xml b/v7/appcompat/res/values-pt-rPT/strings.xml
index 8d05c98..e905530 100644
--- a/v7/appcompat/res/values-pt-rPT/strings.xml
+++ b/v7/appcompat/res/values-pt-rPT/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Pesquisar"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Pesquisar..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de pesquisa"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Limpar consulta"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
diff --git a/v7/appcompat/res/values-pt/strings.xml b/v7/appcompat/res/values-pt/strings.xml
index 4fecc5c..b6c94e9 100644
--- a/v7/appcompat/res/values-pt/strings.xml
+++ b/v7/appcompat/res/values-pt/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Pesquisar"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Pesquisar..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de pesquisa"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Limpar consulta"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
diff --git a/v7/appcompat/res/values-ro/strings.xml b/v7/appcompat/res/values-ro/strings.xml
index ea1ecbd..4c741f3 100644
--- a/v7/appcompat/res/values-ro/strings.xml
+++ b/v7/appcompat/res/values-ro/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Căutați"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Căutați…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Interogare de căutare"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Ștergeți interogarea"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Trimiteți interogarea"</string>
diff --git a/v7/appcompat/res/values-ru/strings.xml b/v7/appcompat/res/values-ru/strings.xml
index 3042ce6..4879b5d 100644
--- a/v7/appcompat/res/values-ru/strings.xml
+++ b/v7/appcompat/res/values-ru/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Поиск"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Поиск"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Поисковый запрос"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Удалить запрос"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Отправить запрос"</string>
diff --git a/v7/appcompat/res/values-si-rLK/strings.xml b/v7/appcompat/res/values-si-rLK/strings.xml
index 6fb7d6a..252448f 100644
--- a/v7/appcompat/res/values-si-rLK/strings.xml
+++ b/v7/appcompat/res/values-si-rLK/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"සෙවීම"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"සොයන්න..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"සෙවුම් විමසුම"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"විමසුම හිස් කරන්න"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"විමසුම යොමු කරන්න"</string>
diff --git a/v7/appcompat/res/values-sk/strings.xml b/v7/appcompat/res/values-sk/strings.xml
index f48351f..501e065 100644
--- a/v7/appcompat/res/values-sk/strings.xml
+++ b/v7/appcompat/res/values-sk/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Hľadať"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Vyhľadať…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Vyhľadávací dopyt"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Vymazať dopyt"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Odoslať dopyt"</string>
diff --git a/v7/appcompat/res/values-sl/strings.xml b/v7/appcompat/res/values-sl/strings.xml
index 3e8c881..da49123 100644
--- a/v7/appcompat/res/values-sl/strings.xml
+++ b/v7/appcompat/res/values-sl/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Iskanje"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Iskanje …"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Iskalna poizvedba"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Izbris poizvedbe"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Pošiljanje poizvedbe"</string>
diff --git a/v7/appcompat/res/values-sr/strings.xml b/v7/appcompat/res/values-sr/strings.xml
index cb85060..9e2a9e8 100644
--- a/v7/appcompat/res/values-sr/strings.xml
+++ b/v7/appcompat/res/values-sr/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Претрага"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Претражите..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Упит за претрагу"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Брисање упита"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Слање упита"</string>
diff --git a/v7/appcompat/res/values-sv/strings.xml b/v7/appcompat/res/values-sv/strings.xml
index be6827e..905e3ea 100644
--- a/v7/appcompat/res/values-sv/strings.xml
+++ b/v7/appcompat/res/values-sv/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Sök"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Sök …"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Sökfråga"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Ta bort frågan"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Skicka fråga"</string>
diff --git a/v7/appcompat/res/values-sw/strings.xml b/v7/appcompat/res/values-sw/strings.xml
index 7f07a2a..7287c0d 100644
--- a/v7/appcompat/res/values-sw/strings.xml
+++ b/v7/appcompat/res/values-sw/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Tafuta"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Tafuta…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Hoja ya utafutaji"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Futa hoja"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Wasilisha hoja"</string>
diff --git a/v7/appcompat/res/values-sw600dp/dimens.xml b/v7/appcompat/res/values-sw600dp/dimens.xml
index cba2150..e221b50 100644
--- a/v7/appcompat/res/values-sw600dp/dimens.xml
+++ b/v7/appcompat/res/values-sw600dp/dimens.xml
@@ -29,5 +29,12 @@
     <dimen name="abc_action_bar_default_height_material">64dp</dimen>
     <!-- Default padding of an action bar. -->
     <dimen name="abc_action_bar_default_padding_material">4dp</dimen>
+    <!-- Default content inset of an action bar. -->
+    <dimen name="abc_action_bar_content_inset_material">24dp</dimen>
+
+    <!-- Padding to add to the start of the overflow action button. -->
+    <dimen name="abc_action_bar_navigation_padding_start_material">8dp</dimen>
+    <!-- Padding to add to the end of the overflow action button. -->
+    <dimen name="abc_action_bar_overflow_padding_end_material">18dp</dimen>
 
 </resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-ta-rIN/strings.xml b/v7/appcompat/res/values-ta-rIN/strings.xml
index d72fcc4..ab728a6 100644
--- a/v7/appcompat/res/values-ta-rIN/strings.xml
+++ b/v7/appcompat/res/values-ta-rIN/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"தேடு"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"தேடு..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"தேடல் வினவல்"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"வினவலை அழி"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"வினவலைச் சமர்ப்பி"</string>
diff --git a/v7/appcompat/res/values-te-rIN/strings.xml b/v7/appcompat/res/values-te-rIN/strings.xml
index 20fbbeb..901859b 100644
--- a/v7/appcompat/res/values-te-rIN/strings.xml
+++ b/v7/appcompat/res/values-te-rIN/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"శోధించు"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"శోధించు..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"ప్రశ్న శోధించండి"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ప్రశ్నను క్లియర్ చేయి"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ప్రశ్నని సమర్పించు"</string>
diff --git a/v7/appcompat/res/values-th/strings.xml b/v7/appcompat/res/values-th/strings.xml
index 38b5c90..e962aa5 100644
--- a/v7/appcompat/res/values-th/strings.xml
+++ b/v7/appcompat/res/values-th/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"ค้นหา"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"ค้นหา…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"ข้อความค้นหา"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ล้างข้อความค้นหา"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ส่งข้อความค้นหา"</string>
diff --git a/v7/appcompat/res/values-tl/strings.xml b/v7/appcompat/res/values-tl/strings.xml
index 079990f..f41b15f 100644
--- a/v7/appcompat/res/values-tl/strings.xml
+++ b/v7/appcompat/res/values-tl/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Maghanap"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Maghanap…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Query sa paghahanap"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"I-clear ang query"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Isumite ang query"</string>
diff --git a/v7/appcompat/res/values-tr/strings.xml b/v7/appcompat/res/values-tr/strings.xml
index 51ee6ec..9cde4a2 100644
--- a/v7/appcompat/res/values-tr/strings.xml
+++ b/v7/appcompat/res/values-tr/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Ara"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Ara…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Arama sorgusu"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Sorguyu temizle"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Sorguyu gönder"</string>
diff --git a/v7/appcompat/res/values-uk/strings.xml b/v7/appcompat/res/values-uk/strings.xml
index 28f6b8e..0a5c31c 100644
--- a/v7/appcompat/res/values-uk/strings.xml
+++ b/v7/appcompat/res/values-uk/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Пошук"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Пошук…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Пошуковий запит"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Очистити запит"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Надіслати запит"</string>
diff --git a/v7/appcompat/res/values-ur-rPK/strings.xml b/v7/appcompat/res/values-ur-rPK/strings.xml
index 787651e..e6f6260 100644
--- a/v7/appcompat/res/values-ur-rPK/strings.xml
+++ b/v7/appcompat/res/values-ur-rPK/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"تلاش کریں"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"تلاش کریں…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"استفسار تلاش کریں"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"استفسار صاف کریں"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"استفسار جمع کرائیں"</string>
diff --git a/v7/appcompat/res/values-uz-rUZ/strings.xml b/v7/appcompat/res/values-uz-rUZ/strings.xml
index 033e4e0..241b3b1 100644
--- a/v7/appcompat/res/values-uz-rUZ/strings.xml
+++ b/v7/appcompat/res/values-uz-rUZ/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Izlash"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Qidirish…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"So‘rovni izlash"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"So‘rovni tozalash"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"So‘rov yaratish"</string>
diff --git a/v7/appcompat/res/values-v11/themes_base.xml b/v7/appcompat/res/values-v11/themes_base.xml
index 6880321..2ca5b6f 100644
--- a/v7/appcompat/res/values-v11/themes_base.xml
+++ b/v7/appcompat/res/values-v11/themes_base.xml
@@ -22,12 +22,17 @@
         unbundled Action Bar.
     -->
     <eat-comment/>
-    <style name="Platform.AppCompat" parent="android:Theme.Holo">
+
+    <style name="Platform.AppCompat" parent="Platform.V11.AppCompat" />
+    <style name="Platform.AppCompat.Light" parent="Platform.V11.AppCompat.Light" />
+
+    <style name="Platform.V11.AppCompat" parent="android:Theme.Holo">
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowActionBar">false</item>
 
-        <item name="buttonBarStyle">?android:attr/buttonBarStyle</item>
-        <item name="buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
+        <item name="android:buttonBarStyle">?attr/buttonBarStyle</item>
+        <item name="android:buttonBarButtonStyle">?attr/buttonBarButtonStyle</item>
+
         <item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
 
         <!-- Window colors -->
@@ -35,7 +40,7 @@
         <item name="android:colorForegroundInverse">@color/bright_foreground_material_light</item>
         <item name="android:colorBackground">@color/background_material_dark</item>
         <item name="android:colorBackgroundCacheHint">@color/abc_background_cache_hint_selector_material_dark</item>
-        <item name="android:disabledAlpha">0.5</item>
+        <item name="android:disabledAlpha">@dimen/abc_disabled_alpha_material_dark</item>
         <item name="android:backgroundDimAmount">0.6</item>
         <item name="android:windowBackground">@color/background_material_dark</item>
 
@@ -65,20 +70,21 @@
         <item name="android:textAppearanceSmall">@style/TextAppearance.AppCompat.Small</item>
         <item name="android:textAppearanceSmallInverse">@style/TextAppearance.AppCompat.Small.Inverse</item>
 
-        <item name="android:spinnerStyle">@style/Widget.AppCompat.Spinner</item>
-        <item name="android:spinnerItemStyle">@style/Widget.AppCompat.TextView.SpinnerItem</item>
-        <item name="android:dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
-
         <item name="android:listChoiceIndicatorSingle">@drawable/abc_btn_radio_material</item>
         <item name="android:listChoiceIndicatorMultiple">@drawable/abc_btn_check_material</item>
+
+        <item name="android:actionModeCutDrawable">?actionModeCutDrawable</item>
+        <item name="android:actionModeCopyDrawable">?actionModeCopyDrawable</item>
+        <item name="android:actionModePasteDrawable">?actionModePasteDrawable</item>
     </style>
 
-    <style name="Platform.AppCompat.Light" parent="android:Theme.Holo.Light">
+    <style name="Platform.V11.AppCompat.Light" parent="android:Theme.Holo.Light">
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowActionBar">false</item>
 
-        <item name="buttonBarStyle">?android:attr/buttonBarStyle</item>
-        <item name="buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
+        <item name="android:buttonBarStyle">?attr/buttonBarStyle</item>
+        <item name="android:buttonBarButtonStyle">?attr/buttonBarButtonStyle</item>
+
         <item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
 
         <!-- Window colors -->
@@ -86,7 +92,7 @@
         <item name="android:colorForegroundInverse">@color/bright_foreground_material_dark</item>
         <item name="android:colorBackground">@color/background_material_light</item>
         <item name="android:colorBackgroundCacheHint">@color/abc_background_cache_hint_selector_material_light</item>
-        <item name="android:disabledAlpha">0.5</item>
+        <item name="android:disabledAlpha">@dimen/abc_disabled_alpha_material_light</item>
         <item name="android:backgroundDimAmount">0.6</item>
         <item name="android:windowBackground">@color/background_material_light</item>
 
@@ -117,259 +123,27 @@
         <item name="android:textAppearanceSmall">@style/TextAppearance.AppCompat.Small</item>
         <item name="android:textAppearanceSmallInverse">@style/TextAppearance.AppCompat.Small.Inverse</item>
 
-        <item name="android:spinnerStyle">@style/Widget.AppCompat.Spinner</item>
-        <item name="android:spinnerItemStyle">@style/Widget.AppCompat.TextView.SpinnerItem</item>
-        <item name="android:dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
-
         <item name="android:listChoiceIndicatorSingle">@drawable/abc_btn_radio_material</item>
         <item name="android:listChoiceIndicatorMultiple">@drawable/abc_btn_check_material</item>
-    </style>
 
-    <style name="Platform.AppCompat.Dialog" parent="android:Theme.Holo.Dialog">
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowActionBar">false</item>
-
-        <item name="buttonBarStyle">?android:attr/buttonBarStyle</item>
-        <item name="buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
-        <item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
-
-        <!-- Window colors -->
-        <item name="android:colorForeground">@color/bright_foreground_material_dark</item>
-        <item name="android:colorForegroundInverse">@color/bright_foreground_material_light</item>
-        <item name="android:colorBackground">@color/background_material_dark</item>
-        <item name="android:colorBackgroundCacheHint">@color/abc_background_cache_hint_selector_material_dark</item>
-        <item name="android:disabledAlpha">0.5</item>
-        <item name="android:backgroundDimAmount">0.6</item>
-        <item name="android:windowBackground">@color/background_material_dark</item>
-
-        <!-- Text colors -->
-        <item name="android:textColorPrimary">@color/abc_primary_text_material_dark</item>
-        <item name="android:textColorPrimaryInverse">@color/abc_primary_text_material_light</item>
-        <item name="android:textColorPrimaryDisableOnly">@color/abc_primary_text_disable_only_material_dark</item>
-        <item name="android:textColorSecondary">@color/abc_secondary_text_material_dark</item>
-        <item name="android:textColorSecondaryInverse">@color/abc_secondary_text_material_light</item>
-        <item name="android:textColorTertiary">@color/abc_secondary_text_material_dark</item>
-        <item name="android:textColorTertiaryInverse">@color/abc_secondary_text_material_light</item>
-        <item name="android:textColorHint">@color/hint_foreground_material_dark</item>
-        <item name="android:textColorHintInverse">@color/hint_foreground_material_light</item>
-        <item name="android:textColorHighlight">@color/highlighted_text_material_dark</item>
-        <item name="android:textColorHighlightInverse">@color/highlighted_text_material_light</item>
-        <item name="android:textColorLink">@color/link_text_material_dark</item>
-        <item name="android:textColorLinkInverse">@color/link_text_material_light</item>
-        <item name="android:textColorAlertDialogListItem">@color/abc_primary_text_material_dark</item>
-
-        <!-- Text styles -->
-        <item name="android:textAppearance">@style/TextAppearance.AppCompat</item>
-        <item name="android:textAppearanceInverse">@style/TextAppearance.AppCompat.Inverse</item>
-        <item name="android:textAppearanceLarge">@style/TextAppearance.AppCompat.Large</item>
-        <item name="android:textAppearanceLargeInverse">@style/TextAppearance.AppCompat.Large.Inverse</item>
-        <item name="android:textAppearanceMedium">@style/TextAppearance.AppCompat.Medium</item>
-        <item name="android:textAppearanceMediumInverse">@style/TextAppearance.AppCompat.Medium.Inverse</item>
-        <item name="android:textAppearanceSmall">@style/TextAppearance.AppCompat.Small</item>
-        <item name="android:textAppearanceSmallInverse">@style/TextAppearance.AppCompat.Small.Inverse</item>
-
-        <item name="android:spinnerStyle">@style/Widget.AppCompat.Spinner</item>
-        <item name="android:spinnerItemStyle">@style/Widget.AppCompat.TextView.SpinnerItem</item>
-        <item name="android:dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
-
-        <item name="android:listChoiceIndicatorSingle">@drawable/abc_btn_radio_material</item>
-        <item name="android:listChoiceIndicatorMultiple">@drawable/abc_btn_check_material</item>
-    </style>
-
-    <style name="Platform.AppCompat.Light.Dialog" parent="android:Theme.Holo.Light.Dialog">
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowActionBar">false</item>
-
-        <item name="buttonBarStyle">?android:attr/buttonBarStyle</item>
-        <item name="buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
-        <item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
-
-        <!-- Window colors -->
-        <item name="android:colorForeground">@color/bright_foreground_material_light</item>
-        <item name="android:colorForegroundInverse">@color/bright_foreground_material_dark</item>
-        <item name="android:colorBackground">@color/background_material_light</item>
-        <item name="android:colorBackgroundCacheHint">@color/abc_background_cache_hint_selector_material_light</item>
-        <item name="android:disabledAlpha">0.5</item>
-        <item name="android:backgroundDimAmount">0.6</item>
-        <item name="android:windowBackground">@color/background_material_light</item>
-
-        <!-- Text colors -->
-        <item name="android:textColorPrimary">@color/abc_primary_text_material_light</item>
-        <item name="android:textColorPrimaryInverse">@color/abc_primary_text_material_dark</item>
-        <item name="android:textColorSecondary">@color/abc_secondary_text_material_light</item>
-        <item name="android:textColorSecondaryInverse">@color/abc_secondary_text_material_dark</item>
-        <item name="android:textColorTertiary">@color/abc_secondary_text_material_light</item>
-        <item name="android:textColorTertiaryInverse">@color/abc_secondary_text_material_dark</item>
-        <item name="android:textColorPrimaryDisableOnly">@color/abc_primary_text_disable_only_material_light</item>
-        <item name="android:textColorPrimaryInverseDisableOnly">@color/abc_primary_text_disable_only_material_dark</item>
-        <item name="android:textColorHint">@color/hint_foreground_material_light</item>
-        <item name="android:textColorHintInverse">@color/hint_foreground_material_dark</item>
-        <item name="android:textColorHighlight">@color/highlighted_text_material_light</item>
-        <item name="android:textColorHighlightInverse">@color/highlighted_text_material_dark</item>
-        <item name="android:textColorLink">@color/link_text_material_light</item>
-        <item name="android:textColorLinkInverse">@color/link_text_material_dark</item>
-        <item name="android:textColorAlertDialogListItem">@color/abc_primary_text_material_light</item>
-
-        <!-- Text styles -->
-        <item name="android:textAppearance">@style/TextAppearance.AppCompat</item>
-        <item name="android:textAppearanceInverse">@style/TextAppearance.AppCompat.Inverse</item>
-        <item name="android:textAppearanceLarge">@style/TextAppearance.AppCompat.Large</item>
-        <item name="android:textAppearanceLargeInverse">@style/TextAppearance.AppCompat.Large.Inverse</item>
-        <item name="android:textAppearanceMedium">@style/TextAppearance.AppCompat.Medium</item>
-        <item name="android:textAppearanceMediumInverse">@style/TextAppearance.AppCompat.Medium.Inverse</item>
-        <item name="android:textAppearanceSmall">@style/TextAppearance.AppCompat.Small</item>
-        <item name="android:textAppearanceSmallInverse">@style/TextAppearance.AppCompat.Small.Inverse</item>
-
-        <item name="android:spinnerStyle">@style/Widget.AppCompat.Spinner</item>
-        <item name="android:spinnerItemStyle">@style/Widget.AppCompat.TextView.SpinnerItem</item>
-        <item name="android:dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
-
-        <item name="android:listChoiceIndicatorSingle">@drawable/abc_btn_radio_material</item>
-        <item name="android:listChoiceIndicatorMultiple">@drawable/abc_btn_check_material</item>
-    </style>
-
-    <style name="Base.V11.Theme.AppCompat" parent="Base.V7.Theme.AppCompat" />
-    <style name="Base.V11.Theme.AppCompat.Light" parent="Base.V7.Theme.AppCompat.Light" />
-    <style name="Base.V11.Theme.AppCompat.Dialog" parent="Base.V7.Theme.AppCompat.Dialog" />
-
-    <style name="Base.V11.Theme.AppCompat.Light.Dialog" parent="Platform.AppCompat.Light.Dialog">
-        <item name="windowActionBar">true</item>
-        <item name="windowActionBarOverlay">false</item>
-        <item name="isLightTheme">true</item>
-
-        <item name="selectableItemBackground">@drawable/abc_item_background_holo_light</item>
-        <item name="selectableItemBackgroundBorderless">?attr/selectableItemBackground</item>
-        <item name="homeAsUpIndicator">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
-
-        <item name="dividerVertical">@drawable/abc_list_divider_mtrl_alpha</item>
-        <item name="dividerHorizontal">@drawable/abc_list_divider_mtrl_alpha</item>
-
-        <!-- Action Bar Styles -->
-        <item name="actionBarTabStyle">@style/Widget.AppCompat.Light.ActionBar.TabView</item>
-        <item name="actionBarTabBarStyle">@style/Widget.AppCompat.Light.ActionBar.TabBar</item>
-        <item name="actionBarTabTextStyle">@style/Widget.AppCompat.Light.ActionBar.TabText</item>
-        <item name="actionButtonStyle">@style/Widget.AppCompat.Light.ActionButton</item>
-        <item name="actionOverflowButtonStyle">@style/Widget.AppCompat.Light.ActionButton.Overflow</item>
-        <item name="actionOverflowMenuStyle">@style/Widget.AppCompat.Light.PopupMenu.Overflow</item>
-        <item name="actionBarStyle">@style/Widget.AppCompat.Light.ActionBar.Solid</item>
-        <item name="actionBarSplitStyle">?attr/actionBarStyle</item>
-        <item name="actionBarWidgetTheme">@null</item>
-        <item name="actionBarTheme">@style/ThemeOverlay.AppCompat.ActionBar</item>
-        <item name="actionBarSize">@dimen/abc_action_bar_default_height_material</item>
-        <item name="actionBarDivider">?attr/dividerVertical</item>
-        <item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
-        <item name="actionMenuTextAppearance">@style/TextAppearance.AppCompat.Widget.ActionBar.Menu</item>
-        <item name="actionMenuTextColor">?android:attr/textColorPrimaryDisableOnly</item>
-
-        <!-- Action Mode -->
-        <item name="actionModeStyle">@style/Widget.AppCompat.ActionMode</item>
-        <item name="actionModeBackground">@drawable/abc_cab_background_top_material</item>
-        <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item>
-        <item name="actionModeCloseDrawable">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
-        <item name="actionModeCloseButtonStyle">@style/Widget.AppCompat.ActionButton.CloseMode</item>
-
-        <item name="actionModeCutDrawable">@drawable/abc_ic_menu_cut_mtrl_alpha</item>
-        <item name="actionModeCopyDrawable">@drawable/abc_ic_menu_copy_mtrl_am_alpha</item>
-        <item name="actionModePasteDrawable">@drawable/abc_ic_menu_paste_mtrl_am_alpha</item>
-        <item name="actionModeSelectAllDrawable">@drawable/abc_ic_menu_selectall_mtrl_alpha</item>
-        <item name="actionModeShareDrawable">@drawable/abc_ic_menu_share_mtrl_alpha</item>
-
-        <!-- Dropdown Spinner Attributes -->
-        <item name="actionDropDownStyle">@style/Widget.AppCompat.Light.Spinner.DropDown.ActionBar</item>
-
-        <!-- Panel attributes -->
-        <item name="panelMenuListWidth">@dimen/abc_panel_menu_list_width</item>
-        <item name="panelMenuListTheme">@style/Theme.AppCompat.CompactMenu</item>
-        <item name="android:panelBackground">@android:color/transparent</item>
-        <item name="panelBackground">@drawable/abc_menu_hardkey_panel_mtrl_mult</item>
-        <item name="listChoiceBackgroundIndicator">@drawable/abc_list_selector_holo_light</item>
-
-        <!-- List attributes -->
-        <item name="textAppearanceListItem">@style/TextAppearance.AppCompat.Subhead</item>
-        <item name="textAppearanceListItemSmall">@style/TextAppearance.AppCompat.Subhead</item>
-        <item name="listPreferredItemHeight">64dp</item>
-        <item name="listPreferredItemHeightSmall">48dp</item>
-        <item name="listPreferredItemHeightLarge">80dp</item>
-        <item name="listPreferredItemPaddingLeft">16dip</item>
-        <item name="listPreferredItemPaddingRight">16dip</item>
-
-        <!-- Required for use of support_simple_spinner_dropdown_item.xml -->
-        <item name="spinnerDropDownItemStyle">@style/Widget.AppCompat.DropDownItem.Spinner</item>
-        <item name="dropdownListPreferredItemHeight">?attr/listPreferredItemHeightSmall</item>
-
-        <!-- Popup Menu styles -->
-        <item name="popupMenuStyle">@style/Widget.AppCompat.Light.PopupMenu</item>
-        <item name="textAppearanceLargePopupMenu">@style/TextAppearance.AppCompat.Light.Widget.PopupMenu.Large</item>
-        <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.AppCompat.Light.Widget.PopupMenu.Small</item>
-        <item name="listPopupWindowStyle">@style/Widget.AppCompat.ListPopupWindow</item>
-        <item name="dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
-
-        <!-- SearchView attributes -->
-        <item name="searchViewStyle">@style/Widget.AppCompat.Light.SearchView</item>
-        <item name="android:dropDownItemStyle">@style/Widget.AppCompat.DropDownItem.Spinner</item>
-        <item name="textColorSearchUrl">@color/abc_search_url_text</item>
-        <item name="textAppearanceSearchResultTitle">@style/TextAppearance.AppCompat.SearchResult.Title</item>
-        <item name="textAppearanceSearchResultSubtitle">@style/TextAppearance.AppCompat.SearchResult.Subtitle</item>
-
-        <!-- ShareActionProvider attributes -->
-        <item name="activityChooserViewStyle">@style/Widget.AppCompat.ActivityChooserView</item>
-
-        <!-- Toolbar styles -->
-        <item name="toolbarStyle">@style/Widget.AppCompat.Toolbar</item>
-        <item name="toolbarNavigationButtonStyle">@style/Widget.AppCompat.Toolbar.Button.Navigation</item>
-
-        <item name="android:editTextStyle">@style/Widget.AppCompat.EditText</item>
-        <item name="editTextBackground">@drawable/abc_edit_text_material</item>
-        <item name="editTextColor">?android:attr/textColorPrimary</item>
-        <item name="android:autoCompleteTextViewStyle">@style/Widget.AppCompat.AutoCompleteTextView</item>
-
-        <!-- Color palette -->
-        <item name="colorPrimaryDark">@color/primary_dark_material_light</item>
-        <item name="colorPrimary">@color/primary_material_light</item>
-        <item name="colorAccent">@color/accent_material_light</item>
-
-        <item name="colorControlNormal">?android:attr/textColorSecondary</item>
-        <item name="colorControlActivated">?attr/colorAccent</item>
-        <item name="colorControlHighlight">@color/ripple_material_light</item>
-        <item name="colorButtonNormal">@color/button_material_light</item>
-        <item name="colorSwitchThumbNormal">@color/switch_thumb_normal_material_light</item>
-
-        <item name="switchStyle">@style/Widget.AppCompat.CompoundButton.Switch</item>
-
-        <item name="android:ratingBarStyle">@style/Widget.AppCompat.RatingBar</item>
-
-        <!-- Button styles -->
-        <item name="android:buttonStyle">@style/Widget.AppCompat.Button</item>
-        <item name="android:buttonStyleSmall">@style/Widget.AppCompat.Button.Small</item>
-        <item name="android:textAppearanceButton">@style/TextAppearance.AppCompat.Button</item>
-    </style>
-
-    <style name="Base.Theme.AppCompat" parent="Base.V11.Theme.AppCompat">
         <item name="android:actionModeCutDrawable">?actionModeCutDrawable</item>
         <item name="android:actionModeCopyDrawable">?actionModeCopyDrawable</item>
         <item name="android:actionModePasteDrawable">?actionModePasteDrawable</item>
-        <item name="android:actionModeShareDrawable">?actionModeShareDrawable</item>
     </style>
 
-    <style name="Base.Theme.AppCompat.Light" parent="Base.V11.Theme.AppCompat.Light">
-        <item name="android:actionModeCutDrawable">?actionModeCutDrawable</item>
-        <item name="android:actionModeCopyDrawable">?actionModeCopyDrawable</item>
-        <item name="android:actionModePasteDrawable">?actionModePasteDrawable</item>
-        <item name="android:actionModeShareDrawable">?actionModeShareDrawable</item>
+    <style name="Base.V11.Theme.AppCompat.Dialog" parent="Base.V7.Theme.AppCompat.Dialog">
+        <item name="android:buttonBarStyle">@style/Widget.AppCompat.ButtonBar.AlertDialog</item>
+        <item name="android:borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
+        <item name="android:windowCloseOnTouchOutside">@bool/abc_config_closeDialogWhenTouchOutside</item>
     </style>
 
-    <style name="Base.Theme.AppCompat.Dialog" parent="Base.V11.Theme.AppCompat.Dialog">
-        <item name="android:actionModeCutDrawable">?actionModeCutDrawable</item>
-        <item name="android:actionModeCopyDrawable">?actionModeCopyDrawable</item>
-        <item name="android:actionModePasteDrawable">?actionModePasteDrawable</item>
-        <item name="android:actionModeShareDrawable">?actionModeShareDrawable</item>
+    <style name="Base.V11.Theme.AppCompat.Light.Dialog" parent="Base.V7.Theme.AppCompat.Light.Dialog">
+        <item name="android:buttonBarStyle">@style/Widget.AppCompat.ButtonBar.AlertDialog</item>
+        <item name="android:borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
+        <item name="android:windowCloseOnTouchOutside">@bool/abc_config_closeDialogWhenTouchOutside</item>
     </style>
 
-    <style name="Base.Theme.AppCompat.Light.Dialog" parent="Base.V11.Theme.AppCompat.Light.Dialog">
-        <item name="android:actionModeCutDrawable">?actionModeCutDrawable</item>
-        <item name="android:actionModeCopyDrawable">?actionModeCopyDrawable</item>
-        <item name="android:actionModePasteDrawable">?actionModePasteDrawable</item>
-        <item name="android:actionModeShareDrawable">?actionModeShareDrawable</item>
-    </style>
+    <style name="Base.Theme.AppCompat.Dialog" parent="Base.V11.Theme.AppCompat.Dialog" />
+    <style name="Base.Theme.AppCompat.Light.Dialog" parent="Base.V11.Theme.AppCompat.Light.Dialog" />
 
 </resources>
diff --git a/v7/appcompat/res/values-v12/themes_base.xml b/v7/appcompat/res/values-v12/themes_base.xml
new file mode 100644
index 0000000..c912434
--- /dev/null
+++ b/v7/appcompat/res/values-v12/themes_base.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.
+-->
+
+<resources>
+
+    <style name="Platform.AppCompat" parent="Platform.V12.AppCompat" />
+    <style name="Platform.AppCompat.Light" parent="Platform.V12.AppCompat.Light" />
+
+    <style name="Platform.V12.AppCompat" parent="Platform.V11.AppCompat">
+        <item name="android:textCursorDrawable">@drawable/abc_text_cursor_mtrl_alpha</item>
+    </style>
+
+    <style name="Platform.V12.AppCompat.Light" parent="Platform.V11.AppCompat.Light">
+        <item name="android:textCursorDrawable">@drawable/abc_text_cursor_mtrl_alpha</item>
+    </style>
+
+</resources>
diff --git a/v7/appcompat/res/values-v14/themes_base.xml b/v7/appcompat/res/values-v14/themes_base.xml
index 3f26ca2..5fdc73c 100644
--- a/v7/appcompat/res/values-v14/themes_base.xml
+++ b/v7/appcompat/res/values-v14/themes_base.xml
@@ -16,41 +16,21 @@
 
 <resources>
 
-    <style name="Base.V14.Theme.AppCompat" parent="Base.V11.Theme.AppCompat" />
-    <style name="Base.V14.Theme.AppCompat.Light" parent="Base.V11.Theme.AppCompat.Light" />
-    <style name="Base.V14.Theme.AppCompat.Dialog" parent="Base.V11.Theme.AppCompat.Dialog" />
-    <style name="Base.V14.Theme.AppCompat.Light.Dialog" parent="Base.V11.Theme.AppCompat.Light.Dialog" />
+    <style name="Platform.AppCompat" parent="Platform.V14.AppCompat" />
+    <style name="Platform.AppCompat.Light" parent="Platform.V14.AppCompat.Light" />
 
-    <style name="Base.Theme.AppCompat" parent="Base.V14.Theme.AppCompat">
-        <item name="android:actionModeCutDrawable">?actionModeCutDrawable</item>
-        <item name="android:actionModeCopyDrawable">?actionModeCopyDrawable</item>
-        <item name="android:actionModePasteDrawable">?actionModePasteDrawable</item>
+    <style name="Platform.V14.AppCompat" parent="Platform.V12.AppCompat">
         <item name="android:actionModeSelectAllDrawable">?actionModeSelectAllDrawable</item>
-        <item name="android:actionModeShareDrawable">?actionModeShareDrawable</item>
+
+        <item name="android:listPreferredItemPaddingLeft">@dimen/abc_list_item_padding_horizontal_material</item>
+        <item name="android:listPreferredItemPaddingRight">@dimen/abc_list_item_padding_horizontal_material</item>
     </style>
 
-    <style name="Base.Theme.AppCompat.Light" parent="Base.V14.Theme.AppCompat.Light">
-        <item name="android:actionModeCutDrawable">?actionModeCutDrawable</item>
-        <item name="android:actionModeCopyDrawable">?actionModeCopyDrawable</item>
-        <item name="android:actionModePasteDrawable">?actionModePasteDrawable</item>
+    <style name="Platform.V14.AppCompat.Light" parent="Platform.V12.AppCompat.Light">
         <item name="android:actionModeSelectAllDrawable">?actionModeSelectAllDrawable</item>
-        <item name="android:actionModeShareDrawable">?actionModeShareDrawable</item>
-    </style>
 
-    <style name="Base.Theme.AppCompat.Dialog" parent="Base.V14.Theme.AppCompat.Dialog">
-        <item name="android:actionModeCutDrawable">?actionModeCutDrawable</item>
-        <item name="android:actionModeCopyDrawable">?actionModeCopyDrawable</item>
-        <item name="android:actionModePasteDrawable">?actionModePasteDrawable</item>
-        <item name="android:actionModeSelectAllDrawable">?actionModeSelectAllDrawable</item>
-        <item name="android:actionModeShareDrawable">?actionModeShareDrawable</item>
-    </style>
-
-    <style name="Base.Theme.AppCompat.Light.Dialog" parent="Base.V14.Theme.AppCompat.Light.Dialog">
-        <item name="android:actionModeCutDrawable">?actionModeCutDrawable</item>
-        <item name="android:actionModeCopyDrawable">?actionModeCopyDrawable</item>
-        <item name="android:actionModePasteDrawable">?actionModePasteDrawable</item>
-        <item name="android:actionModeSelectAllDrawable">?actionModeSelectAllDrawable</item>
-        <item name="android:actionModeShareDrawable">?actionModeShareDrawable</item>
+        <item name="android:listPreferredItemPaddingLeft">@dimen/abc_list_item_padding_horizontal_material</item>
+        <item name="android:listPreferredItemPaddingRight">@dimen/abc_list_item_padding_horizontal_material</item>
     </style>
 
 </resources>
diff --git a/v7/appcompat/res/values-v17/styles_rtl.xml b/v7/appcompat/res/values-v17/styles_rtl.xml
index 0c7d861..d89a63d 100644
--- a/v7/appcompat/res/values-v17/styles_rtl.xml
+++ b/v7/appcompat/res/values-v17/styles_rtl.xml
@@ -47,14 +47,9 @@
         <item name="android:paddingEnd">8dp</item>
     </style>
 
-    <style name="RtlOverlay.Widget.AppCompat.ActionButton.CloseMode" parent="Base.Widget.AppCompat.ActionButton.CloseMode">
-        <item name="android:paddingStart">8dp</item>
-        <item name="android:layout_marginEnd">16dp</item>
-    </style>
-
     <style name="RtlOverlay.Widget.AppCompat.ActionButton.Overflow" parent="Base.Widget.AppCompat.ActionButton.Overflow">
-        <item name="android:paddingStart">0dp</item>
-        <item name="android:paddingEnd">12dp</item>
+        <item name="android:paddingStart">@dimen/abc_action_bar_overflow_padding_start_material</item>
+        <item name="android:paddingEnd">@dimen/abc_action_bar_overflow_padding_end_material</item>
     </style>
 
     <style name="RtlOverlay.Widget.AppCompat.PopupMenuItem" parent="android:Widget">
@@ -70,4 +65,8 @@
         <item name="android:textAlignment">viewStart</item>
     </style>
 
+    <style name="RtlOverlay.Widget.AppCompat.Toolbar.Button.Navigation" parent="Base.Widget.AppCompat.Toolbar.Button.Navigation">
+        <item name="android:paddingStart">@dimen/abc_action_bar_navigation_padding_start_material</item>
+    </style>
+
 </resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-v18/dimens.xml b/v7/appcompat/res/values-v18/dimens.xml
new file mode 100644
index 0000000..bb784b7
--- /dev/null
+++ b/v7/appcompat/res/values-v18/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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>
+
+    <!-- Since SwitchCompat can use optical insets on v18+, reset the manual padding -->
+    <dimen name="abc_switch_padding">0px</dimen>
+
+</resources>
diff --git a/v7/appcompat/res/values-v21/styles_base.xml b/v7/appcompat/res/values-v21/styles_base.xml
index 6a352ef..1bf90ef 100644
--- a/v7/appcompat/res/values-v21/styles_base.xml
+++ b/v7/appcompat/res/values-v21/styles_base.xml
@@ -78,6 +78,7 @@
 
     <style name="Base.Widget.AppCompat.ActionButton.CloseMode"
            parent="android:Widget.Material.ActionButton.CloseMode">
+        <item name="android:minWidth">56dp</item>
     </style>
 
     <style name="Base.Widget.AppCompat.ActionButton.Overflow"
@@ -120,8 +121,8 @@
         <item name="popupPromptView">@layout/abc_simple_dropdown_hint</item>
     </style>
 
-    <style name="Base.Widget.AppCompat.ListView.Menu"
-           parent="android:Widget.Material.ListView" />
+    <style name="Base.Widget.AppCompat.ListView" parent="android:Widget.Material.ListView" />
+    <style name="Base.Widget.AppCompat.ListView.Menu" />
 
     <!-- Popup Menu -->
 
@@ -179,6 +180,16 @@
 
     <style name="Base.Widget.AppCompat.Button.Small" parent="android:Widget.Material.Button.Small" />
 
+    <style name="Base.Widget.AppCompat.Button.Borderless" parent="android:Widget.Material.Button.Borderless" />
+
+    <style name="Base.Widget.AppCompat.Button.Borderless.Colored" parent="android:Widget.Material.Button.Borderless.Colored" />
+
+    <style name="Base.Widget.AppCompat.ButtonBar" parent="android:Widget.Material.ButtonBar" />
+
+    <style name="Base.Widget.AppCompat.CompoundButton.CheckBox" parent="android:Widget.Material.CompoundButton.CheckBox" />
+
+    <style name="Base.Widget.AppCompat.CompoundButton.RadioButton" parent="android:Widget.Material.CompoundButton.RadioButton" />
+
     <!-- Progress Bar -->
 
     <style name="Base.Widget.AppCompat.ProgressBar.Horizontal"
diff --git a/v7/appcompat/res/values-v21/themes_base.xml b/v7/appcompat/res/values-v21/themes_base.xml
index c586030..a8c3df4 100644
--- a/v7/appcompat/res/values-v21/themes_base.xml
+++ b/v7/appcompat/res/values-v21/themes_base.xml
@@ -26,25 +26,20 @@
     <style name="Platform.AppCompat" parent="android:Theme.Material">
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowActionBar">false</item>
+
+        <item name="android:buttonBarStyle">?attr/buttonBarStyle</item>
+        <item name="android:buttonBarButtonStyle">?attr/buttonBarButtonStyle</item>
     </style>
 
     <style name="Platform.AppCompat.Light" parent="android:Theme.Material.Light">
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowActionBar">false</item>
-    </style>
 
-    <style name="Platform.AppCompat.Dialog" parent="android:Theme.Material.Dialog">
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowActionBar">false</item>
-    </style>
-
-    <style name="Platform.AppCompat.Light.Dialog" parent="android:Theme.Material.Light.Dialog">
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowActionBar">false</item>
+        <item name="android:buttonBarStyle">?attr/buttonBarStyle</item>
+        <item name="android:buttonBarButtonStyle">?attr/buttonBarButtonStyle</item>
     </style>
 
     <style name="Base.Theme.AppCompat" parent="Base.V21.Theme.AppCompat" />
-
     <style name="Base.Theme.AppCompat.Light" parent="Base.V21.Theme.AppCompat.Light" />
 
     <style name="Base.V21.Theme.AppCompat" parent="Base.V7.Theme.AppCompat">
@@ -75,6 +70,17 @@
         <item name="editTextColor">?android:attr/editTextColor</item>
         <item name="listChoiceBackgroundIndicator">?android:attr/listChoiceBackgroundIndicator</item>
 
+        <!-- Copy the platform default styles for the AppCompat widgets -->
+        <item name="autoCompleteTextViewStyle">?android:attr/autoCompleteTextViewStyle</item>
+        <item name="buttonStyle">?android:attr/buttonStyle</item>
+        <item name="buttonStyleSmall">?android:attr/buttonStyleSmall</item>
+        <item name="checkboxStyle">?android:attr/checkboxStyle</item>
+        <item name="checkedTextViewStyle">?android:attr/checkedTextViewStyle</item>
+        <item name="editTextStyle">?android:attr/editTextStyle</item>
+        <item name="radioButtonStyle">?android:attr/radioButtonStyle</item>
+        <item name="ratingBarStyle">?android:attr/ratingBarStyle</item>
+        <item name="spinnerStyle">?android:attr/spinnerStyle</item>
+
         <!-- Copy our color theme attributes to the framework -->
         <item name="android:colorPrimary">?attr/colorPrimary</item>
         <item name="android:colorPrimaryDark">?attr/colorPrimaryDark</item>
@@ -82,6 +88,7 @@
         <item name="android:colorControlNormal">?attr/colorControlNormal</item>
         <item name="android:colorControlActivated">?attr/colorControlActivated</item>
         <item name="android:colorControlHighlight">?attr/colorControlHighlight</item>
+        <item name="android:colorButtonNormal">?attr/colorButtonNormal</item>
     </style>
 
     <style name="Base.V21.Theme.AppCompat.Light" parent="Base.V7.Theme.AppCompat.Light">
@@ -112,6 +119,17 @@
         <item name="editTextColor">?android:attr/editTextColor</item>
         <item name="listChoiceBackgroundIndicator">?android:attr/listChoiceBackgroundIndicator</item>
 
+        <!-- Copy the platform default styles for the AppCompat widgets -->
+        <item name="autoCompleteTextViewStyle">?android:attr/autoCompleteTextViewStyle</item>
+        <item name="buttonStyle">?android:attr/buttonStyle</item>
+        <item name="buttonStyleSmall">?android:attr/buttonStyleSmall</item>
+        <item name="checkboxStyle">?android:attr/checkboxStyle</item>
+        <item name="checkedTextViewStyle">?android:attr/checkedTextViewStyle</item>
+        <item name="editTextStyle">?android:attr/editTextStyle</item>
+        <item name="radioButtonStyle">?android:attr/radioButtonStyle</item>
+        <item name="ratingBarStyle">?android:attr/ratingBarStyle</item>
+        <item name="spinnerStyle">?android:attr/spinnerStyle</item>
+
         <!-- Copy our color theme attributes to the framework -->
         <item name="android:colorPrimary">?attr/colorPrimary</item>
         <item name="android:colorPrimaryDark">?attr/colorPrimaryDark</item>
@@ -119,73 +137,21 @@
         <item name="android:colorControlNormal">?attr/colorControlNormal</item>
         <item name="android:colorControlActivated">?attr/colorControlActivated</item>
         <item name="android:colorControlHighlight">?attr/colorControlHighlight</item>
+        <item name="android:colorButtonNormal">?attr/colorButtonNormal</item>
     </style>
 
     <style name="Base.V21.Theme.AppCompat.Dialog" parent="Base.V11.Theme.AppCompat.Dialog">
-        <!-- Action Bar styling attributes -->
-        <item name="actionBarSize">?android:attr/actionBarSize</item>
-        <item name="actionBarDivider">?android:attr/actionBarDivider</item>
-        <item name="actionBarItemBackground">?android:attr/actionBarItemBackground</item>
-        <item name="actionButtonStyle">?android:attr/actionButtonStyle</item>
-        <item name="actionMenuTextColor">?android:attr/actionMenuTextColor</item>
-        <item name="actionMenuTextAppearance">?android:attr/actionMenuTextAppearance</item>
-        <item name="actionModeBackground">?android:attr/actionModeBackground</item>
-        <item name="actionModeCloseDrawable">?android:attr/actionModeCloseDrawable</item>
-        <item name="actionModeShareDrawable">?android:attr/actionModeShareDrawable</item>
-        <item name="actionOverflowButtonStyle">?android:attr/actionOverflowButtonStyle</item>
-        <item name="homeAsUpIndicator">?android:attr/homeAsUpIndicator</item>
-
-        <!-- For PopupMenu -->
-        <item name="listPreferredItemHeightSmall">?android:attr/listPreferredItemHeightSmall</item>
-        <item name="textAppearanceLargePopupMenu">?android:attr/textAppearanceLargePopupMenu</item>
-        <item name="textAppearanceSmallPopupMenu">?android:attr/textAppearanceSmallPopupMenu</item>
-
-        <!-- General view attributes -->
-        <item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
-        <item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackgroundBorderless</item>
-        <item name="dividerHorizontal">?android:attr/dividerHorizontal</item>
-        <item name="dividerVertical">?android:attr/dividerVertical</item>
-        <item name="editTextBackground">?android:attr/editTextBackground</item>
-        <item name="editTextColor">?android:attr/editTextColor</item>
-        <item name="listChoiceBackgroundIndicator">?android:attr/listChoiceBackgroundIndicator</item>
-
-        <!-- Copy our color theme attributes to the framework -->
-        <item name="android:colorPrimary">?attr/colorPrimary</item>
-        <item name="android:colorPrimaryDark">?attr/colorPrimaryDark</item>
-        <item name="android:colorAccent">?attr/colorAccent</item>
-        <item name="android:colorControlNormal">?attr/colorControlNormal</item>
-        <item name="android:colorControlActivated">?attr/colorControlActivated</item>
-        <item name="android:colorControlHighlight">?attr/colorControlHighlight</item>
+        <item name="android:windowElevation">@dimen/abc_floating_window_z</item>
     </style>
 
     <style name="Base.V21.Theme.AppCompat.Light.Dialog" parent="Base.V11.Theme.AppCompat.Light.Dialog">
-        <!-- Action Bar styling attributes -->
-        <item name="actionBarSize">?android:attr/actionBarSize</item>
-        <item name="actionBarDivider">?android:attr/actionBarDivider</item>
-        <item name="actionBarItemBackground">?android:attr/actionBarItemBackground</item>
-        <item name="actionButtonStyle">?android:attr/actionButtonStyle</item>
-        <item name="actionMenuTextColor">?android:attr/actionMenuTextColor</item>
-        <item name="actionMenuTextAppearance">?android:attr/actionMenuTextAppearance</item>
-        <item name="actionModeBackground">?android:attr/actionModeBackground</item>
-        <item name="actionModeCloseDrawable">?android:attr/actionModeCloseDrawable</item>
-        <item name="actionModeShareDrawable">?android:attr/actionModeShareDrawable</item>
-        <item name="actionOverflowButtonStyle">?android:attr/actionOverflowButtonStyle</item>
-        <item name="homeAsUpIndicator">?android:attr/homeAsUpIndicator</item>
+        <item name="android:windowElevation">@dimen/abc_floating_window_z</item>
+    </style>
 
-        <!-- For PopupMenu -->
-        <item name="listPreferredItemHeightSmall">?android:attr/listPreferredItemHeightSmall</item>
-        <item name="textAppearanceLargePopupMenu">?android:attr/textAppearanceLargePopupMenu</item>
-        <item name="textAppearanceSmallPopupMenu">?android:attr/textAppearanceSmallPopupMenu</item>
+    <style name="Base.Theme.AppCompat.Dialog" parent="Base.V21.Theme.AppCompat.Dialog" />
+    <style name="Base.Theme.AppCompat.Light.Dialog" parent="Base.V21.Theme.AppCompat.Light.Dialog" />
 
-        <!-- General view attributes -->
-        <item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
-        <item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackgroundBorderless</item>
-        <item name="dividerHorizontal">?android:attr/dividerHorizontal</item>
-        <item name="dividerVertical">?android:attr/dividerVertical</item>
-        <item name="editTextBackground">?android:attr/editTextBackground</item>
-        <item name="editTextColor">?android:attr/editTextColor</item>
-        <item name="listChoiceBackgroundIndicator">?android:attr/listChoiceBackgroundIndicator</item>
-
+    <style name="Platform.ThemeOverlay.AppCompat.Dark" parent="">
         <!-- Copy our color theme attributes to the framework -->
         <item name="android:colorPrimary">?attr/colorPrimary</item>
         <item name="android:colorPrimaryDark">?attr/colorPrimaryDark</item>
@@ -193,53 +159,18 @@
         <item name="android:colorControlNormal">?attr/colorControlNormal</item>
         <item name="android:colorControlActivated">?attr/colorControlActivated</item>
         <item name="android:colorControlHighlight">?attr/colorControlHighlight</item>
+        <item name="android:colorButtonNormal">?attr/colorButtonNormal</item>
     </style>
 
-    <style name="Base.Theme.AppCompat.Dialog" parent="Base.V21.Theme.AppCompat.Dialog" />
-
-    <style name="Base.Theme.AppCompat.Light.Dialog" parent="Base.V21.Theme.AppCompat.Light.Dialog" />
-
-    <style name="Base.ThemeOverlay.AppCompat" parent="android:ThemeOverlay.Material">
+    <style name="Platform.ThemeOverlay.AppCompat.Light" parent="">
+        <!-- Copy our color theme attributes to the framework -->
+        <item name="android:colorPrimary">?attr/colorPrimary</item>
+        <item name="android:colorPrimaryDark">?attr/colorPrimaryDark</item>
+        <item name="android:colorAccent">?attr/colorAccent</item>
         <item name="android:colorControlNormal">?attr/colorControlNormal</item>
+        <item name="android:colorControlActivated">?attr/colorControlActivated</item>
         <item name="android:colorControlHighlight">?attr/colorControlHighlight</item>
-    </style>
-
-    <style name="Base.ThemeOverlay.AppCompat.Dark" parent="android:ThemeOverlay.Material.Dark">
-        <item name="colorControlHighlight">@color/ripple_material_dark</item>
-
-        <item name="android:colorControlNormal">?attr/colorControlNormal</item>
-        <item name="android:colorControlHighlight">?attr/colorControlHighlight</item>
-
-        <!-- Used by MediaRouter -->
-        <item name="isLightTheme">false</item>
-    </style>
-
-    <style name="Base.ThemeOverlay.AppCompat.Light" parent="android:ThemeOverlay.Material.Light">
-        <item name="colorControlHighlight">@color/ripple_material_light</item>
-
-        <item name="android:colorControlNormal">?attr/colorControlNormal</item>
-        <item name="android:colorControlHighlight">?attr/colorControlHighlight</item>
-
-        <!-- Used by MediaRouter -->
-        <item name="isLightTheme">true</item>
-    </style>
-
-    <style name="Base.ThemeOverlay.AppCompat.ActionBar" parent="android:ThemeOverlay.Material.ActionBar">
-        <item name="colorControlNormal">?android:attr/textColorPrimary</item>
-
-        <item name="android:colorControlNormal">?attr/colorControlNormal</item>
-        <item name="android:colorControlHighlight">?attr/colorControlHighlight</item>
-    </style>
-
-    <style name="Base.ThemeOverlay.AppCompat.Dark.ActionBar" parent="android:ThemeOverlay.Material.Dark.ActionBar">
-        <item name="colorControlNormal">?android:attr/textColorPrimary</item>
-        <item name="colorControlHighlight">@color/ripple_material_dark</item>
-
-        <item name="android:colorControlNormal">?attr/colorControlNormal</item>
-        <item name="android:colorControlHighlight">?attr/colorControlHighlight</item>
-
-        <!-- Used by MediaRouter -->
-        <item name="isLightTheme">false</item>
+        <item name="android:colorButtonNormal">?attr/colorButtonNormal</item>
     </style>
 
 </resources>
diff --git a/v7/appcompat/res/values-vi/strings.xml b/v7/appcompat/res/values-vi/strings.xml
index 28b93ab..9cf34c2 100644
--- a/v7/appcompat/res/values-vi/strings.xml
+++ b/v7/appcompat/res/values-vi/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Tìm kiếm"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Tìm kiếm…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Tìm kiếm truy vấn"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Xóa truy vấn"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Gửi truy vấn"</string>
diff --git a/v7/appcompat/res/values-zh-rCN/strings.xml b/v7/appcompat/res/values-zh-rCN/strings.xml
index 519abd2..a0b492a 100644
--- a/v7/appcompat/res/values-zh-rCN/strings.xml
+++ b/v7/appcompat/res/values-zh-rCN/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s:%2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s - %2$s:%3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"搜索"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"搜索…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"搜索查询"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"清除查询"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"提交查询"</string>
diff --git a/v7/appcompat/res/values-zh-rHK/strings.xml b/v7/appcompat/res/values-zh-rHK/strings.xml
index 4974223..2e37307 100644
--- a/v7/appcompat/res/values-zh-rHK/strings.xml
+++ b/v7/appcompat/res/values-zh-rHK/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s:%2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s (%2$s):%3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"搜尋"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"搜尋…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"搜尋查詢"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"清除查詢"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"提交查詢"</string>
diff --git a/v7/appcompat/res/values-zh-rTW/strings.xml b/v7/appcompat/res/values-zh-rTW/strings.xml
index 97f2589..41a9401 100644
--- a/v7/appcompat/res/values-zh-rTW/strings.xml
+++ b/v7/appcompat/res/values-zh-rTW/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s:%2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s - %2$s:%3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"搜尋"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"搜尋…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"搜尋查詢"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"清除查詢"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"提交查詢"</string>
diff --git a/v7/appcompat/res/values-zu/strings.xml b/v7/appcompat/res/values-zu/strings.xml
index 21ee299..48d586b 100644
--- a/v7/appcompat/res/values-zu/strings.xml
+++ b/v7/appcompat/res/values-zu/strings.xml
@@ -24,6 +24,7 @@
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
     <string name="abc_searchview_description_search" msgid="8264924765203268293">"Sesha"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Iyasesha..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"Umbuzo wosesho"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Sula inkinga"</string>
     <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Hambisa umbuzo"</string>
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index 3fd90b2..5909e55 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -42,6 +42,9 @@
              in place of the usual title bar. -->
         <attr name="windowActionBar" format="boolean" />
 
+        <!-- Flag indicating whether there should be no title on this window. -->
+        <attr name="windowNoTitle" format="boolean" />
+
         <!-- Flag indicating whether this window's Action Bar should overlay
              application content. Does nothing if the window would not
              have an Action Bar. -->
@@ -69,6 +72,17 @@
              or a fraction of the screen size in that dimension. -->
         <attr name="windowFixedHeightMajor" format="dimension|fraction" />
 
+        <!-- The minimum width the window is allowed to be, along the major
+             axis of the screen.  That is, when in landscape.  Can be either
+             an absolute dimension or a fraction of the screen size in that
+             dimension. -->
+        <attr name="windowMinWidthMajor" format="dimension|fraction" />
+        <!-- The minimum width the window is allowed to be, along the minor
+             axis of the screen.  That is, when in portrait.  Can be either
+             an absolute dimension or a fraction of the screen size in that
+             dimension. -->
+        <attr name="windowMinWidthMinor" format="dimension|fraction" />
+
         <attr name="android:windowIsFloating" />
         <attr name="android:windowAnimationStyle" />
 
@@ -163,6 +177,18 @@
 
 
         <!-- =================== -->
+        <!-- Dialog styles -->
+        <!-- =================== -->
+        <eat-comment />
+
+        <!-- Theme to use for dialogs spawned from this theme. -->
+        <attr name="dialogTheme" format="reference" />
+        <!-- Preferred padding for dialog content. -->
+        <attr name="dialogPreferredPadding" format="dimension" />
+        <!-- The list divider used in alert dialogs. -->
+        <attr name="listDividerAlertDialog" format="reference" />
+
+        <!-- =================== -->
         <!-- Other widget styles -->
         <!-- =================== -->
         <eat-comment />
@@ -171,9 +197,6 @@
         <attr name="actionDropDownStyle" format="reference"/>
         <!-- The preferred item height for dropdown lists. -->
         <attr name="dropdownListPreferredItemHeight" format="dimension"/>
-
-        <!-- Default Spinner style. -->
-        <attr name="spinnerStyle" format="reference" />
         <!-- Default Spinner style. -->
         <attr name="spinnerDropDownItemStyle" format="reference" />
         <!-- Specifies a drawable to use for the 'home as up' indicator. -->
@@ -182,11 +205,9 @@
         <!-- Default action button style. -->
         <attr name="actionButtonStyle" format="reference"/>
 
-        <!-- A style that may be applied to horizontal LinearLayouts
-         to form a button bar. -->
+        <!-- Style for button bars -->
         <attr name="buttonBarStyle" format="reference"/>
-        <!-- A style that may be applied to Buttons placed within a
-             LinearLayout with the style buttonBarStyle to form a button bar. -->
+        <!-- Style for buttons within button bars -->
         <attr name="buttonBarButtonStyle" format="reference"/>
         <!-- A style that may be applied to buttons or other selectable items
              that should react to pressed and focus states, but that do not
@@ -216,9 +237,6 @@
         <!-- EditText background drawable. -->
         <attr name="editTextBackground" format="reference" />
 
-        <!-- Default style for the Switch widget. -->
-        <attr name="switchStyle" format="reference" />
-
         <!-- ============================ -->
         <!-- SearchView styles and assets -->
         <!-- ============================ -->
@@ -305,6 +323,54 @@
         <!-- The color applied to framework switch thumbs in their normal state. -->
         <attr name="colorSwitchThumbNormal" format="color" />
 
+        <!-- ============ -->
+        <!-- Alert Dialog styles -->
+        <!-- ============ -->
+        <eat-comment />
+        <attr name="alertDialogStyle" format="reference" />
+        <attr name="alertDialogButtonGroupStyle" format="reference" />
+        <attr name="alertDialogCenterButtons" format="boolean" />
+        <!-- Theme to use for alert dialogs spawned from this theme. -->
+        <attr name="alertDialogTheme" format="reference" />
+
+        <!-- Color of list item text in alert dialogs. -->
+        <attr name="textColorAlertDialogListItem" format="reference|color" />
+
+        <!-- Style for the "positive" buttons within button bars -->
+        <attr name="buttonBarPositiveButtonStyle" format="reference" />
+
+        <!-- Style for the "negative" buttons within button bars -->
+        <attr name="buttonBarNegativeButtonStyle" format="reference" />
+
+        <!-- Style for the "neutral" buttons within button bars -->
+        <attr name="buttonBarNeutralButtonStyle" format="reference" />
+
+        <!-- ===================== -->
+        <!-- Default widget styles -->
+        <!-- ===================== -->
+        <eat-comment />
+
+        <!-- Default AutoCompleteTextView style. -->
+        <attr name="autoCompleteTextViewStyle" format="reference" />
+        <!-- Normal Button style. -->
+        <attr name="buttonStyle" format="reference" />
+        <!-- Small Button style. -->
+        <attr name="buttonStyleSmall" format="reference" />
+        <!-- Default Checkbox style. -->
+        <attr name="checkboxStyle" format="reference" />
+        <!-- Default CheckedTextView style. -->
+        <attr name="checkedTextViewStyle" format="reference" />
+        <!-- Default EditText style. -->
+        <attr name="editTextStyle" format="reference" />
+        <!-- Default RadioButton style. -->
+        <attr name="radioButtonStyle" format="reference" />
+        <!-- Default RatingBar style. -->
+        <attr name="ratingBarStyle" format="reference" />
+        <!-- Default Spinner style. -->
+        <attr name="spinnerStyle" format="reference" />
+        <!-- Default style for the Switch widget. -->
+        <attr name="switchStyle" format="reference" />
+
     </declare-styleable>
 
 
@@ -432,6 +498,35 @@
              always request focus regardless of this view.  It only impacts where
              focus navigation will try to move focus. -->
         <attr name="android:focusable" />
+
+        <!-- Deprecated. -->
+        <attr name="theme" format="reference" />
+
+        <!-- Specifies a theme override for a view. When a theme override is set, the
+             view will be inflated using a {@link android.content.Context} themed with
+             the specified resource. -->
+        <attr name="android:theme" />
+
+        <!-- Tint to apply to the background. -->
+        <attr name="backgroundTint" format="color" />
+
+        <!-- Blending mode used to apply the background tint. -->
+        <attr name="backgroundTintMode">
+            <!-- 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" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="MenuView">
@@ -570,6 +665,29 @@
              for more info. -->
         <attr name="actionProviderClass" format="string" />
 
+        <!-- An optional tint for the item's icon -->
+        <attr name="iconTint" format="color" />
+
+        <!-- The blending mode used for tinting the item's icon -->
+        <attr name="iconTintMode">
+            <!-- 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). Only works on APIv 11+ -->
+            <enum name="add" value="16" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="Spinner">
@@ -627,6 +745,8 @@
         <attr name="goIcon" format="reference" />
         <!-- Search icon -->
         <attr name="searchIcon" format="reference" />
+        <!-- Search icon displayed as a text field hint -->
+        <attr name="searchHintIcon" format="reference" />
         <!-- Voice button icon -->
         <attr name="voiceIcon" format="reference" />
         <!-- Commit icon shown in the query suggestion row -->
@@ -653,9 +773,10 @@
         <attr name="expandActivityOverflowButtonDrawable" format="reference" />
     </declare-styleable>
 
-    <declare-styleable name="CompatTextView">
+    <declare-styleable name="AppCompatTextView">
         <!-- Present the text in ALL CAPS. This may use a small-caps form when available. -->
         <attr name="textAllCaps" format="reference|boolean" />
+        <attr name="android:textAppearance" />
     </declare-styleable>
 
     <declare-styleable name="LinearLayoutCompat">
@@ -719,12 +840,6 @@
         <attr name="contentInsetRight" />
         <attr name="maxButtonHeight" format="dimension" />
 
-        <!-- Specifies a theme override for a view. When a theme override is set, the
-             view will be inflated using a {@link android.content.Context} themed with
-             the specified resource. During XML inflation, any child views under the
-             view with a theme override will inherit the themed context. -->
-        <attr name="theme" format="reference" />
-        <!-- Icon drawable to use for the collapse button. -->
         <attr name="collapseIcon" format="reference" />
         <!-- Text to set as the content description for the collapse button. -->
         <attr name="collapseContentDescription" format="string" />
@@ -740,6 +855,52 @@
 
         <!-- Allows us to read in the minHeight attr pre-v16 -->
         <attr name="android:minHeight" />
+
+        <!-- Tint used for the overflow button -->
+        <attr name="overflowTint" format="color" />
+        <!-- The blending mode used for tinting the overflow button -->
+        <attr name="overflowTintMode">
+            <!-- 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). Only works on APIv 11+ -->
+            <enum name="add" value="16" />
+        </attr>
+
+        <!-- Tint used for the navigation button -->
+        <attr name="navigationTint" format="color" />
+        <!-- The blending mode used for tinting the navigation button -->
+        <attr name="navigationTintMode">
+            <!-- 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). Only works on APIv 11+ -->
+            <enum name="add" value="16" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="PopupWindowBackgroundState">
@@ -814,10 +975,22 @@
         <attr name="showText" format="boolean" />
     </declare-styleable>
 
-    <declare-styleable name="SwitchCompatTextAppearance">
+    <declare-styleable name="TextAppearance">
         <attr name="android:textSize" />
         <attr name="android:textColor" />
+        <attr name="android:textStyle" />
+        <attr name="android:typeface" />
         <attr name="textAllCaps" />
     </declare-styleable>
 
+    <!-- The set of attributes that describe a AlertDialog's theme. -->
+    <declare-styleable name="AlertDialog">
+        <attr name="android:layout" />
+        <attr name="buttonPanelSideLayout" format="reference" />
+        <attr name="listLayout" format="reference" />
+        <attr name="multiChoiceItemLayout" format="reference" />
+        <attr name="singleChoiceItemLayout" format="reference" />
+        <attr name="listItemLayout" format="reference" />
+    </declare-styleable>
+
 </resources>
diff --git a/v7/appcompat/res/values/colors_material.xml b/v7/appcompat/res/values/colors_material.xml
index 94448b5..6b3cca5 100644
--- a/v7/appcompat/res/values/colors_material.xml
+++ b/v7/appcompat/res/values/colors_material.xml
@@ -23,12 +23,12 @@
     <color name="background_floating_material_light">#ffeeeeee</color>
 
     <color name="primary_material_dark">#ff212121</color>
-    <color name="primary_material_light">#ffbdbdbd</color>
+    <color name="primary_material_light">#ffefefef</color>
     <color name="primary_dark_material_dark">#ff000000</color>
     <color name="primary_dark_material_light">#ff757575</color>
 
-    <color name="ripple_material_dark">#40ffffff</color>
-    <color name="ripple_material_light">#40000000</color>
+    <color name="ripple_material_dark">#4dffffff</color>
+    <color name="ripple_material_light">#1f000000</color>
 
     <color name="accent_material_light">@color/material_deep_teal_500</color>
     <color name="accent_material_dark">@color/material_deep_teal_200</color>
@@ -38,6 +38,8 @@
 
     <color name="switch_thumb_normal_material_dark">#ffbdbdbd</color>
     <color name="switch_thumb_normal_material_light">#fff1f1f1</color>
+    <color name="switch_thumb_disabled_material_dark">#ff616161</color>
+    <color name="switch_thumb_disabled_material_light">#ffbdbdbd</color>
 
     <color name="bright_foreground_material_dark">@android:color/white</color>
     <color name="bright_foreground_material_light">@android:color/black</color>
diff --git a/v7/appcompat/res/values/config.xml b/v7/appcompat/res/values/config.xml
index 0f8b7dc..be6a7a1 100644
--- a/v7/appcompat/res/values/config.xml
+++ b/v7/appcompat/res/values/config.xml
@@ -36,4 +36,6 @@
     <integer name="abc_config_activityShortDur">150</integer>
     <integer name="abc_config_activityDefaultDur">220</integer>
 
+    <bool name="abc_config_closeDialogWhenTouchOutside">true</bool>
+
 </resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values/dimens.xml b/v7/appcompat/res/values/dimens.xml
index 34a81a6..b8fa76c 100644
--- a/v7/appcompat/res/values/dimens.xml
+++ b/v7/appcompat/res/values/dimens.xml
@@ -71,4 +71,30 @@
     <!-- Default rounded corner for controls -->
     <dimen name="abc_control_corner_material">2dp</dimen>
 
+    <dimen name="abc_edit_text_inset_horizontal_material">4dp</dimen>
+    <dimen name="abc_edit_text_inset_top_material">10dp</dimen>
+    <dimen name="abc_edit_text_inset_bottom_material">7dp</dimen>
+
+    <!-- Since optical insets are not available pre-v18, we add a small amount of padding -->
+    <dimen name="abc_switch_padding">3dp</dimen>
+
+    <dimen name="abc_dialog_padding_material">24dp</dimen>
+    <dimen name="abc_dialog_padding_top_material">18dp</dimen>
+
+    <!-- Dialog button bar height -->
+    <dimen name="abc_alert_dialog_button_bar_height">48dp</dimen>
+
+    <!-- Padding above and below selection dialog lists. -->
+    <dimen name="abc_dialog_list_padding_vertical_material">8dp</dimen>
+
+    <!-- The platform's desired minimum size for a dialog's width when it
+         is along the major axis (that is the screen is landscape).  This may
+         be either a fraction or a dimension. -->
+    <item type="dimen" name="abc_dialog_min_width_major">65%</item>
+
+    <!-- The platform's desired minimum size for a dialog's width when it
+         is along the minor axis (that is the screen is portrait).  This may
+         be either a fraction or a dimension. -->
+    <item type="dimen" name="abc_dialog_min_width_minor">95%</item>
+
 </resources>
diff --git a/v7/appcompat/res/values/dimens_material.xml b/v7/appcompat/res/values/dimens_material.xml
index a620b31..6f5f1f8 100644
--- a/v7/appcompat/res/values/dimens_material.xml
+++ b/v7/appcompat/res/values/dimens_material.xml
@@ -20,6 +20,8 @@
     <dimen name="abc_action_bar_default_height_material">56dp</dimen>
     <!-- Default padding of an action bar. -->
     <dimen name="abc_action_bar_default_padding_material">4dp</dimen>
+    <!-- Default content inset of an action bar. -->
+    <dimen name="abc_action_bar_content_inset_material">16dp</dimen>
     <!-- Vertical padding around action bar icons. -->
     <dimen name="abc_action_bar_icon_vertical_padding_material">16dp</dimen>
     <!-- Top margin for action bar subtitles -->
@@ -27,6 +29,17 @@
     <!-- Bottom margin for action bar subtitles -->
     <dimen name="abc_action_bar_subtitle_bottom_margin_material">5dp</dimen>
 
+    <!-- Default padding for list items. This should match the action bar
+         content inset so that ListActivity items line up correctly. -->
+    <dimen name="abc_list_item_padding_horizontal_material">@dimen/abc_action_bar_content_inset_material</dimen>
+
+    <!-- Padding to add to the start of the overflow action button. -->
+    <dimen name="abc_action_bar_navigation_padding_start_material">0dp</dimen>
+    <!-- Padding to add to the start of the overflow action button. -->
+    <dimen name="abc_action_bar_overflow_padding_start_material">6dp</dimen>
+    <!-- Padding to add to the end of the overflow action button. -->
+    <dimen name="abc_action_bar_overflow_padding_end_material">10dp</dimen>
+
     <dimen name="abc_action_button_min_width_overflow_material">36dp</dimen>
     <dimen name="abc_action_button_min_width_material">48dp</dimen>
     <dimen name="abc_action_button_min_height_material">48dp</dimen>
@@ -50,4 +63,9 @@
     <dimen name="abc_text_size_medium_material">18sp</dimen>
     <dimen name="abc_text_size_small_material">14sp</dimen>
 
+    <dimen name="abc_floating_window_z">16dp</dimen>
+
+    <item name="abc_disabled_alpha_material_light" format="float" type="dimen">0.26</item>
+    <item name="abc_disabled_alpha_material_dark" format="float" type="dimen">0.30</item>
+
 </resources>
diff --git a/v7/appcompat/res/values/strings.xml b/v7/appcompat/res/values/strings.xml
index 5080070..87cfd29 100644
--- a/v7/appcompat/res/values/strings.xml
+++ b/v7/appcompat/res/values/strings.xml
@@ -41,6 +41,8 @@
 
     <!-- SearchView accessibility description for search button [CHAR LIMIT=NONE] -->
     <string name="abc_searchview_description_search">Search</string>
+    <!-- Default hint text for the system-wide search UI's text field. [CHAR LIMIT=30] -->
+    <string name="abc_search_hint">Search…</string>
     <!-- SearchView accessibility description for search text field [CHAR LIMIT=NONE] -->
     <string name="abc_searchview_description_query">Search query</string>
     <!-- SearchView accessibility description for clear button [CHAR LIMIT=NONE] -->
diff --git a/v7/appcompat/res/values/styles.xml b/v7/appcompat/res/values/styles.xml
index f80aae5..1f30314 100644
--- a/v7/appcompat/res/values/styles.xml
+++ b/v7/appcompat/res/values/styles.xml
@@ -60,8 +60,7 @@
 
     <style name="Widget.AppCompat.ActionButton" parent="Base.Widget.AppCompat.ActionButton" />
 
-    <!-- This style has an extra indirection to properly set RTL attributes. See styles_rtl.xml -->
-    <style name="Widget.AppCompat.ActionButton.CloseMode" parent="RtlOverlay.Widget.AppCompat.ActionButton.CloseMode" />
+    <style name="Widget.AppCompat.ActionButton.CloseMode" parent="Base.Widget.AppCompat.ActionButton.CloseMode" />
 
     <style name="Widget.AppCompat.ActionButton.Overflow"
            parent="RtlOverlay.Widget.AppCompat.ActionButton.Overflow" />
@@ -129,9 +128,8 @@
     <!-- This style has an extra indirection to properly set RTL attributes. See styles_rtl.xml -->
     <style name="Widget.AppCompat.DropDownItem.Spinner" parent="RtlOverlay.Widget.AppCompat.Search.DropDown.Text" />
 
-    <style name="Widget.AppCompat.ListView.DropDown"
-           parent="Base.Widget.AppCompat.ListView.DropDown">
-    </style>
+    <style name="Widget.AppCompat.ListView" parent="Base.Widget.AppCompat.ListView" />
+    <style name="Widget.AppCompat.ListView.DropDown" parent="Base.Widget.AppCompat.ListView.DropDown" />
 
     <style name="TextAppearance.Widget.AppCompat.ExpandedMenu.Item"
            parent="Base.TextAppearance.Widget.AppCompat.ExpandedMenu.Item">
@@ -194,6 +192,7 @@
     </style>
 
     <style name="Widget.AppCompat.SearchView" parent="Base.Widget.AppCompat.SearchView" />
+    <style name="Widget.AppCompat.SearchView.ActionBar" parent="Base.Widget.AppCompat.SearchView.ActionBar" />
 
     <style name="Widget.AppCompat.EditText"
            parent="Base.Widget.AppCompat.EditText">
@@ -201,20 +200,38 @@
 
     <style name="Widget.AppCompat.CompoundButton.Switch" parent="Base.Widget.AppCompat.CompoundButton.Switch" />
 
+    <style name="Widget.AppCompat.CompoundButton.CheckBox" parent="Base.Widget.AppCompat.CompoundButton.CheckBox" />
+
+    <style name="Widget.AppCompat.CompoundButton.RadioButton" parent="Base.Widget.AppCompat.CompoundButton.RadioButton" />
+
     <style name="Widget.AppCompat.RatingBar" parent="Base.Widget.AppCompat.RatingBar" />
 
     <style name="Widget.AppCompat.Button" parent="Base.Widget.AppCompat.Button" />
 
     <style name="Widget.AppCompat.Button.Small" parent="Base.Widget.AppCompat.Button.Small" />
 
+    <style name="Widget.AppCompat.Button.Borderless" parent="Base.Widget.AppCompat.Button.Borderless" />
+
+    <style name="Widget.AppCompat.Button.Borderless.Colored" parent="Base.Widget.AppCompat.Button.Borderless.Colored" />
+
+    <style name="Widget.AppCompat.Button.ButtonBar.AlertDialog" parent="Base.Widget.AppCompat.Button.ButtonBar.AlertDialog" />
+
+    <style name="Widget.AppCompat.ButtonBar" parent="Base.Widget.AppCompat.ButtonBar" />
+
+    <style name="Widget.AppCompat.ButtonBar.AlertDialog" parent="Base.Widget.AppCompat.ButtonBar.AlertDialog" />
+
     <style name="Widget.AppCompat.TextView.SpinnerItem" parent="Base.Widget.AppCompat.TextView.SpinnerItem" />
 
+    <style name="AlertDialog.AppCompat" parent="Base.AlertDialog.AppCompat" />
+
+    <style name="AlertDialog.AppCompat.Light" parent="Base.AlertDialog.AppCompat.Light" />
+
     <!-- Toolbar -->
 
     <style name="Widget.AppCompat.Toolbar" parent="Base.Widget.AppCompat.Toolbar" />
 
     <style name="Widget.AppCompat.Toolbar.Button.Navigation"
-           parent="Base.Widget.AppCompat.Toolbar.Button.Navigation" />
+           parent="RtlOverlay.Widget.AppCompat.Toolbar.Button.Navigation" />
 
     <style name="TextAppearance.Widget.AppCompat.Toolbar.Title"
            parent="Base.TextAppearance.Widget.AppCompat.Toolbar.Title">
@@ -225,8 +242,14 @@
     </style>
 
 
+    <!-- Animation styles -->
     <eat-comment />
+    <style name="Animation.AppCompat.Dialog" parent="Base.Animation.AppCompat.Dialog" />
+    <style name="Animation.AppCompat.DropDownUp" parent="Base.Animation.AppCompat.DropDownUp" />
+
+
     <!-- Text styles -->
+    <eat-comment />
 
     <style name="TextAppearance.AppCompat" parent="Base.TextAppearance.AppCompat" />
 
diff --git a/v7/appcompat/res/values/styles_base.xml b/v7/appcompat/res/values/styles_base.xml
index fb28f4e..4768fc8 100644
--- a/v7/appcompat/res/values/styles_base.xml
+++ b/v7/appcompat/res/values/styles_base.xml
@@ -37,6 +37,8 @@
         <item name="actionOverflowButtonStyle">@style/Widget.AppCompat.ActionButton.Overflow</item>
 
         <item name="android:gravity">center_vertical</item>
+        <item name="contentInsetStart">@dimen/abc_action_bar_content_inset_material</item>
+        <item name="contentInsetEnd">@dimen/abc_action_bar_content_inset_material</item>
         <item name="elevation">8dp</item>
         <item name="popupTheme">?attr/actionBarPopupTheme</item>
     </style>
@@ -72,6 +74,7 @@
 
     <style name="Base.Widget.AppCompat.ActionButton.CloseMode">
         <item name="android:background">?attr/selectableItemBackgroundBorderless</item>
+        <item name="android:minWidth">56dp</item>
     </style>
 
     <style name="Base.Widget.AppCompat.ActionButton.Overflow">
@@ -210,8 +213,12 @@
         <item name="android:gravity">center_vertical</item>
     </style>
 
-    <style name="Base.Widget.AppCompat.ListView.DropDown" parent="android:Widget.ListView">
+    <style name="Base.Widget.AppCompat.ListView" parent="android:Widget.ListView">
         <item name="android:listSelector">?attr/listChoiceBackgroundIndicator</item>
+        <item name="android:divider">?attr/dividerHorizontal</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.ListView.DropDown">
         <item name="android:divider">@null</item>
     </style>
 
@@ -331,12 +338,20 @@
         <item name="submitBackground">@drawable/abc_textfield_search_material</item>
         <item name="closeIcon">@drawable/abc_ic_clear_mtrl_alpha</item>
         <item name="searchIcon">@drawable/abc_ic_search_api_mtrl_alpha</item>
+        <item name="searchHintIcon">@drawable/abc_ic_search_api_mtrl_alpha</item>
         <item name="goIcon">@drawable/abc_ic_go_search_api_mtrl_alpha</item>
         <item name="voiceIcon">@drawable/abc_ic_voice_search_api_mtrl_alpha</item>
         <item name="commitIcon">@drawable/abc_ic_commit_search_api_mtrl_alpha</item>
         <item name="suggestionRowLayout">@layout/abc_search_dropdown_item_icons_2line</item>
     </style>
 
+    <style name="Base.Widget.AppCompat.SearchView.ActionBar">
+        <item name="queryBackground">@null</item>
+        <item name="submitBackground">@null</item>
+        <item name="searchHintIcon">@null</item>
+        <item name="queryHint">@string/abc_search_hint</item>
+    </style>
+
     <style name="Base.Widget.AppCompat.EditText" parent="android:Widget.EditText">
         <item name="android:background">?attr/editTextBackground</item>
         <item name="android:textColor">?attr/editTextColor</item>
@@ -358,12 +373,23 @@
         <item name="drawableSize">24dp</item>
     </style>
 
+    <style name="Base.Widget.AppCompat.CompoundButton.CheckBox" parent="android:Widget.CompoundButton.CheckBox">
+        <item name="android:button">?android:attr/listChoiceIndicatorMultiple</item>
+        <item name="android:background">?attr/selectableItemBackgroundBorderless</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.CompoundButton.RadioButton" parent="android:Widget.CompoundButton.RadioButton">
+        <item name="android:button">?android:attr/listChoiceIndicatorSingle</item>
+        <item name="android:background">?attr/selectableItemBackgroundBorderless</item>
+    </style>
+
     <style name="Base.Widget.AppCompat.CompoundButton.Switch" parent="android:Widget.CompoundButton">
         <item name="track">@drawable/abc_switch_track_mtrl_alpha</item>
         <item name="android:thumb">@drawable/abc_switch_thumb_material</item>
         <item name="switchTextAppearance">@style/TextAppearance.AppCompat.Widget.Switch</item>
         <item name="android:background">?attr/selectableItemBackgroundBorderless</item>
         <item name="showText">false</item>
+        <item name="android:padding">@dimen/abc_switch_padding</item>
     </style>
 
     <style name="Base.TextAppearance.AppCompat.Widget.Switch" parent="TextAppearance.AppCompat.Button" />
@@ -390,6 +416,22 @@
         <item name="android:minWidth">48dip</item>
     </style>
 
+    <!-- Borderless ink button -->
+    <style name="Base.Widget.AppCompat.Button.Borderless">
+        <item name="android:background">@drawable/abc_btn_borderless_material</item>
+    </style>
+
+    <!-- Colored borderless ink button -->
+    <style name="Base.Widget.AppCompat.Button.Borderless.Colored">
+        <item name="android:textColor">?attr/colorAccent</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.Button.ButtonBar.AlertDialog" parent="Widget.AppCompat.Button.Borderless.Colored">
+        <item name="android:minWidth">64dp</item>
+        <item name="android:maxLines">2</item>
+        <item name="android:minHeight">@dimen/abc_alert_dialog_button_bar_height</item>
+    </style>
+
     <style name="Base.Widget.AppCompat.TextView.SpinnerItem" parent="android:Widget.TextView.SpinnerItem">
         <item name="android:textAppearance">@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem</item>
         <item name="android:paddingLeft">8dp</item>
@@ -398,4 +440,45 @@
 
     <style name="Base.TextAppearance.AppCompat.Widget.TextView.SpinnerItem" parent="TextAppearance.AppCompat.Menu" />
 
+    <style name="Base.DialogWindowTitleBackground.AppCompat" parent="android:Widget">
+        <item name="android:background">@null</item>
+        <item name="android:paddingLeft">?attr/dialogPreferredPadding</item>
+        <item name="android:paddingRight">?attr/dialogPreferredPadding</item>
+        <item name="android:paddingTop">@dimen/abc_dialog_padding_top_material</item>
+    </style>
+
+    <style name="Base.DialogWindowTitle.AppCompat" parent="android:Widget">
+        <item name="android:maxLines">1</item>
+        <item name="android:scrollHorizontally">true</item>
+        <item name="android:textAppearance">@style/TextAppearance.AppCompat.Title</item>
+    </style>
+
+    <style name="Base.Animation.AppCompat.Dialog" parent="android:Animation">
+        <item name="android:windowEnterAnimation">@anim/abc_popup_enter</item>
+        <item name="android:windowExitAnimation">@anim/abc_popup_exit</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.ButtonBar" parent="android:Widget">
+        <item name="android:background">@null</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.ButtonBar.AlertDialog">
+        <item name="android:background">@null</item>
+    </style>
+
+    <style name="Base.Animation.AppCompat.DropDownUp" parent="android:Animation">
+        <item name="android:windowEnterAnimation">@anim/abc_grow_fade_in_from_bottom</item>
+        <item name="android:windowExitAnimation">@anim/abc_shrink_fade_out_from_bottom</item>
+    </style>
+
+    <style name="Base.AlertDialog.AppCompat" parent="android:Widget">
+        <item name="android:layout">@layout/abc_alert_dialog_material</item>
+        <item name="listLayout">@layout/abc_select_dialog_material</item>
+        <item name="listItemLayout">@layout/select_dialog_item_material</item>
+        <item name="multiChoiceItemLayout">@layout/select_dialog_multichoice_material</item>
+        <item name="singleChoiceItemLayout">@layout/select_dialog_singlechoice_material</item>
+    </style>
+
+    <style name="Base.AlertDialog.AppCompat.Light" parent="Base.AlertDialog.AppCompat" />
+
 </resources>
diff --git a/v7/appcompat/res/values/styles_rtl.xml b/v7/appcompat/res/values/styles_rtl.xml
index fad1291..5320800 100644
--- a/v7/appcompat/res/values/styles_rtl.xml
+++ b/v7/appcompat/res/values/styles_rtl.xml
@@ -47,14 +47,9 @@
         <item name="android:paddingRight">8dp</item>
     </style>
 
-    <style name="RtlOverlay.Widget.AppCompat.ActionButton.CloseMode" parent="Base.Widget.AppCompat.ActionButton.CloseMode">
-        <item name="android:paddingLeft">8dp</item>
-        <item name="android:layout_marginRight">16dp</item>
-    </style>
-
     <style name="RtlOverlay.Widget.AppCompat.ActionButton.Overflow" parent="Base.Widget.AppCompat.ActionButton.Overflow">
-        <item name="android:paddingLeft">0dp</item>
-        <item name="android:paddingRight">12dp</item>
+        <item name="android:paddingLeft">@dimen/abc_action_bar_overflow_padding_start_material</item>
+        <item name="android:paddingRight">@dimen/abc_action_bar_overflow_padding_end_material</item>
     </style>
 
     <style name="RtlOverlay.Widget.AppCompat.PopupMenuItem" parent="android:Widget">
@@ -69,4 +64,8 @@
         <item name="android:layout_alignParentLeft">true</item>
     </style>
 
+    <style name="RtlOverlay.Widget.AppCompat.Toolbar.Button.Navigation" parent="Base.Widget.AppCompat.Toolbar.Button.Navigation">
+        <item name="android:paddingLeft">@dimen/abc_action_bar_navigation_padding_start_material</item>
+    </style>
+
 </resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values/themes.xml b/v7/appcompat/res/values/themes.xml
index 3409c68..f8962df 100644
--- a/v7/appcompat/res/values/themes.xml
+++ b/v7/appcompat/res/values/themes.xml
@@ -36,12 +36,12 @@
 
     <style name="Theme.AppCompat.NoActionBar">
         <item name="windowActionBar">false</item>
-        <item name="android:windowNoTitle">true</item>
+        <item name="windowNoTitle">true</item>
     </style>
 
     <style name="Theme.AppCompat.Light.NoActionBar">
         <item name="windowActionBar">false</item>
-        <item name="android:windowNoTitle">true</item>
+        <item name="windowNoTitle">true</item>
     </style>
 
     <style name="Theme.AppCompat.DialogWhenLarge"
@@ -56,9 +56,22 @@
 
     <style name="Theme.AppCompat.Light.Dialog" parent="Base.Theme.AppCompat.Light.Dialog" />
 
+
+    <!-- Material theme for alert dialog windows, which is used by the AlertDialog class.
+         This is basically a dialog but sets the background to empty so it can do
+         two-tone backgrounds. For applications targeting Honeycomb or newer, this is the default
+         AlertDialog theme. -->
+    <style name="Theme.AppCompat.Dialog.Alert" parent="Base.Theme.AppCompat.Dialog.Alert" />
+    <style name="Theme.AppCompat.Light.Dialog.Alert" parent="Base.Theme.AppCompat.Light.Dialog.Alert" />
+
+    <!-- Variant of Theme.AppCompat.Dialog that has a nice minimum width for
+         a regular dialog. -->
+    <style name="Theme.AppCompat.Dialog.MinWidth" parent="Base.Theme.AppCompat.Dialog.MinWidth" />
+    <style name="Theme.AppCompat.Light.Dialog.MinWidth" parent="Base.Theme.AppCompat.Light.Dialog.MinWidth" />
+
     <!-- Menu/item attributes -->
     <style name="Theme.AppCompat.CompactMenu" parent="Base.Theme.AppCompat.CompactMenu" />
-    <style name="Animation.AppCompat.DropDownUp" parent="Base.Animation.AppCompat.DropDownUp" />
+
 
     <style name="ThemeOverlay.AppCompat" parent="Base.ThemeOverlay.AppCompat" />
 
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index c151d3b..2ea8eff 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -25,15 +25,12 @@
     <style name="Platform.AppCompat" parent="android:Theme">
         <item name="android:windowNoTitle">true</item>
 
-        <item name="buttonBarStyle">@android:style/ButtonBar</item>
-        <item name="buttonBarButtonStyle">@android:style/Widget.Button</item>
-
         <!-- Window colors -->
         <item name="android:colorForeground">@color/bright_foreground_material_dark</item>
         <item name="android:colorForegroundInverse">@color/bright_foreground_material_light</item>
         <item name="android:colorBackground">@color/background_material_dark</item>
         <item name="android:colorBackgroundCacheHint">@color/abc_background_cache_hint_selector_material_dark</item>
-        <item name="android:disabledAlpha">0.5</item>
+        <item name="android:disabledAlpha">@dimen/abc_disabled_alpha_material_dark</item>
         <item name="android:backgroundDimAmount">0.6</item>
         <item name="android:windowBackground">@color/background_material_dark</item>
 
@@ -60,10 +57,6 @@
         <item name="android:textAppearanceSmall">@style/TextAppearance.AppCompat.Small</item>
         <item name="android:textAppearanceSmallInverse">@style/TextAppearance.AppCompat.Small.Inverse</item>
 
-        <item name="android:spinnerStyle">@style/Widget.AppCompat.Spinner</item>
-        <item name="android:spinnerItemStyle">@style/Widget.AppCompat.TextView.SpinnerItem</item>
-        <item name="android:dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
-
         <item name="android:listChoiceIndicatorSingle">@drawable/abc_btn_radio_material</item>
         <item name="android:listChoiceIndicatorMultiple">@drawable/abc_btn_check_material</item>
     </style>
@@ -71,15 +64,12 @@
     <style name="Platform.AppCompat.Light" parent="android:Theme.Light">
         <item name="android:windowNoTitle">true</item>
 
-        <item name="buttonBarStyle">@android:style/ButtonBar</item>
-        <item name="buttonBarButtonStyle">@android:style/Widget.Button</item>
-
         <!-- Window colors -->
         <item name="android:colorForeground">@color/bright_foreground_material_light</item>
         <item name="android:colorForegroundInverse">@color/bright_foreground_material_dark</item>
         <item name="android:colorBackground">@color/background_material_light</item>
         <item name="android:colorBackgroundCacheHint">@color/abc_background_cache_hint_selector_material_light</item>
-        <item name="android:disabledAlpha">0.5</item>
+        <item name="android:disabledAlpha">@dimen/abc_disabled_alpha_material_light</item>
         <item name="android:backgroundDimAmount">0.6</item>
         <item name="android:windowBackground">@color/background_material_light</item>
 
@@ -107,62 +97,10 @@
         <item name="android:textAppearanceSmall">@style/TextAppearance.AppCompat.Small</item>
         <item name="android:textAppearanceSmallInverse">@style/TextAppearance.AppCompat.Small.Inverse</item>
 
-        <item name="android:spinnerStyle">@style/Widget.AppCompat.Spinner</item>
-        <item name="android:spinnerItemStyle">@style/Widget.AppCompat.TextView.SpinnerItem</item>
-        <item name="android:dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
-
         <item name="android:listChoiceIndicatorSingle">@drawable/abc_btn_radio_material</item>
         <item name="android:listChoiceIndicatorMultiple">@drawable/abc_btn_check_material</item>
     </style>
 
-    <style name="Platform.AppCompat.Dialog" parent="android:Theme.Dialog">
-        <item name="android:windowNoTitle">true</item>
-
-        <item name="buttonBarStyle">@android:style/ButtonBar</item>
-        <item name="buttonBarButtonStyle">@android:style/Widget.Button</item>
-
-        <!-- Window colors -->
-        <item name="android:colorForeground">@color/bright_foreground_material_dark</item>
-        <item name="android:colorForegroundInverse">@color/bright_foreground_material_light</item>
-        <item name="android:colorBackground">@color/background_material_dark</item>
-        <item name="android:colorBackgroundCacheHint">@color/abc_background_cache_hint_selector_material_dark</item>
-        <item name="android:disabledAlpha">0.5</item>
-        <item name="android:backgroundDimAmount">0.6</item>
-        <item name="android:windowBackground">@color/background_material_dark</item>
-
-        <!-- Text colors -->
-        <item name="android:textColorPrimary">@color/abc_primary_text_material_dark</item>
-        <item name="android:textColorPrimaryInverse">@color/abc_primary_text_material_light</item>
-        <item name="android:textColorPrimaryDisableOnly">@color/abc_primary_text_disable_only_material_dark</item>
-        <item name="android:textColorSecondary">@color/abc_secondary_text_material_dark</item>
-        <item name="android:textColorSecondaryInverse">@color/abc_secondary_text_material_light</item>
-        <item name="android:textColorTertiary">@color/abc_secondary_text_material_dark</item>
-        <item name="android:textColorTertiaryInverse">@color/abc_secondary_text_material_light</item>
-        <item name="android:textColorHint">@color/hint_foreground_material_dark</item>
-        <item name="android:textColorHintInverse">@color/hint_foreground_material_light</item>
-        <item name="android:textColorHighlight">@color/highlighted_text_material_dark</item>
-        <item name="android:textColorLink">@color/link_text_material_dark</item>
-
-        <!-- Text styles -->
-        <item name="android:textAppearance">@style/TextAppearance.AppCompat</item>
-        <item name="android:textAppearanceInverse">@style/TextAppearance.AppCompat.Inverse</item>
-        <item name="android:textAppearanceLarge">@style/TextAppearance.AppCompat.Large</item>
-        <item name="android:textAppearanceLargeInverse">@style/TextAppearance.AppCompat.Large.Inverse</item>
-        <item name="android:textAppearanceMedium">@style/TextAppearance.AppCompat.Medium</item>
-        <item name="android:textAppearanceMediumInverse">@style/TextAppearance.AppCompat.Medium.Inverse</item>
-        <item name="android:textAppearanceSmall">@style/TextAppearance.AppCompat.Small</item>
-        <item name="android:textAppearanceSmallInverse">@style/TextAppearance.AppCompat.Small.Inverse</item>
-
-        <item name="android:spinnerStyle">@style/Widget.AppCompat.Spinner</item>
-        <item name="android:spinnerItemStyle">@style/Widget.AppCompat.TextView.SpinnerItem</item>
-        <item name="android:dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
-
-        <item name="android:listChoiceIndicatorSingle">@drawable/abc_btn_radio_material</item>
-        <item name="android:listChoiceIndicatorMultiple">@drawable/abc_btn_check_material</item>
-    </style>
-
-    <style name="Platform.AppCompat.Light.Dialog" parent="Platform.AppCompat.Dialog" />
-
     <!-- Themes in the "Base.Theme" family vary based on the current platform
          version to provide the correct basis on each device. You probably don't
          want to use them directly in your apps.
@@ -232,8 +170,13 @@
         <item name="listPreferredItemHeight">64dp</item>
         <item name="listPreferredItemHeightSmall">48dp</item>
         <item name="listPreferredItemHeightLarge">80dp</item>
-        <item name="listPreferredItemPaddingLeft">16dip</item>
-        <item name="listPreferredItemPaddingRight">16dip</item>
+        <item name="listPreferredItemPaddingLeft">@dimen/abc_list_item_padding_horizontal_material</item>
+        <item name="listPreferredItemPaddingRight">@dimen/abc_list_item_padding_horizontal_material</item>
+
+        <!-- Spinner styles -->
+        <item name="spinnerStyle">@style/Widget.AppCompat.Spinner</item>
+        <item name="android:spinnerItemStyle">@style/Widget.AppCompat.TextView.SpinnerItem</item>
+        <item name="android:dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
 
         <!-- Required for use of support_simple_spinner_dropdown_item.xml -->
         <item name="spinnerDropDownItemStyle">@style/Widget.AppCompat.DropDownItem.Spinner</item>
@@ -260,10 +203,10 @@
         <item name="toolbarStyle">@style/Widget.AppCompat.Toolbar</item>
         <item name="toolbarNavigationButtonStyle">@style/Widget.AppCompat.Toolbar.Button.Navigation</item>
 
-        <item name="android:editTextStyle">@style/Widget.AppCompat.EditText</item>
+        <item name="editTextStyle">@style/Widget.AppCompat.EditText</item>
         <item name="editTextBackground">@drawable/abc_edit_text_material</item>
         <item name="editTextColor">?android:attr/textColorPrimary</item>
-        <item name="android:autoCompleteTextViewStyle">@style/Widget.AppCompat.AutoCompleteTextView</item>
+        <item name="autoCompleteTextViewStyle">@style/Widget.AppCompat.AutoCompleteTextView</item>
 
         <!-- Color palette -->
         <item name="colorPrimaryDark">@color/primary_dark_material_dark</item>
@@ -274,18 +217,43 @@
         <item name="colorControlActivated">?attr/colorAccent</item>
         <item name="colorControlHighlight">@color/ripple_material_dark</item>
         <item name="colorButtonNormal">@color/button_material_dark</item>
-        <item name="colorSwitchThumbNormal">@color/switch_thumb_normal_material_dark</item>
+        <item name="colorSwitchThumbNormal">@color/switch_thumb_material_dark</item>
 
         <item name="drawerArrowStyle">@style/Widget.AppCompat.DrawerArrowToggle</item>
 
+        <item name="checkboxStyle">@style/Widget.AppCompat.CompoundButton.CheckBox</item>
+        <item name="radioButtonStyle">@style/Widget.AppCompat.CompoundButton.RadioButton</item>
         <item name="switchStyle">@style/Widget.AppCompat.CompoundButton.Switch</item>
 
-        <item name="android:ratingBarStyle">@style/Widget.AppCompat.RatingBar</item>
+        <item name="ratingBarStyle">@style/Widget.AppCompat.RatingBar</item>
 
         <!-- Button styles -->
-        <item name="android:buttonStyle">@style/Widget.AppCompat.Button</item>
-        <item name="android:buttonStyleSmall">@style/Widget.AppCompat.Button.Small</item>
+        <item name="buttonStyle">@style/Widget.AppCompat.Button</item>
+        <item name="buttonStyleSmall">@style/Widget.AppCompat.Button.Small</item>
         <item name="android:textAppearanceButton">@style/TextAppearance.AppCompat.Button</item>
+
+        <item name="buttonBarStyle">@style/Widget.AppCompat.ButtonBar</item>
+        <item name="buttonBarButtonStyle">@style/Widget.AppCompat.Button.ButtonBar.AlertDialog</item>
+        <item name="buttonBarPositiveButtonStyle">?attr/buttonBarButtonStyle</item>
+        <item name="buttonBarNegativeButtonStyle">?attr/buttonBarButtonStyle</item>
+        <item name="buttonBarNeutralButtonStyle">?attr/buttonBarButtonStyle</item>
+
+        <!-- Dialog attributes -->
+        <item name="dialogTheme">@style/Theme.AppCompat.Dialog</item>
+        <item name="dialogPreferredPadding">@dimen/abc_dialog_padding_material</item>
+
+        <item name="alertDialogTheme">@style/Theme.AppCompat.Dialog.Alert</item>
+        <item name="alertDialogStyle">@style/AlertDialog.AppCompat</item>
+        <item name="alertDialogCenterButtons">false</item>
+        <item name="textColorAlertDialogListItem">@color/abc_primary_text_material_dark</item>
+        <item name="listDividerAlertDialog">@null</item>
+
+        <!-- Define these here; ContextThemeWrappers around themes that define them should
+             always clear these values. -->
+        <item name="windowFixedWidthMajor">0dp</item>
+        <item name="windowFixedWidthMinor">0dp</item>
+        <item name="windowFixedHeightMajor">0dp</item>
+        <item name="windowFixedHeightMinor">0dp</item>
     </style>
 
     <!-- Base platform-dependent theme providing an action bar in a light-themed activity. -->
@@ -349,8 +317,13 @@
         <item name="listPreferredItemHeight">64dp</item>
         <item name="listPreferredItemHeightSmall">48dp</item>
         <item name="listPreferredItemHeightLarge">80dp</item>
-        <item name="listPreferredItemPaddingLeft">16dip</item>
-        <item name="listPreferredItemPaddingRight">16dip</item>
+        <item name="listPreferredItemPaddingLeft">@dimen/abc_list_item_padding_horizontal_material</item>
+        <item name="listPreferredItemPaddingRight">@dimen/abc_list_item_padding_horizontal_material</item>
+
+        <!-- Spinner styles -->
+        <item name="spinnerStyle">@style/Widget.AppCompat.Spinner</item>
+        <item name="android:spinnerItemStyle">@style/Widget.AppCompat.TextView.SpinnerItem</item>
+        <item name="android:dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
 
         <!-- Required for use of support_simple_spinner_dropdown_item.xml -->
         <item name="spinnerDropDownItemStyle">@style/Widget.AppCompat.DropDownItem.Spinner</item>
@@ -377,10 +350,10 @@
         <item name="toolbarStyle">@style/Widget.AppCompat.Toolbar</item>
         <item name="toolbarNavigationButtonStyle">@style/Widget.AppCompat.Toolbar.Button.Navigation</item>
 
-        <item name="android:editTextStyle">@style/Widget.AppCompat.EditText</item>
+        <item name="editTextStyle">@style/Widget.AppCompat.EditText</item>
         <item name="editTextBackground">@drawable/abc_edit_text_material</item>
         <item name="editTextColor">?android:attr/textColorPrimary</item>
-        <item name="android:autoCompleteTextViewStyle">@style/Widget.AppCompat.AutoCompleteTextView</item>
+        <item name="autoCompleteTextViewStyle">@style/Widget.AppCompat.AutoCompleteTextView</item>
 
         <!-- Color palette -->
         <item name="colorPrimaryDark">@color/primary_dark_material_light</item>
@@ -391,18 +364,43 @@
         <item name="colorControlActivated">?attr/colorAccent</item>
         <item name="colorControlHighlight">@color/ripple_material_light</item>
         <item name="colorButtonNormal">@color/button_material_light</item>
-        <item name="colorSwitchThumbNormal">@color/switch_thumb_normal_material_light</item>
+        <item name="colorSwitchThumbNormal">@color/switch_thumb_material_light</item>
 
         <item name="drawerArrowStyle">@style/Widget.AppCompat.DrawerArrowToggle</item>
 
+        <item name="checkboxStyle">@style/Widget.AppCompat.CompoundButton.CheckBox</item>
+        <item name="radioButtonStyle">@style/Widget.AppCompat.CompoundButton.RadioButton</item>
         <item name="switchStyle">@style/Widget.AppCompat.CompoundButton.Switch</item>
 
-        <item name="android:ratingBarStyle">@style/Widget.AppCompat.RatingBar</item>
+        <item name="ratingBarStyle">@style/Widget.AppCompat.RatingBar</item>
 
         <!-- Button styles -->
-        <item name="android:buttonStyle">@style/Widget.AppCompat.Button</item>
-        <item name="android:buttonStyleSmall">@style/Widget.AppCompat.Button.Small</item>
+        <item name="buttonStyle">@style/Widget.AppCompat.Button</item>
+        <item name="buttonStyleSmall">@style/Widget.AppCompat.Button.Small</item>
         <item name="android:textAppearanceButton">@style/TextAppearance.AppCompat.Button</item>
+
+        <item name="buttonBarStyle">@style/Widget.AppCompat.ButtonBar</item>
+        <item name="buttonBarButtonStyle">@style/Widget.AppCompat.Button.ButtonBar.AlertDialog</item>
+        <item name="buttonBarPositiveButtonStyle">?attr/buttonBarButtonStyle</item>
+        <item name="buttonBarNegativeButtonStyle">?attr/buttonBarButtonStyle</item>
+        <item name="buttonBarNeutralButtonStyle">?attr/buttonBarButtonStyle</item>
+
+        <!-- Dialog attributes -->
+        <item name="dialogTheme">@style/Theme.AppCompat.Light.Dialog</item>
+        <item name="dialogPreferredPadding">@dimen/abc_dialog_padding_material</item>
+
+        <item name="alertDialogTheme">@style/Theme.AppCompat.Light.Dialog.Alert</item>
+        <item name="alertDialogStyle">@style/AlertDialog.AppCompat.Light</item>
+        <item name="alertDialogCenterButtons">false</item>
+        <item name="textColorAlertDialogListItem">@color/abc_primary_text_material_light</item>
+        <item name="listDividerAlertDialog">@null</item>
+
+        <!-- Define these here; ContextThemeWrappers around themes that define them should
+             always clear these values. -->
+        <item name="windowFixedWidthMajor">0dp</item>
+        <item name="windowFixedWidthMinor">0dp</item>
+        <item name="windowFixedHeightMajor">0dp</item>
+        <item name="windowFixedHeightMinor">0dp</item>
     </style>
 
     <style name="Base.Theme.AppCompat" parent="Base.V7.Theme.AppCompat">
@@ -430,126 +428,72 @@
         <item name="android:windowAnimationStyle">@style/Animation.AppCompat.DropDownUp</item>
     </style>
 
-    <style name="Base.Animation.AppCompat.DropDownUp" parent="android:Animation">
-        <item name="android:windowEnterAnimation">@anim/abc_grow_fade_in_from_bottom</item>
-        <item name="android:windowExitAnimation">@anim/abc_shrink_fade_out_from_bottom</item>
+    <style name="Base.V7.Theme.AppCompat.Dialog" parent="Base.Theme.AppCompat">
+        <item name="android:colorBackground">@color/background_floating_material_dark</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowTitleStyle">@style/Base.DialogWindowTitle.AppCompat</item>
+        <item name="android:windowTitleBackgroundStyle">@style/Base.DialogWindowTitleBackground.AppCompat</item>
+        <item name="android:windowBackground">@drawable/abc_dialog_material_background_dark</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowAnimationStyle">@style/Animation.AppCompat.Dialog</item>
+        <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
+
+        <item name="windowActionBar">false</item>
+        <item name="windowActionModeOverlay">true</item>
+
+        <item name="listPreferredItemPaddingLeft">24dip</item>
+        <item name="listPreferredItemPaddingRight">24dip</item>
+
+        <item name="android:listDivider">@null</item>
     </style>
 
-    <style name="Base.V7.Theme.AppCompat.Dialog" parent="Platform.AppCompat.Dialog">
-        <item name="windowActionBar">true</item>
-        <item name="windowActionBarOverlay">false</item>
-        <item name="isLightTheme">false</item>
+    <style name="Base.V7.Theme.AppCompat.Light.Dialog" parent="Base.Theme.AppCompat.Light">
+        <item name="android:colorBackground">@color/background_floating_material_light</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
 
-        <item name="selectableItemBackground">@drawable/abc_item_background_holo_dark</item>
-        <item name="selectableItemBackgroundBorderless">?attr/selectableItemBackground</item>
-        <item name="homeAsUpIndicator">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowTitleStyle">@style/Base.DialogWindowTitle.AppCompat</item>
+        <item name="android:windowTitleBackgroundStyle">@style/Base.DialogWindowTitleBackground.AppCompat</item>
+        <item name="android:windowBackground">@drawable/abc_dialog_material_background_light</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowAnimationStyle">@style/Animation.AppCompat.Dialog</item>
+        <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
 
-        <item name="dividerVertical">@drawable/abc_list_divider_mtrl_alpha</item>
-        <item name="dividerHorizontal">@drawable/abc_list_divider_mtrl_alpha</item>
+        <item name="windowActionBar">false</item>
+        <item name="windowActionModeOverlay">true</item>
 
-        <!-- Action Bar Styles -->
-        <item name="actionBarTabStyle">@style/Widget.AppCompat.ActionBar.TabView</item>
-        <item name="actionBarTabBarStyle">@style/Widget.AppCompat.ActionBar.TabBar</item>
-        <item name="actionBarTabTextStyle">@style/Widget.AppCompat.ActionBar.TabText</item>
-        <item name="actionButtonStyle">@style/Widget.AppCompat.ActionButton</item>
-        <item name="actionOverflowButtonStyle">@style/Widget.AppCompat.ActionButton.Overflow</item>
-        <item name="actionOverflowMenuStyle">@style/Widget.AppCompat.PopupMenu.Overflow</item>
-        <item name="actionBarStyle">@style/Widget.AppCompat.ActionBar.Solid</item>
-        <item name="actionBarSplitStyle">?attr/actionBarStyle</item>
-        <item name="actionBarWidgetTheme">@null</item>
-        <item name="actionBarTheme">@style/ThemeOverlay.AppCompat.ActionBar</item>
-        <item name="actionBarSize">@dimen/abc_action_bar_default_height_material</item>
-        <item name="actionBarDivider">?attr/dividerVertical</item>
-        <item name="actionBarItemBackground">?attr/selectableItemBackground</item>
-        <item name="actionMenuTextAppearance">@style/TextAppearance.AppCompat.Widget.ActionBar.Menu</item>
-        <item name="actionMenuTextColor">?android:attr/textColorPrimaryDisableOnly</item>
+        <item name="listPreferredItemPaddingLeft">24dip</item>
+        <item name="listPreferredItemPaddingRight">24dip</item>
 
-        <!-- Dropdown Spinner Attributes -->
-        <item name="actionDropDownStyle">@style/Widget.AppCompat.Spinner.DropDown.ActionBar</item>
-
-        <!-- Action Mode -->
-        <item name="actionModeStyle">@style/Widget.AppCompat.ActionMode</item>
-        <item name="actionModeBackground">@drawable/abc_cab_background_top_material</item>
-        <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item>
-        <item name="actionModeCloseDrawable">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
-        <item name="actionModeCloseButtonStyle">@style/Widget.AppCompat.ActionButton.CloseMode</item>
-
-        <item name="actionModeCutDrawable">@drawable/abc_ic_menu_cut_mtrl_alpha</item>
-        <item name="actionModeCopyDrawable">@drawable/abc_ic_menu_copy_mtrl_am_alpha</item>
-        <item name="actionModePasteDrawable">@drawable/abc_ic_menu_paste_mtrl_am_alpha</item>
-        <item name="actionModeSelectAllDrawable">@drawable/abc_ic_menu_selectall_mtrl_alpha</item>
-        <item name="actionModeShareDrawable">@drawable/abc_ic_menu_share_mtrl_alpha</item>
-
-        <!-- Panel attributes -->
-        <item name="panelMenuListWidth">@dimen/abc_panel_menu_list_width</item>
-        <item name="panelMenuListTheme">@style/Theme.AppCompat.CompactMenu</item>
-        <item name="panelBackground">@drawable/abc_menu_hardkey_panel_mtrl_mult</item>
-        <item name="android:panelBackground">@android:color/transparent</item>
-        <item name="listChoiceBackgroundIndicator">@drawable/abc_list_selector_holo_dark</item>
-
-        <!-- List attributes -->
-        <item name="textAppearanceListItem">@style/TextAppearance.AppCompat.Subhead</item>
-        <item name="textAppearanceListItemSmall">@style/TextAppearance.AppCompat.Subhead</item>
-        <item name="listPreferredItemHeight">64dp</item>
-        <item name="listPreferredItemHeightSmall">48dp</item>
-        <item name="listPreferredItemHeightLarge">80dp</item>
-        <item name="listPreferredItemPaddingLeft">16dip</item>
-        <item name="listPreferredItemPaddingRight">16dip</item>
-
-        <!-- Required for use of support_simple_spinner_dropdown_item.xml -->
-        <item name="spinnerDropDownItemStyle">@style/Widget.AppCompat.DropDownItem.Spinner</item>
-        <item name="dropdownListPreferredItemHeight">?attr/listPreferredItemHeightSmall</item>
-
-        <!-- Popup Menu styles -->
-        <item name="popupMenuStyle">@style/Widget.AppCompat.PopupMenu</item>
-        <item name="textAppearanceLargePopupMenu">@style/TextAppearance.AppCompat.Widget.PopupMenu.Large</item>
-        <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.AppCompat.Widget.PopupMenu.Small</item>
-        <item name="listPopupWindowStyle">@style/Widget.AppCompat.ListPopupWindow</item>
-        <item name="dropDownListViewStyle">@style/Widget.AppCompat.ListView.DropDown</item>
-
-        <!-- SearchView attributes -->
-        <item name="searchViewStyle">@style/Widget.AppCompat.SearchView</item>
-        <item name="android:dropDownItemStyle">@style/Widget.AppCompat.DropDownItem.Spinner</item>
-        <item name="textColorSearchUrl">@color/abc_search_url_text</item>
-        <item name="textAppearanceSearchResultTitle">@style/TextAppearance.AppCompat.SearchResult.Title</item>
-        <item name="textAppearanceSearchResultSubtitle">@style/TextAppearance.AppCompat.SearchResult.Subtitle</item>
-
-        <!-- ShareActionProvider attributes -->
-        <item name="activityChooserViewStyle">@style/Widget.AppCompat.ActivityChooserView</item>
-
-        <!-- Toolbar styles -->
-        <item name="toolbarStyle">@style/Widget.AppCompat.Toolbar</item>
-        <item name="toolbarNavigationButtonStyle">@style/Widget.AppCompat.Toolbar.Button.Navigation</item>
-
-        <item name="android:editTextStyle">@style/Widget.AppCompat.EditText</item>
-        <item name="editTextBackground">@drawable/abc_edit_text_material</item>
-        <item name="editTextColor">?android:attr/textColorPrimary</item>
-        <item name="android:autoCompleteTextViewStyle">@style/Widget.AppCompat.AutoCompleteTextView</item>
-
-        <!-- Color palette -->
-        <item name="colorPrimaryDark">@color/primary_dark_material_dark</item>
-        <item name="colorPrimary">@color/primary_material_dark</item>
-        <item name="colorAccent">@color/accent_material_dark</item>
-
-        <item name="colorControlNormal">?android:attr/textColorSecondary</item>
-        <item name="colorControlActivated">?attr/colorAccent</item>
-        <item name="colorControlHighlight">@color/ripple_material_dark</item>
-        <item name="colorButtonNormal">@color/button_material_dark</item>
-        <item name="colorSwitchThumbNormal">@color/switch_thumb_normal_material_dark</item>
-
-        <item name="switchStyle">@style/Widget.AppCompat.CompoundButton.Switch</item>
-
-        <item name="android:ratingBarStyle">@style/Widget.AppCompat.RatingBar</item>
-
-        <!-- Button styles -->
-        <item name="android:buttonStyle">@style/Widget.AppCompat.Button</item>
-        <item name="android:buttonStyleSmall">@style/Widget.AppCompat.Button.Small</item>
-        <item name="android:textAppearanceButton">@style/TextAppearance.AppCompat.Button</item>
+        <item name="android:listDivider">@null</item>
     </style>
 
     <style name="Base.Theme.AppCompat.Dialog" parent="Base.V7.Theme.AppCompat.Dialog" />
+    <style name="Base.Theme.AppCompat.Light.Dialog" parent="Base.V7.Theme.AppCompat.Light.Dialog" />
 
-    <style name="Base.Theme.AppCompat.Light.Dialog" parent="Base.Theme.AppCompat.Dialog" />
+    <style name="Base.Theme.AppCompat.Dialog.Alert">
+        <item name="windowMinWidthMajor">@dimen/abc_dialog_min_width_major</item>
+        <item name="windowMinWidthMinor">@dimen/abc_dialog_min_width_minor</item>
+    </style>
+
+    <style name="Base.Theme.AppCompat.Light.Dialog.Alert">
+        <item name="windowMinWidthMajor">@dimen/abc_dialog_min_width_major</item>
+        <item name="windowMinWidthMinor">@dimen/abc_dialog_min_width_minor</item>
+    </style>
+
+    <style name="Base.Theme.AppCompat.Dialog.MinWidth">
+        <item name="windowMinWidthMajor">@dimen/abc_dialog_min_width_major</item>
+        <item name="windowMinWidthMinor">@dimen/abc_dialog_min_width_minor</item>
+    </style>
+
+    <style name="Base.Theme.AppCompat.Light.Dialog.MinWidth">
+        <item name="windowMinWidthMajor">@dimen/abc_dialog_min_width_major</item>
+        <item name="windowMinWidthMinor">@dimen/abc_dialog_min_width_minor</item>
+    </style>
 
     <style name="Base.Theme.AppCompat.Dialog.FixedSize">
         <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
@@ -573,7 +517,28 @@
     <!-- Overlay themes -->
     <style name="Base.ThemeOverlay.AppCompat" parent="" />
 
-    <style name="Base.ThemeOverlay.AppCompat.Light">
+    <style name="Platform.ThemeOverlay.AppCompat.Dark" parent="">
+        <!-- Action Bar styles -->
+        <item name="actionBarItemBackground">@drawable/abc_item_background_holo_dark</item>
+        <item name="actionDropDownStyle">@style/Widget.AppCompat.Spinner.DropDown.ActionBar</item>
+        <item name="selectableItemBackground">@drawable/abc_item_background_holo_dark</item>
+
+        <!-- SearchView styles -->
+        <item name="android:autoCompleteTextViewStyle">@style/Widget.AppCompat.AutoCompleteTextView</item>
+        <item name="android:dropDownItemStyle">@style/Widget.AppCompat.DropDownItem.Spinner</item>
+    </style>
+
+    <style name="Platform.ThemeOverlay.AppCompat.Light" parent="">
+        <item name="actionBarItemBackground">@drawable/abc_item_background_holo_light</item>
+        <item name="actionDropDownStyle">@style/Widget.AppCompat.Light.Spinner.DropDown.ActionBar</item>
+        <item name="selectableItemBackground">@drawable/abc_item_background_holo_light</item>
+
+        <!-- SearchView attributes -->
+        <item name="android:autoCompleteTextViewStyle">@style/Widget.AppCompat.Light.AutoCompleteTextView</item>
+        <item name="android:dropDownItemStyle">@style/Widget.AppCompat.DropDownItem.Spinner</item>
+    </style>
+
+    <style name="Base.ThemeOverlay.AppCompat.Light" parent="Platform.ThemeOverlay.AppCompat.Light">
         <item name="android:windowBackground">@color/background_material_light</item>
         <item name="android:colorForeground">@color/bright_foreground_material_light</item>
         <item name="android:colorForegroundInverse">@color/bright_foreground_material_dark</item>
@@ -593,17 +558,10 @@
         <item name="android:textColorHighlight">@color/highlighted_text_material_light</item>
         <item name="android:textColorLink">@color/link_text_material_light</item>
 
-        <item name="actionBarItemBackground">@drawable/abc_item_background_holo_light</item>
-        <item name="actionDropDownStyle">@style/Widget.AppCompat.Light.Spinner.DropDown.ActionBar</item>
-        <item name="selectableItemBackground">@drawable/abc_item_background_holo_light</item>
-
-        <!-- SearchView attributes -->
-        <item name="android:autoCompleteTextViewStyle">@style/Widget.AppCompat.Light.AutoCompleteTextView</item>
-        <item name="android:dropDownItemStyle">@style/Widget.AppCompat.DropDownItem.Spinner</item>
-
         <item name="colorControlNormal">?android:attr/textColorSecondary</item>
         <item name="colorControlHighlight">@color/ripple_material_light</item>
-        <item name="colorSwitchThumbNormal">@color/switch_thumb_normal_material_light</item>
+        <item name="colorButtonNormal">@color/button_material_light</item>
+        <item name="colorSwitchThumbNormal">@color/switch_thumb_material_light</item>
 
         <!-- Used by MediaRouter -->
         <item name="isLightTheme">true</item>
@@ -628,18 +586,10 @@
         <item name="android:textColorHighlight">@color/highlighted_text_material_dark</item>
         <item name="android:textColorLink">@color/link_text_material_dark</item>
 
-        <!-- Action Bar styles -->
-        <item name="actionBarItemBackground">@drawable/abc_item_background_holo_dark</item>
-        <item name="actionDropDownStyle">@style/Widget.AppCompat.Spinner.DropDown.ActionBar</item>
-        <item name="selectableItemBackground">@drawable/abc_item_background_holo_dark</item>
-
-        <!-- SearchView styles -->
-        <item name="android:autoCompleteTextViewStyle">@style/Widget.AppCompat.AutoCompleteTextView</item>
-        <item name="android:dropDownItemStyle">@style/Widget.AppCompat.DropDownItem.Spinner</item>
-
         <item name="colorControlNormal">?android:attr/textColorSecondary</item>
         <item name="colorControlHighlight">@color/ripple_material_dark</item>
-        <item name="colorSwitchThumbNormal">@color/switch_thumb_normal_material_dark</item>
+        <item name="colorButtonNormal">@color/button_material_dark</item>
+        <item name="colorSwitchThumbNormal">@color/switch_thumb_material_dark</item>
 
         <!-- Used by MediaRouter -->
         <item name="isLightTheme">false</item>
@@ -647,10 +597,12 @@
 
     <style name="Base.ThemeOverlay.AppCompat.ActionBar">
         <item name="colorControlNormal">?android:attr/textColorPrimary</item>
+        <item name="searchViewStyle">@style/Widget.AppCompat.SearchView.ActionBar</item>
     </style>
 
     <style name="Base.ThemeOverlay.AppCompat.Dark.ActionBar">
         <item name="colorControlNormal">?android:attr/textColorPrimary</item>
+        <item name="searchViewStyle">@style/Widget.AppCompat.SearchView.ActionBar</item>
     </style>
 
 </resources>
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBar.java b/v7/appcompat/src/android/support/v7/app/ActionBar.java
index 785b7b8..d48dd31 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBar.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBar.java
@@ -1355,12 +1355,4 @@
             super(source);
         }
     }
-
-    /**
-     * Interface implemented by entities such as Activities that host action bars.
-     */
-    interface Callback {
-
-        FragmentManager getSupportFragmentManager();
-    }
 }
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarActivity.java b/v7/appcompat/src/android/support/v7/app/ActionBarActivity.java
index 2168d5e..1834681 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBarActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBarActivity.java
@@ -16,542 +16,9 @@
 
 package android.support.v7.app;
 
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.app.ActionBarDrawerToggle;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.NavUtils;
-import android.support.v4.app.TaskStackBuilder;
-import android.support.v4.view.WindowCompat;
-import android.support.v7.view.ActionMode;
-import android.support.v7.widget.Toolbar;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-
 /**
- * Base class for activities that use the <a
- * href="{@docRoot}tools/extras/support-library.html">support library</a> action bar features.
- *
- * <p>You can add an {@link ActionBar} to your activity when running on API level 7 or higher
- * by extending this class for your activity and setting the activity theme to
- * {@link android.support.v7.appcompat.R.style#Theme_AppCompat Theme.AppCompat} or a similar theme.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- *
- * <p>For information about how to use the action bar, including how to add action items, navigation
- * modes and more, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action
- * Bar</a> API guide.</p>
- * </div>
+ * @deprecated Use {@link android.support.v7.app.AppCompatActivity} instead.
  */
-public class ActionBarActivity extends FragmentActivity implements ActionBar.Callback,
-        TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider,
-        android.support.v7.app.ActionBarDrawerToggle.TmpDelegateProvider {
-
-    private ActionBarActivityDelegate mDelegate;
-
-    /**
-     * Support library version of {@link Activity#getActionBar}.
-     *
-     * <p>Retrieve a reference to this activity's ActionBar.
-     *
-     * @return The Activity's ActionBar, or null if it does not have one.
-     */
-    public ActionBar getSupportActionBar() {
-        return getDelegate().getSupportActionBar();
-    }
-
-    /**
-     * Set a {@link android.widget.Toolbar Toolbar} to act as the {@link ActionBar} for this
-     * Activity window.
-     *
-     * <p>When set to a non-null value the {@link #getActionBar()} method will return
-     * an {@link ActionBar} object that can be used to control the given toolbar as if it were
-     * a traditional window decor action bar. The toolbar's menu will be populated with the
-     * Activity's options menu and the navigation button will be wired through the standard
-     * {@link android.R.id#home home} menu select action.</p>
-     *
-     * <p>In order to use a Toolbar within the Activity's window content the application
-     * must not request the window feature {@link Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p>
-     *
-     * @param toolbar Toolbar to set as the Activity's action bar
-     */
-    public void setSupportActionBar(@Nullable Toolbar toolbar) {
-        getDelegate().setSupportActionBar(toolbar);
-    }
-
-    @Override
-    public MenuInflater getMenuInflater() {
-        return getDelegate().getMenuInflater();
-    }
-
-    @Override
-    public void setContentView(@LayoutRes int layoutResID) {
-        getDelegate().setContentView(layoutResID);
-    }
-
-    @Override
-    public void setContentView(View view) {
-        getDelegate().setContentView(view);
-    }
-
-    @Override
-    public void setContentView(View view, ViewGroup.LayoutParams params) {
-        getDelegate().setContentView(view, params);
-    }
-
-    @Override
-    public void addContentView(View view, ViewGroup.LayoutParams params) {
-        getDelegate().addContentView(view, params);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getDelegate().onCreate(savedInstanceState);
-    }
-
-    @Override
-    protected void onPostCreate(Bundle savedInstanceState) {
-        super.onPostCreate(savedInstanceState);
-        getDelegate().onPostCreate(savedInstanceState);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        getDelegate().onConfigurationChanged(newConfig);
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        getDelegate().onStop();
-    }
-
-    @Override
-    protected void onPostResume() {
-        super.onPostResume();
-        getDelegate().onPostResume();
-    }
-
-    @Override
-    public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) {
-        if (super.onMenuItemSelected(featureId, item)) {
-            return true;
-        }
-
-        final ActionBar ab = getSupportActionBar();
-        if (item.getItemId() == android.R.id.home && ab != null &&
-                (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
-            return onSupportNavigateUp();
-        }
-        return false;
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        getDelegate().destroy();
-    }
-
-    @Override
-    protected void onTitleChanged(CharSequence title, int color) {
-        super.onTitleChanged(title, color);
-        getDelegate().onTitleChanged(title);
-    }
-
-    /**
-     * Enable extended support library window features.
-     * <p>
-     * This is a convenience for calling
-     * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
-     * </p>
-     *
-     * @param featureId The desired feature as defined in
-     * {@link android.view.Window} or {@link WindowCompat}.
-     * @return Returns true if the requested feature is supported and now enabled.
-     *
-     * @see android.app.Activity#requestWindowFeature
-     * @see android.view.Window#requestFeature
-     */
-    public boolean supportRequestWindowFeature(int featureId) {
-        return getDelegate().supportRequestWindowFeature(featureId);
-    }
-
-    @Override
-    public void supportInvalidateOptionsMenu() {
-        getDelegate().supportInvalidateOptionsMenu();
-    }
-
-    /**
-     * @hide
-     */
-    public void invalidateOptionsMenu() {
-        getDelegate().supportInvalidateOptionsMenu();
-    }
-
-    /**
-     * Notifies the Activity that a support action mode has been started.
-     * Activity subclasses overriding this method should call the superclass implementation.
-     *
-     * @param mode The new action mode.
-     */
-    public void onSupportActionModeStarted(ActionMode mode) {
-    }
-
-    /**
-     * Notifies the activity that a support action mode has finished.
-     * Activity subclasses overriding this method should call the superclass implementation.
-     *
-     * @param mode The action mode that just finished.
-     */
-    public void onSupportActionModeFinished(ActionMode mode) {
-    }
-
-    public ActionMode startSupportActionMode(ActionMode.Callback callback) {
-        return getDelegate().startSupportActionMode(callback);
-    }
-
-    @Override
-    public boolean onCreatePanelMenu(int featureId, Menu menu) {
-        return getDelegate().onCreatePanelMenu(featureId, menu);
-    }
-
-    @Override
-    public boolean onPreparePanel(int featureId, View view, Menu menu) {
-        return getDelegate().onPreparePanel(featureId, view, menu);
-    }
-
-    @Override
-    public void onPanelClosed(int featureId, Menu menu) {
-        getDelegate().onPanelClosed(featureId, menu);
-    }
-
-    @Override
-    public boolean onMenuOpened(int featureId, Menu menu) {
-        return getDelegate().onMenuOpened(featureId, menu);
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    protected boolean onPrepareOptionsPanel(View view, Menu menu) {
-        return getDelegate().onPrepareOptionsPanel(view, menu);
-    }
-
-    void superSetContentView(int resId) {
-        super.setContentView(resId);
-    }
-
-    void superSetContentView(View v) {
-        super.setContentView(v);
-    }
-
-    void superSetContentView(View v, ViewGroup.LayoutParams lp) {
-        super.setContentView(v, lp);
-    }
-
-    void superAddContentView(View v, ViewGroup.LayoutParams lp) {
-        super.addContentView(v, lp);
-    }
-
-    boolean superOnCreatePanelMenu(int featureId, android.view.Menu frameworkMenu) {
-        return super.onCreatePanelMenu(featureId, frameworkMenu);
-    }
-
-    boolean superOnPreparePanel(int featureId, View view, android.view.Menu menu) {
-        return super.onPreparePanel(featureId, view, menu);
-    }
-
-    boolean superOnPrepareOptionsPanel(View view, Menu menu) {
-        return super.onPrepareOptionsPanel(view, menu);
-    }
-
-    void superOnPanelClosed(int featureId, Menu menu) {
-        super.onPanelClosed(featureId, menu);
-    }
-
-    boolean superOnMenuOpened(int featureId, Menu menu) {
-        return super.onMenuOpened(featureId, menu);
-    }
-
-    @Override
-    public void onBackPressed() {
-        if (!getDelegate().onBackPressed()) {
-            super.onBackPressed();
-        }
-    }
-
-    /**
-     * Support library version of {@link Activity#setProgressBarVisibility(boolean)}
-     * <p>
-     * Sets the visibility of the progress bar in the title.
-     * <p>
-     * In order for the progress bar to be shown, the feature must be requested
-     * via {@link #supportRequestWindowFeature(int)}.
-     *
-     * @param visible Whether to show the progress bars in the title.
-     */
-    public void setSupportProgressBarVisibility(boolean visible) {
-        getDelegate().setSupportProgressBarVisibility(visible);
-    }
-
-    /**
-     * Support library version of {@link Activity#setProgressBarIndeterminateVisibility(boolean)}
-     * <p>
-     * Sets the visibility of the indeterminate progress bar in the title.
-     * <p>
-     * In order for the progress bar to be shown, the feature must be requested
-     * via {@link #supportRequestWindowFeature(int)}.
-     *
-     * @param visible Whether to show the progress bars in the title.
-     */
-    public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
-        getDelegate().setSupportProgressBarIndeterminateVisibility(visible);
-    }
-
-    /**
-     * Support library version of {@link Activity#setProgressBarIndeterminate(boolean)}
-     * <p>
-     * Sets whether the horizontal progress bar in the title should be indeterminate (the
-     * circular is always indeterminate).
-     * <p>
-     * In order for the progress bar to be shown, the feature must be requested
-     * via {@link #supportRequestWindowFeature(int)}.
-     *
-     * @param indeterminate Whether the horizontal progress bar should be indeterminate.
-     */
-    public void setSupportProgressBarIndeterminate(boolean indeterminate) {
-        getDelegate().setSupportProgressBarIndeterminate(indeterminate);
-    }
-
-    /**
-     * Support library version of {@link Activity#setProgress(int)}.
-     * <p>
-     * Sets the progress for the progress bars in the title.
-     * <p>
-     * In order for the progress bar to be shown, the feature must be requested
-     * via {@link #supportRequestWindowFeature(int)}.
-     *
-     * @param progress The progress for the progress bar. Valid ranges are from
-     *            0 to 10000 (both inclusive). If 10000 is given, the progress
-     *            bar will be completely filled and will fade out.
-     */
-    public void setSupportProgress(int progress) {
-        getDelegate().setSupportProgress(progress);
-    }
-
-    /**
-     * Support version of {@link #onCreateNavigateUpTaskStack(android.app.TaskStackBuilder)}.
-     * This method will be called on all platform versions.
-     *
-     * Define the synthetic task stack that will be generated during Up navigation from
-     * a different task.
-     *
-     * <p>The default implementation of this method adds the parent chain of this activity
-     * as specified in the manifest to the supplied {@link TaskStackBuilder}. Applications
-     * may choose to override this method to construct the desired task stack in a different
-     * way.</p>
-     *
-     * <p>This method will be invoked by the default implementation of {@link #onNavigateUp()}
-     * if {@link #shouldUpRecreateTask(Intent)} returns true when supplied with the intent
-     * returned by {@link #getParentActivityIntent()}.</p>
-     *
-     * <p>Applications that wish to supply extra Intent parameters to the parent stack defined
-     * by the manifest should override
-     * {@link #onPrepareSupportNavigateUpTaskStack(TaskStackBuilder)}.</p>
-     *
-     * @param builder An empty TaskStackBuilder - the application should add intents representing
-     *                the desired task stack
-     */
-    public void onCreateSupportNavigateUpTaskStack(TaskStackBuilder builder) {
-        builder.addParentStack(this);
-    }
-
-    /**
-     * Support version of {@link #onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder)}.
-     * This method will be called on all platform versions.
-     *
-     * Prepare the synthetic task stack that will be generated during Up navigation
-     * from a different task.
-     *
-     * <p>This method receives the {@link TaskStackBuilder} with the constructed series of
-     * Intents as generated by {@link #onCreateSupportNavigateUpTaskStack(TaskStackBuilder)}.
-     * If any extra data should be added to these intents before launching the new task,
-     * the application should override this method and add that data here.</p>
-     *
-     * @param builder A TaskStackBuilder that has been populated with Intents by
-     *                onCreateNavigateUpTaskStack.
-     */
-    public void onPrepareSupportNavigateUpTaskStack(TaskStackBuilder builder) {
-    }
-
-    /**
-     * This method is called whenever the user chooses to navigate Up within your application's
-     * activity hierarchy from the action bar.
-     *
-     * <p>If a parent was specified in the manifest for this activity or an activity-alias to it,
-     * default Up navigation will be handled automatically. See
-     * {@link #getSupportParentActivityIntent()} for how to specify the parent. If any activity
-     * along the parent chain requires extra Intent arguments, the Activity subclass
-     * should override the method {@link #onPrepareSupportNavigateUpTaskStack(TaskStackBuilder)}
-     * to supply those arguments.</p>
-     *
-     * <p>See <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and
-     * Back Stack</a> from the developer guide and
-     * <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> from the design guide
-     * for more information about navigating within your app.</p>
-     *
-     * <p>See the {@link TaskStackBuilder} class and the Activity methods
-     * {@link #getSupportParentActivityIntent()}, {@link #supportShouldUpRecreateTask(Intent)}, and
-     * {@link #supportNavigateUpTo(Intent)} for help implementing custom Up navigation.</p>
-     *
-     * @return true if Up navigation completed successfully and this Activity was finished,
-     *         false otherwise.
-     */
-    public boolean onSupportNavigateUp() {
-        Intent upIntent = getSupportParentActivityIntent();
-
-        if (upIntent != null) {
-            if (supportShouldUpRecreateTask(upIntent)) {
-                TaskStackBuilder b = TaskStackBuilder.create(this);
-                onCreateSupportNavigateUpTaskStack(b);
-                onPrepareSupportNavigateUpTaskStack(b);
-                b.startActivities();
-
-                try {
-                    ActivityCompat.finishAffinity(this);
-                } catch (IllegalStateException e) {
-                    // This can only happen on 4.1+, when we don't have a parent or a result set.
-                    // In that case we should just finish().
-                    finish();
-                }
-            } else {
-                // This activity is part of the application's task, so simply
-                // navigate up to the hierarchical parent activity.
-                supportNavigateUpTo(upIntent);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Obtain an {@link Intent} that will launch an explicit target activity
-     * specified by sourceActivity's {@link NavUtils#PARENT_ACTIVITY} &lt;meta-data&gt;
-     * element in the application's manifest. If the device is running
-     * Jellybean or newer, the android:parentActivityName attribute will be preferred
-     * if it is present.
-     *
-     * @return a new Intent targeting the defined parent activity of sourceActivity
-     */
-    public Intent getSupportParentActivityIntent() {
-        return NavUtils.getParentActivityIntent(this);
-    }
-
-    /**
-     * Returns true if sourceActivity should recreate the task when navigating 'up'
-     * by using targetIntent.
-     *
-     * <p>If this method returns false the app can trivially call
-     * {@link #supportNavigateUpTo(Intent)} using the same parameters to correctly perform
-     * up navigation. If this method returns false, the app should synthesize a new task stack
-     * by using {@link TaskStackBuilder} or another similar mechanism to perform up navigation.</p>
-     *
-     * @param targetIntent An intent representing the target destination for up navigation
-     * @return true if navigating up should recreate a new task stack, false if the same task
-     *         should be used for the destination
-     */
-    public boolean supportShouldUpRecreateTask(Intent targetIntent) {
-        return NavUtils.shouldUpRecreateTask(this, targetIntent);
-    }
-
-    /**
-     * Navigate from sourceActivity to the activity specified by upIntent, finishing sourceActivity
-     * in the process. upIntent will have the flag {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} set
-     * by this method, along with any others required for proper up navigation as outlined
-     * in the Android Design Guide.
-     *
-     * <p>This method should be used when performing up navigation from within the same task
-     * as the destination. If up navigation should cross tasks in some cases, see
-     * {@link #supportShouldUpRecreateTask(Intent)}.</p>
-     *
-     * @param upIntent An intent representing the target destination for up navigation
-     */
-    public void supportNavigateUpTo(Intent upIntent) {
-        NavUtils.navigateUpTo(this, upIntent);
-    }
-
-    @Override
-    public final ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
-        return getDelegate().getDrawerToggleDelegate();
-    }
-
-    @Nullable
-    @Override
-    /**
-     * Temporary method until ActionBarDrawerToggle transition from v4 to v7 is complete.
-     */
-    public android.support.v7.app.ActionBarDrawerToggle.Delegate getV7DrawerToggleDelegate() {
-        return getDelegate().getV7DrawerToggleDelegate();
-    }
-
-    @Override
-    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
-        return getDelegate().onKeyShortcut(keyCode, event);
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if (getDelegate().dispatchKeyEvent(event)) {
-            return true;
-        }
-        return super.dispatchKeyEvent(event);
-    }
-
-    /**
-     * Use {@link #onSupportContentChanged()} instead.
-     */
-    public final void onContentChanged() {
-        getDelegate().onContentChanged();
-    }
-
-    /**
-     * This hook is called whenever the content view of the screen changes.
-     * @see android.app.Activity#onContentChanged()
-     */
-    public void onSupportContentChanged() {
-    }
-
-    @Override
-    public View onCreateView(String name, @NonNull Context context, @NonNull AttributeSet attrs) {
-        // Allow super (FragmentActivity) to try and create a view first
-        final View result = super.onCreateView(name, context, attrs);
-        if (result != null) {
-            return result;
-        }
-        // If we reach here super didn't create a View, so let our delegate attempt it
-        return getDelegate().createView(name, context, attrs);
-    }
-
-    private ActionBarActivityDelegate getDelegate() {
-        if (mDelegate == null) {
-            mDelegate = ActionBarActivityDelegate.createDelegate(this);
-        }
-        return mDelegate;
-    }
+@Deprecated
+public class ActionBarActivity extends AppCompatActivity {
 }
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegate.java b/v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegate.java
deleted file mode 100644
index 9599adc..0000000
--- a/v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegate.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2013 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.v7.app;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.ActionBarDrawerToggle;
-import android.support.v7.appcompat.R;
-import android.support.v7.internal.app.WindowCallback;
-import android.support.v7.internal.view.SupportMenuInflater;
-import android.support.v7.internal.widget.TintTypedArray;
-import android.support.v7.view.ActionMode;
-import android.support.v7.widget.Toolbar;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-
-abstract class ActionBarActivityDelegate {
-
-    static final String METADATA_UI_OPTIONS = "android.support.UI_OPTIONS";
-
-    private static final String TAG = "ActionBarActivityDelegate";
-
-    static ActionBarActivityDelegate createDelegate(ActionBarActivity activity) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
-            return new ActionBarActivityDelegateHC(activity);
-        } else {
-            return new ActionBarActivityDelegateBase(activity);
-        }
-    }
-
-    final ActionBarActivity mActivity;
-
-    private ActionBar mActionBar;
-    private MenuInflater mMenuInflater;
-
-    // true if this activity has an action bar.
-    boolean mHasActionBar;
-    // true if this activity's action bar overlays other activity content.
-    boolean mOverlayActionBar;
-    // true if this any action modes should overlay the activity content
-    boolean mOverlayActionMode;
-    // true if this activity is floating (e.g. Dialog)
-    boolean mIsFloating;
-    // The default window callback
-    final WindowCallback mDefaultWindowCallback = new WindowCallback() {
-        @Override
-        public boolean onMenuItemSelected(int featureId, MenuItem menuItem) {
-            return mActivity.onMenuItemSelected(featureId, menuItem);
-        }
-
-        @Override
-        public boolean onCreatePanelMenu(int featureId, Menu menu) {
-            return mActivity.superOnCreatePanelMenu(featureId, menu);
-        }
-
-        @Override
-        public boolean onPreparePanel(int featureId, View menuView, Menu menu) {
-            return mActivity.superOnPreparePanel(featureId, menuView, menu);
-        }
-
-        @Override
-        public void onPanelClosed(int featureId, Menu menu) {
-            mActivity.onPanelClosed(featureId, menu);
-        }
-
-        @Override
-        public boolean onMenuOpened(int featureId, Menu menu) {
-            return mActivity.onMenuOpened(featureId, menu);
-        }
-
-        @Override
-        public ActionMode startActionMode(ActionMode.Callback callback) {
-            return startSupportActionModeFromWindow(callback);
-        }
-
-        @Override
-        public View onCreatePanelView(int featureId) {
-            return mActivity.onCreatePanelView(featureId);
-        }
-    };
-    // The fake window callback we're currently using
-    private WindowCallback mWindowCallback;
-    private boolean mIsDestroyed;
-
-    ActionBarActivityDelegate(ActionBarActivity activity) {
-        mActivity = activity;
-        mWindowCallback = mDefaultWindowCallback;
-    }
-
-    abstract ActionBar createSupportActionBar();
-
-    final ActionBar getSupportActionBar() {
-        // The Action Bar should be lazily created as hasActionBar
-        // could change after onCreate
-        if (mHasActionBar) {
-            if (mActionBar == null) {
-                mActionBar = createSupportActionBar();
-            }
-        }
-        return mActionBar;
-    }
-
-    final ActionBar peekSupportActionBar() {
-        return mActionBar;
-    }
-
-    protected final void setSupportActionBar(ActionBar actionBar) {
-        mActionBar = actionBar;
-    }
-
-    abstract void setSupportActionBar(Toolbar toolbar);
-
-    MenuInflater getMenuInflater() {
-        if (mMenuInflater == null) {
-            mMenuInflater = new SupportMenuInflater(getActionBarThemedContext());
-        }
-        return mMenuInflater;
-    }
-
-    void onCreate(Bundle savedInstanceState) {
-        TypedArray a = mActivity.obtainStyledAttributes(R.styleable.Theme);
-
-        if (!a.hasValue(R.styleable.Theme_windowActionBar)) {
-            a.recycle();
-            throw new IllegalStateException(
-                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
-        }
-
-        if (a.getBoolean(R.styleable.Theme_windowActionBar, false)) {
-            mHasActionBar = true;
-        }
-        if (a.getBoolean(R.styleable.Theme_windowActionBarOverlay, false)) {
-            mOverlayActionBar = true;
-        }
-        if (a.getBoolean(R.styleable.Theme_windowActionModeOverlay, false)) {
-            mOverlayActionMode = true;
-        }
-        mIsFloating = a.getBoolean(R.styleable.Theme_android_windowIsFloating, false);
-        a.recycle();
-    }
-
-    abstract void onPostCreate(Bundle savedInstanceState);
-
-    abstract void onConfigurationChanged(Configuration newConfig);
-
-    abstract void onStop();
-
-    abstract void onPostResume();
-
-    abstract void setContentView(View v);
-
-    abstract void setContentView(int resId);
-
-    abstract void setContentView(View v, ViewGroup.LayoutParams lp);
-
-    abstract void addContentView(View v, ViewGroup.LayoutParams lp);
-
-    abstract void onTitleChanged(CharSequence title);
-
-    abstract void supportInvalidateOptionsMenu();
-
-    abstract boolean supportRequestWindowFeature(int featureId);
-
-    // Methods used to create and respond to options menu
-    abstract boolean onPreparePanel(int featureId, View view, Menu menu);
-
-    abstract void onPanelClosed(int featureId, Menu menu);
-
-    abstract boolean onMenuOpened(int featureId, Menu menu);
-
-    boolean onPrepareOptionsPanel(View view, Menu menu) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
-            // Call straight through to onPrepareOptionsMenu, bypassing super.onPreparePanel().
-            // This is because Activity.onPreparePanel() on <v4.1 calls menu.hasVisibleItems(),
-            // which interferes with the initially invisible items.
-            return mActivity.onPrepareOptionsMenu(menu);
-        }
-        return mActivity.superOnPrepareOptionsPanel(view, menu);
-    }
-
-    abstract boolean onCreatePanelMenu(int featureId, Menu menu);
-
-    abstract boolean onBackPressed();
-
-    abstract ActionMode startSupportActionMode(ActionMode.Callback callback);
-
-    abstract void setSupportProgressBarVisibility(boolean visible);
-
-    abstract void setSupportProgressBarIndeterminateVisibility(boolean visible);
-
-    abstract void setSupportProgressBarIndeterminate(boolean indeterminate);
-
-    abstract void setSupportProgress(int progress);
-
-    abstract boolean dispatchKeyEvent(KeyEvent event);
-
-    abstract boolean onKeyShortcut(int keyCode, KeyEvent event);
-
-    final ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
-        return new ActionBarDrawableToggleImpl();
-    }
-
-    final android.support.v7.app.ActionBarDrawerToggle.Delegate getV7DrawerToggleDelegate() {
-        return new ActionBarDrawableToggleImpl();
-    }
-
-    abstract int getHomeAsUpIndicatorAttrId();
-
-    abstract void onContentChanged();
-
-    final String getUiOptionsFromMetadata() {
-        try {
-            PackageManager pm = mActivity.getPackageManager();
-            ActivityInfo info = pm.getActivityInfo(mActivity.getComponentName(),
-                    PackageManager.GET_META_DATA);
-
-            String uiOptions = null;
-            if (info.metaData != null) {
-                uiOptions = info.metaData.getString(METADATA_UI_OPTIONS);
-            }
-            return uiOptions;
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "getUiOptionsFromMetadata: Activity '" + mActivity.getClass()
-                    .getSimpleName() + "' not in manifest");
-            return null;
-        }
-    }
-
-    protected final Context getActionBarThemedContext() {
-        Context context = null;
-
-        // If we have an action bar, let it return a themed context
-        ActionBar ab = getSupportActionBar();
-        if (ab != null) {
-            context = ab.getThemedContext();
-        }
-
-        if (context == null) {
-            context = mActivity;
-        }
-        return context;
-    }
-
-    abstract View createView(String name, @NonNull Context context, @NonNull AttributeSet attrs);
-
-    private class ActionBarDrawableToggleImpl implements
-            android.support.v7.app.ActionBarDrawerToggle.Delegate,
-            ActionBarDrawerToggle.Delegate {
-        @Override
-        public Drawable getThemeUpIndicator() {
-            final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
-                    getActionBarThemedContext(), null, new int[]{ getHomeAsUpIndicatorAttrId() });
-            final Drawable result = a.getDrawable(0);
-            a.recycle();
-            return result;
-        }
-
-        @Override
-        public Context getActionBarThemedContext() {
-            return ActionBarActivityDelegate.this.getActionBarThemedContext();
-        }
-
-        @Override
-        public boolean isNavigationVisible() {
-            final ActionBar ab = getSupportActionBar();
-            return ab != null && (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0;
-        }
-
-        @Override
-        public void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
-            ActionBar ab = getSupportActionBar();
-            if (ab != null) {
-                ab.setHomeAsUpIndicator(upDrawable);
-                ab.setHomeActionContentDescription(contentDescRes);
-            }
-        }
-
-        @Override
-        public void setActionBarDescription(int contentDescRes) {
-            ActionBar ab = getSupportActionBar();
-            if (ab != null) {
-                ab.setHomeActionContentDescription(contentDescRes);
-            }
-        }
-    }
-
-    abstract ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback);
-
-    final void setWindowCallback(WindowCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback can not be null");
-        }
-        mWindowCallback = callback;
-    }
-
-    final WindowCallback getWindowCallback() {
-        return mWindowCallback;
-    }
-
-    final void destroy() {
-        mIsDestroyed = true;
-    }
-
-    final boolean isDestroyed() {
-        return mIsDestroyed;
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
index 93b6a66..93a79a6 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
@@ -78,15 +78,6 @@
         Delegate getDrawerToggleDelegate();
     }
 
-    /**
-     * @deprecated Temporary class for the transition from old drawer toggle to new drawer toggle.
-     */
-    interface TmpDelegateProvider {
-
-        @Nullable
-        Delegate getV7DrawerToggleDelegate();
-    }
-
     public interface Delegate {
 
         /**
@@ -215,8 +206,6 @@
             });
         } else if (activity instanceof DelegateProvider) { // Allow the Activity to provide an impl
             mActivityImpl = ((DelegateProvider) activity).getDrawerToggleDelegate();
-        } else if (activity instanceof TmpDelegateProvider) {// tmp interface for transition
-            mActivityImpl = ((TmpDelegateProvider) activity).getV7DrawerToggleDelegate();
         } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
             mActivityImpl = new JellybeanMr2Delegate(activity);
         } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
diff --git a/v7/appcompat/src/android/support/v7/app/AlertController.java b/v7/appcompat/src/android/support/v7/app/AlertController.java
new file mode 100644
index 0000000..76c1670
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/app/AlertController.java
@@ -0,0 +1,856 @@
+/*
+ * 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.v7.app;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckedTextView;
+import android.widget.CursorAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.ScrollView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+
+import java.lang.ref.WeakReference;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+class AlertController {
+    private final Context mContext;
+    private final AppCompatDialog mDialog;
+    private final Window mWindow;
+
+    private CharSequence mTitle;
+    private CharSequence mMessage;
+    private ListView mListView;
+    private View mView;
+
+    private int mViewLayoutResId;
+
+    private int mViewSpacingLeft;
+    private int mViewSpacingTop;
+    private int mViewSpacingRight;
+    private int mViewSpacingBottom;
+    private boolean mViewSpacingSpecified = false;
+
+    private Button mButtonPositive;
+    private CharSequence mButtonPositiveText;
+    private Message mButtonPositiveMessage;
+
+    private Button mButtonNegative;
+    private CharSequence mButtonNegativeText;
+    private Message mButtonNegativeMessage;
+
+    private Button mButtonNeutral;
+    private CharSequence mButtonNeutralText;
+    private Message mButtonNeutralMessage;
+
+    private ScrollView mScrollView;
+
+    private int mIconId = 0;
+    private Drawable mIcon;
+
+    private ImageView mIconView;
+    private TextView mTitleView;
+    private TextView mMessageView;
+    private View mCustomTitleView;
+
+    private ListAdapter mAdapter;
+
+    private int mCheckedItem = -1;
+
+    private int mAlertDialogLayout;
+    private int mButtonPanelSideLayout;
+    private int mListLayout;
+    private int mMultiChoiceItemLayout;
+    private int mSingleChoiceItemLayout;
+    private int mListItemLayout;
+
+    private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;
+
+    private Handler mHandler;
+
+    private final View.OnClickListener mButtonHandler = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            final Message m;
+            if (v == mButtonPositive && mButtonPositiveMessage != null) {
+                m = Message.obtain(mButtonPositiveMessage);
+            } else if (v == mButtonNegative && mButtonNegativeMessage != null) {
+                m = Message.obtain(mButtonNegativeMessage);
+            } else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
+                m = Message.obtain(mButtonNeutralMessage);
+            } else {
+                m = null;
+            }
+
+            if (m != null) {
+                m.sendToTarget();
+            }
+
+            // Post a message so we dismiss after the above handlers are executed
+            mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialog)
+                    .sendToTarget();
+        }
+    };
+
+    private static final class ButtonHandler extends Handler {
+        // Button clicks have Message.what as the BUTTON{1,2,3} constant
+        private static final int MSG_DISMISS_DIALOG = 1;
+
+        private WeakReference<DialogInterface> mDialog;
+
+        public ButtonHandler(DialogInterface dialog) {
+            mDialog = new WeakReference<>(dialog);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+
+                case DialogInterface.BUTTON_POSITIVE:
+                case DialogInterface.BUTTON_NEGATIVE:
+                case DialogInterface.BUTTON_NEUTRAL:
+                    ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
+                    break;
+
+                case MSG_DISMISS_DIALOG:
+                    ((DialogInterface) msg.obj).dismiss();
+            }
+        }
+    }
+
+    private static boolean shouldCenterSingleButton(Context context) {
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.alertDialogCenterButtons, outValue, true);
+        return outValue.data != 0;
+    }
+
+    public AlertController(Context context, AppCompatDialog di, Window window) {
+        mContext = context;
+        mDialog = di;
+        mWindow = window;
+        mHandler = new ButtonHandler(di);
+
+        TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,
+                R.attr.alertDialogStyle, 0);
+
+        mAlertDialogLayout = a.getResourceId(R.styleable.AlertDialog_android_layout, 0);
+        mButtonPanelSideLayout = a.getResourceId(R.styleable.AlertDialog_buttonPanelSideLayout, 0);
+
+        mListLayout = a.getResourceId(R.styleable.AlertDialog_listLayout, 0);
+        mMultiChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_multiChoiceItemLayout, 0);
+        mSingleChoiceItemLayout = a
+                .getResourceId(R.styleable.AlertDialog_singleChoiceItemLayout, 0);
+        mListItemLayout = a.getResourceId(R.styleable.AlertDialog_listItemLayout, 0);
+
+        a.recycle();
+    }
+
+    static boolean canTextInput(View v) {
+        if (v.onCheckIsTextEditor()) {
+            return true;
+        }
+
+        if (!(v instanceof ViewGroup)) {
+            return false;
+        }
+
+        ViewGroup vg = (ViewGroup) v;
+        int i = vg.getChildCount();
+        while (i > 0) {
+            i--;
+            v = vg.getChildAt(i);
+            if (canTextInput(v)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public void installContent() {
+        /* We use a custom title so never request a window title */
+        mDialog.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
+
+        final int contentView = selectContentView();
+        mDialog.setContentView(contentView);
+        setupView();
+    }
+
+    private int selectContentView() {
+        if (mButtonPanelSideLayout == 0) {
+            return mAlertDialogLayout;
+        }
+        if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
+            return mButtonPanelSideLayout;
+        }
+        return mAlertDialogLayout;
+    }
+
+    public void setTitle(CharSequence title) {
+        mTitle = title;
+        if (mTitleView != null) {
+            mTitleView.setText(title);
+        }
+    }
+
+    /**
+     * @see AlertDialog.Builder#setCustomTitle(View)
+     */
+    public void setCustomTitle(View customTitleView) {
+        mCustomTitleView = customTitleView;
+    }
+
+    public void setMessage(CharSequence message) {
+        mMessage = message;
+        if (mMessageView != null) {
+            mMessageView.setText(message);
+        }
+    }
+
+    /**
+     * Set the view resource to display in the dialog.
+     */
+    public void setView(int layoutResId) {
+        mView = null;
+        mViewLayoutResId = layoutResId;
+        mViewSpacingSpecified = false;
+    }
+
+    /**
+     * Set the view to display in the dialog.
+     */
+    public void setView(View view) {
+        mView = view;
+        mViewLayoutResId = 0;
+        mViewSpacingSpecified = false;
+    }
+
+    /**
+     * Set the view to display in the dialog along with the spacing around that view
+     */
+    public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
+            int viewSpacingBottom) {
+        mView = view;
+        mViewLayoutResId = 0;
+        mViewSpacingSpecified = true;
+        mViewSpacingLeft = viewSpacingLeft;
+        mViewSpacingTop = viewSpacingTop;
+        mViewSpacingRight = viewSpacingRight;
+        mViewSpacingBottom = viewSpacingBottom;
+    }
+
+    /**
+     * Sets a hint for the best button panel layout.
+     */
+    public void setButtonPanelLayoutHint(int layoutHint) {
+        mButtonPanelLayoutHint = layoutHint;
+    }
+
+    /**
+     * Sets a click listener or a message to be sent when the button is clicked.
+     * You only need to pass one of {@code listener} or {@code msg}.
+     *
+     * @param whichButton Which button, can be one of
+     *                    {@link DialogInterface#BUTTON_POSITIVE},
+     *                    {@link DialogInterface#BUTTON_NEGATIVE}, or
+     *                    {@link DialogInterface#BUTTON_NEUTRAL}
+     * @param text        The text to display in positive button.
+     * @param listener    The {@link DialogInterface.OnClickListener} to use.
+     * @param msg         The {@link Message} to be sent when clicked.
+     */
+    public void setButton(int whichButton, CharSequence text,
+            DialogInterface.OnClickListener listener, Message msg) {
+
+        if (msg == null && listener != null) {
+            msg = mHandler.obtainMessage(whichButton, listener);
+        }
+
+        switch (whichButton) {
+
+            case DialogInterface.BUTTON_POSITIVE:
+                mButtonPositiveText = text;
+                mButtonPositiveMessage = msg;
+                break;
+
+            case DialogInterface.BUTTON_NEGATIVE:
+                mButtonNegativeText = text;
+                mButtonNegativeMessage = msg;
+                break;
+
+            case DialogInterface.BUTTON_NEUTRAL:
+                mButtonNeutralText = text;
+                mButtonNeutralMessage = msg;
+                break;
+
+            default:
+                throw new IllegalArgumentException("Button does not exist");
+        }
+    }
+
+    /**
+     * Specifies the icon to display next to the alert title.
+     *
+     * @param resId the resource identifier of the drawable to use as the icon,
+     *              or 0 for no icon
+     */
+    public void setIcon(int resId) {
+        mIcon = null;
+        mIconId = resId;
+
+        if (mIconView != null) {
+            if (resId != 0) {
+                mIconView.setImageResource(mIconId);
+            } else {
+                mIconView.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    /**
+     * Specifies the icon to display next to the alert title.
+     *
+     * @param icon the drawable to use as the icon or null for no icon
+     */
+    public void setIcon(Drawable icon) {
+        mIcon = icon;
+        mIconId = 0;
+
+        if (mIconView != null) {
+            if (icon != null) {
+                mIconView.setImageDrawable(icon);
+            } else {
+                mIconView.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    /**
+     * @param attrId the attributeId of the theme-specific drawable
+     *               to resolve the resourceId for.
+     * @return resId the resourceId of the theme-specific drawable
+     */
+    public int getIconAttributeResId(int attrId) {
+        TypedValue out = new TypedValue();
+        mContext.getTheme().resolveAttribute(attrId, out, true);
+        return out.resourceId;
+    }
+
+    public ListView getListView() {
+        return mListView;
+    }
+
+    public Button getButton(int whichButton) {
+        switch (whichButton) {
+            case DialogInterface.BUTTON_POSITIVE:
+                return mButtonPositive;
+            case DialogInterface.BUTTON_NEGATIVE:
+                return mButtonNegative;
+            case DialogInterface.BUTTON_NEUTRAL:
+                return mButtonNeutral;
+            default:
+                return null;
+        }
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return mScrollView != null && mScrollView.executeKeyEvent(event);
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        return mScrollView != null && mScrollView.executeKeyEvent(event);
+    }
+
+    private void setupView() {
+        final ViewGroup contentPanel = (ViewGroup) mWindow.findViewById(R.id.contentPanel);
+        setupContent(contentPanel);
+        final boolean hasButtons = setupButtons();
+
+        final ViewGroup topPanel = (ViewGroup) mWindow.findViewById(R.id.topPanel);
+        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(mContext,
+                null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
+        final boolean hasTitle = setupTitle(topPanel);
+
+        final View buttonPanel = mWindow.findViewById(R.id.buttonPanel);
+        if (!hasButtons) {
+            buttonPanel.setVisibility(View.GONE);
+            final View spacer = mWindow.findViewById(R.id.textSpacerNoButtons);
+            if (spacer != null) {
+                spacer.setVisibility(View.VISIBLE);
+            }
+        }
+
+        final FrameLayout customPanel = (FrameLayout) mWindow.findViewById(R.id.customPanel);
+        final View customView;
+        if (mView != null) {
+            customView = mView;
+        } else if (mViewLayoutResId != 0) {
+            final LayoutInflater inflater = LayoutInflater.from(mContext);
+            customView = inflater.inflate(mViewLayoutResId, customPanel, false);
+        } else {
+            customView = null;
+        }
+
+        final boolean hasCustomView = customView != null;
+        if (!hasCustomView || !canTextInput(customView)) {
+            mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                    WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+        }
+
+        if (hasCustomView) {
+            final FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
+            custom.addView(customView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+            if (mViewSpacingSpecified) {
+                custom.setPadding(
+                        mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom);
+            }
+
+            if (mListView != null) {
+                ((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0;
+            }
+        } else {
+            customPanel.setVisibility(View.GONE);
+        }
+
+        final ListView listView = mListView;
+        if (listView != null && mAdapter != null) {
+            listView.setAdapter(mAdapter);
+            final int checkedItem = mCheckedItem;
+            if (checkedItem > -1) {
+                listView.setItemChecked(checkedItem, true);
+                listView.setSelection(checkedItem);
+            }
+        }
+
+        a.recycle();
+    }
+
+    private boolean setupTitle(ViewGroup topPanel) {
+        boolean hasTitle = true;
+
+        if (mCustomTitleView != null) {
+            // Add the custom title view directly to the topPanel layout
+            LayoutParams lp = new LayoutParams(
+                    LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+
+            topPanel.addView(mCustomTitleView, 0, lp);
+
+            // Hide the title template
+            View titleTemplate = mWindow.findViewById(R.id.title_template);
+            titleTemplate.setVisibility(View.GONE);
+        } else {
+            mIconView = (ImageView) mWindow.findViewById(android.R.id.icon);
+
+            final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
+            if (hasTextTitle) {
+                // Display the title if a title is supplied, else hide it.
+                mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
+                mTitleView.setText(mTitle);
+
+                // Do this last so that if the user has supplied any icons we
+                // use them instead of the default ones. If the user has
+                // specified 0 then make it disappear.
+                if (mIconId != 0) {
+                    mIconView.setImageResource(mIconId);
+                } else if (mIcon != null) {
+                    mIconView.setImageDrawable(mIcon);
+                } else {
+                    // Apply the padding from the icon to ensure the title is
+                    // aligned correctly.
+                    mTitleView.setPadding(mIconView.getPaddingLeft(),
+                            mIconView.getPaddingTop(),
+                            mIconView.getPaddingRight(),
+                            mIconView.getPaddingBottom());
+                    mIconView.setVisibility(View.GONE);
+                }
+            } else {
+                // Hide the title template
+                final View titleTemplate = mWindow.findViewById(R.id.title_template);
+                titleTemplate.setVisibility(View.GONE);
+                mIconView.setVisibility(View.GONE);
+                topPanel.setVisibility(View.GONE);
+                hasTitle = false;
+            }
+        }
+        return hasTitle;
+    }
+
+    private void setupContent(ViewGroup contentPanel) {
+        mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView);
+        mScrollView.setFocusable(false);
+
+        // Special case for users that only want to display a String
+        mMessageView = (TextView) mWindow.findViewById(android.R.id.message);
+        if (mMessageView == null) {
+            return;
+        }
+
+        if (mMessage != null) {
+            mMessageView.setText(mMessage);
+        } else {
+            mMessageView.setVisibility(View.GONE);
+            mScrollView.removeView(mMessageView);
+
+            if (mListView != null) {
+                final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent();
+                final int childIndex = scrollParent.indexOfChild(mScrollView);
+                scrollParent.removeViewAt(childIndex);
+                scrollParent.addView(mListView, childIndex,
+                        new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+            } else {
+                contentPanel.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    private boolean setupButtons() {
+        int BIT_BUTTON_POSITIVE = 1;
+        int BIT_BUTTON_NEGATIVE = 2;
+        int BIT_BUTTON_NEUTRAL = 4;
+        int whichButtons = 0;
+        mButtonPositive = (Button) mWindow.findViewById(android.R.id.button1);
+        mButtonPositive.setOnClickListener(mButtonHandler);
+
+        if (TextUtils.isEmpty(mButtonPositiveText)) {
+            mButtonPositive.setVisibility(View.GONE);
+        } else {
+            mButtonPositive.setText(mButtonPositiveText);
+            mButtonPositive.setVisibility(View.VISIBLE);
+            whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
+        }
+
+        mButtonNegative = (Button) mWindow.findViewById(android.R.id.button2);
+        mButtonNegative.setOnClickListener(mButtonHandler);
+
+        if (TextUtils.isEmpty(mButtonNegativeText)) {
+            mButtonNegative.setVisibility(View.GONE);
+        } else {
+            mButtonNegative.setText(mButtonNegativeText);
+            mButtonNegative.setVisibility(View.VISIBLE);
+
+            whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
+        }
+
+        mButtonNeutral = (Button) mWindow.findViewById(android.R.id.button3);
+        mButtonNeutral.setOnClickListener(mButtonHandler);
+
+        if (TextUtils.isEmpty(mButtonNeutralText)) {
+            mButtonNeutral.setVisibility(View.GONE);
+        } else {
+            mButtonNeutral.setText(mButtonNeutralText);
+            mButtonNeutral.setVisibility(View.VISIBLE);
+
+            whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
+        }
+
+        if (shouldCenterSingleButton(mContext)) {
+            /*
+             * If we only have 1 button it should be centered on the layout and
+             * expand to fill 50% of the available space.
+             */
+            if (whichButtons == BIT_BUTTON_POSITIVE) {
+                centerButton(mButtonPositive);
+            } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
+                centerButton(mButtonNegative);
+            } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
+                centerButton(mButtonNeutral);
+            }
+        }
+
+        return whichButtons != 0;
+    }
+
+    private void centerButton(Button button) {
+        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
+        params.gravity = Gravity.CENTER_HORIZONTAL;
+        params.weight = 0.5f;
+        button.setLayoutParams(params);
+    }
+
+    public static class AlertParams {
+        public final Context mContext;
+        public final LayoutInflater mInflater;
+
+        public int mIconId = 0;
+        public Drawable mIcon;
+        public int mIconAttrId = 0;
+        public CharSequence mTitle;
+        public View mCustomTitleView;
+        public CharSequence mMessage;
+        public CharSequence mPositiveButtonText;
+        public DialogInterface.OnClickListener mPositiveButtonListener;
+        public CharSequence mNegativeButtonText;
+        public DialogInterface.OnClickListener mNegativeButtonListener;
+        public CharSequence mNeutralButtonText;
+        public DialogInterface.OnClickListener mNeutralButtonListener;
+        public boolean mCancelable;
+        public DialogInterface.OnCancelListener mOnCancelListener;
+        public DialogInterface.OnDismissListener mOnDismissListener;
+        public DialogInterface.OnKeyListener mOnKeyListener;
+        public CharSequence[] mItems;
+        public ListAdapter mAdapter;
+        public DialogInterface.OnClickListener mOnClickListener;
+        public int mViewLayoutResId;
+        public View mView;
+        public int mViewSpacingLeft;
+        public int mViewSpacingTop;
+        public int mViewSpacingRight;
+        public int mViewSpacingBottom;
+        public boolean mViewSpacingSpecified = false;
+        public boolean[] mCheckedItems;
+        public boolean mIsMultiChoice;
+        public boolean mIsSingleChoice;
+        public int mCheckedItem = -1;
+        public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
+        public Cursor mCursor;
+        public String mLabelColumn;
+        public String mIsCheckedColumn;
+        public boolean mForceInverseBackground;
+        public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
+        public OnPrepareListViewListener mOnPrepareListViewListener;
+        public boolean mRecycleOnMeasure = true;
+
+        /**
+         * Interface definition for a callback to be invoked before the ListView
+         * will be bound to an adapter.
+         */
+        public interface OnPrepareListViewListener {
+
+            /**
+             * Called before the ListView is bound to an adapter.
+             *
+             * @param listView The ListView that will be shown in the dialog.
+             */
+            void onPrepareListView(ListView listView);
+        }
+
+        public AlertParams(Context context) {
+            mContext = context;
+            mCancelable = true;
+            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        }
+
+        public void apply(AlertController dialog) {
+            if (mCustomTitleView != null) {
+                dialog.setCustomTitle(mCustomTitleView);
+            } else {
+                if (mTitle != null) {
+                    dialog.setTitle(mTitle);
+                }
+                if (mIcon != null) {
+                    dialog.setIcon(mIcon);
+                }
+                if (mIconId != 0) {
+                    dialog.setIcon(mIconId);
+                }
+                if (mIconAttrId != 0) {
+                    dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
+                }
+            }
+            if (mMessage != null) {
+                dialog.setMessage(mMessage);
+            }
+            if (mPositiveButtonText != null) {
+                dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
+                        mPositiveButtonListener, null);
+            }
+            if (mNegativeButtonText != null) {
+                dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
+                        mNegativeButtonListener, null);
+            }
+            if (mNeutralButtonText != null) {
+                dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
+                        mNeutralButtonListener, null);
+            }
+            // For a list, the client can either supply an array of items or an
+            // adapter or a cursor
+            if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
+                createListView(dialog);
+            }
+            if (mView != null) {
+                if (mViewSpacingSpecified) {
+                    dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
+                            mViewSpacingBottom);
+                } else {
+                    dialog.setView(mView);
+                }
+            } else if (mViewLayoutResId != 0) {
+                dialog.setView(mViewLayoutResId);
+            }
+
+            /*
+            dialog.setCancelable(mCancelable);
+            dialog.setOnCancelListener(mOnCancelListener);
+            if (mOnKeyListener != null) {
+                dialog.setOnKeyListener(mOnKeyListener);
+            }
+            */
+        }
+
+        private void createListView(final AlertController dialog) {
+            final ListView listView = (ListView) mInflater.inflate(dialog.mListLayout, null);
+            ListAdapter adapter;
+
+            if (mIsMultiChoice) {
+                if (mCursor == null) {
+                    adapter = new ArrayAdapter<CharSequence>(
+                            mContext, dialog.mMultiChoiceItemLayout, android.R.id.text1, mItems) {
+                        @Override
+                        public View getView(int position, View convertView, ViewGroup parent) {
+                            View view = super.getView(position, convertView, parent);
+                            if (mCheckedItems != null) {
+                                boolean isItemChecked = mCheckedItems[position];
+                                if (isItemChecked) {
+                                    listView.setItemChecked(position, true);
+                                }
+                            }
+                            return view;
+                        }
+                    };
+                } else {
+                    adapter = new CursorAdapter(mContext, mCursor, false) {
+                        private final int mLabelIndex;
+                        private final int mIsCheckedIndex;
+
+                        {
+                            final Cursor cursor = getCursor();
+                            mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn);
+                            mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn);
+                        }
+
+                        @Override
+                        public void bindView(View view, Context context, Cursor cursor) {
+                            CheckedTextView text = (CheckedTextView) view
+                                    .findViewById(android.R.id.text1);
+                            text.setText(cursor.getString(mLabelIndex));
+                            listView.setItemChecked(cursor.getPosition(),
+                                    cursor.getInt(mIsCheckedIndex) == 1);
+                        }
+
+                        @Override
+                        public View newView(Context context, Cursor cursor, ViewGroup parent) {
+                            return mInflater.inflate(dialog.mMultiChoiceItemLayout,
+                                    parent, false);
+                        }
+
+                    };
+                }
+            } else {
+                int layout = mIsSingleChoice
+                        ? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout;
+                if (mCursor == null) {
+                    adapter = (mAdapter != null) ? mAdapter
+                            : new CheckedItemAdapter(mContext, layout, android.R.id.text1, mItems);
+                } else {
+                    adapter = new SimpleCursorAdapter(mContext, layout,
+                            mCursor, new String[]{mLabelColumn}, new int[]{android.R.id.text1});
+                }
+            }
+
+            if (mOnPrepareListViewListener != null) {
+                mOnPrepareListViewListener.onPrepareListView(listView);
+            }
+
+            /* Don't directly set the adapter on the ListView as we might
+             * want to add a footer to the ListView later.
+             */
+            dialog.mAdapter = adapter;
+            dialog.mCheckedItem = mCheckedItem;
+
+            if (mOnClickListener != null) {
+                listView.setOnItemClickListener(new OnItemClickListener() {
+                    @Override
+                    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+                        mOnClickListener.onClick(dialog.mDialog, position);
+                        if (!mIsSingleChoice) {
+                            dialog.mDialog.dismiss();
+                        }
+                    }
+                });
+            } else if (mOnCheckboxClickListener != null) {
+                listView.setOnItemClickListener(new OnItemClickListener() {
+                    @Override
+                    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+                        if (mCheckedItems != null) {
+                            mCheckedItems[position] = listView.isItemChecked(position);
+                        }
+                        mOnCheckboxClickListener.onClick(
+                                dialog.mDialog, position, listView.isItemChecked(position));
+                    }
+                });
+            }
+
+            // Attach a given OnItemSelectedListener to the ListView
+            if (mOnItemSelectedListener != null) {
+                listView.setOnItemSelectedListener(mOnItemSelectedListener);
+            }
+
+            if (mIsSingleChoice) {
+                listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+            } else if (mIsMultiChoice) {
+                listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+            }
+            dialog.mListView = listView;
+        }
+    }
+
+    private static class CheckedItemAdapter extends ArrayAdapter<CharSequence> {
+        public CheckedItemAdapter(Context context, int resource, int textViewResourceId,
+                CharSequence[] objects) {
+            super(context, resource, textViewResourceId, objects);
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return true;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/app/AlertDialog.java b/v7/appcompat/src/android/support/v7/app/AlertDialog.java
new file mode 100644
index 0000000..a301242
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/app/AlertDialog.java
@@ -0,0 +1,907 @@
+/*
+ * 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.v7.app;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Message;
+import android.support.v7.appcompat.R;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+/**
+ * A subclass of Dialog that can display one, two or three buttons. If you only want to
+ * display a String in this dialog box, use the setMessage() method.  If you
+ * want to display a more complex view, look up the FrameLayout called "custom"
+ * and add your view to it:
+ *
+ * <pre>
+ * FrameLayout fl = (FrameLayout) findViewById(android.R.id.custom);
+ * fl.addView(myView, new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ * </pre>
+ *
+ * <p>The AlertDialog class takes care of automatically setting
+ * {@link WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
+ * WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} for you based on whether
+ * any views in the dialog return true from {@link View#onCheckIsTextEditor()
+ * View.onCheckIsTextEditor()}.  Generally you want this set for a Dialog
+ * without text editors, so that it will be placed on top of the current
+ * input method UI.  You can modify this behavior by forcing the flag to your
+ * desired mode after calling {@link #onCreate}.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about creating dialogs, read the
+ * <a href="{@docRoot}guide/topics/ui/dialogs.html">Dialogs</a> developer guide.</p>
+ * </div>
+ */
+public class AlertDialog extends AppCompatDialog implements DialogInterface {
+
+    private AlertController mAlert;
+
+    /**
+     * No layout hint.
+     */
+    static final int LAYOUT_HINT_NONE = 0;
+
+    /**
+     * Hint layout to the side.
+     */
+    static final int LAYOUT_HINT_SIDE = 1;
+
+    protected AlertDialog(Context context) {
+        this(context, resolveDialogTheme(context, 0), true);
+    }
+
+    /**
+     * Construct an AlertDialog that uses an explicit theme.  The actual style
+     * that an AlertDialog uses is a private implementation, however you can
+     * here supply either the name of an attribute in the theme from which
+     * to get the dialog's style (such as {@link android.R.attr#alertDialogTheme}.
+     */
+    protected AlertDialog(Context context, int theme) {
+        this(context, theme, true);
+    }
+
+    AlertDialog(Context context, int theme, boolean createThemeContextWrapper) {
+        super(context, resolveDialogTheme(context, theme));
+        mAlert = new AlertController(getContext(), this, getWindow());
+    }
+
+    protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
+        super(context, resolveDialogTheme(context, 0));
+        setCancelable(cancelable);
+        setOnCancelListener(cancelListener);
+        mAlert = new AlertController(context, this, getWindow());
+    }
+
+    static int resolveDialogTheme(Context context, int resid) {
+        if (resid >= 0x01000000) {   // start of real resource IDs.
+            return resid;
+        } else {
+            TypedValue outValue = new TypedValue();
+            context.getTheme().resolveAttribute(R.attr.alertDialogTheme, outValue, true);
+            return outValue.resourceId;
+        }
+    }
+
+    /**
+     * Gets one of the buttons used in the dialog. Returns null if the specified
+     * button does not exist or the dialog has not yet been fully created (for
+     * example, via {@link #show()} or {@link #create()}).
+     *
+     * @param whichButton The identifier of the button that should be returned.
+     *                    For example, this can be
+     *                    {@link DialogInterface#BUTTON_POSITIVE}.
+     * @return The button from the dialog, or null if a button does not exist.
+     */
+    public Button getButton(int whichButton) {
+        return mAlert.getButton(whichButton);
+    }
+
+    /**
+     * Gets the list view used in the dialog.
+     *
+     * @return The {@link ListView} from the dialog.
+     */
+    public ListView getListView() {
+        return mAlert.getListView();
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        super.setTitle(title);
+        mAlert.setTitle(title);
+    }
+
+    /**
+     * @see Builder#setCustomTitle(View)
+     */
+    public void setCustomTitle(View customTitleView) {
+        mAlert.setCustomTitle(customTitleView);
+    }
+
+    public void setMessage(CharSequence message) {
+        mAlert.setMessage(message);
+    }
+
+    /**
+     * Set the view to display in that dialog.
+     */
+    public void setView(View view) {
+        mAlert.setView(view);
+    }
+
+    /**
+     * Set the view to display in that dialog, specifying the spacing to appear around that
+     * view.
+     *
+     * @param view              The view to show in the content area of the dialog
+     * @param viewSpacingLeft   Extra space to appear to the left of {@code view}
+     * @param viewSpacingTop    Extra space to appear above {@code view}
+     * @param viewSpacingRight  Extra space to appear to the right of {@code view}
+     * @param viewSpacingBottom Extra space to appear below {@code view}
+     */
+    public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
+            int viewSpacingBottom) {
+        mAlert.setView(view, viewSpacingLeft, viewSpacingTop, viewSpacingRight, viewSpacingBottom);
+    }
+
+    /**
+     * Internal api to allow hinting for the best button panel layout.
+     *
+     * @hide
+     */
+    void setButtonPanelLayoutHint(int layoutHint) {
+        mAlert.setButtonPanelLayoutHint(layoutHint);
+    }
+
+    /**
+     * Set a message to be sent when a button is pressed.
+     *
+     * @param whichButton Which button to set the message for, can be one of
+     *                    {@link DialogInterface#BUTTON_POSITIVE},
+     *                    {@link DialogInterface#BUTTON_NEGATIVE}, or
+     *                    {@link DialogInterface#BUTTON_NEUTRAL}
+     * @param text        The text to display in positive button.
+     * @param msg         The {@link Message} to be sent when clicked.
+     */
+    public void setButton(int whichButton, CharSequence text, Message msg) {
+        mAlert.setButton(whichButton, text, null, msg);
+    }
+
+    /**
+     * Set a listener to be invoked when the positive button of the dialog is pressed.
+     *
+     * @param whichButton Which button to set the listener on, can be one of
+     *                    {@link DialogInterface#BUTTON_POSITIVE},
+     *                    {@link DialogInterface#BUTTON_NEGATIVE}, or
+     *                    {@link DialogInterface#BUTTON_NEUTRAL}
+     * @param text        The text to display in positive button.
+     * @param listener    The {@link DialogInterface.OnClickListener} to use.
+     */
+    public void setButton(int whichButton, CharSequence text, OnClickListener listener) {
+        mAlert.setButton(whichButton, text, listener, null);
+    }
+
+    /**
+     * Set resId to 0 if you don't want an icon.
+     *
+     * @param resId the resourceId of the drawable to use as the icon or 0
+     *              if you don't want an icon.
+     */
+    public void setIcon(int resId) {
+        mAlert.setIcon(resId);
+    }
+
+    public void setIcon(Drawable icon) {
+        mAlert.setIcon(icon);
+    }
+
+    /**
+     * Set an icon as supplied by a theme attribute. e.g. android.R.attr.alertDialogIcon
+     *
+     * @param attrId ID of a theme attribute that points to a drawable resource.
+     */
+    public void setIconAttribute(int attrId) {
+        TypedValue out = new TypedValue();
+        getContext().getTheme().resolveAttribute(attrId, out, true);
+        mAlert.setIcon(out.resourceId);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mAlert.installContent();
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (mAlert.onKeyDown(keyCode, event)) {
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (mAlert.onKeyUp(keyCode, event)) {
+            return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    public static class Builder {
+
+        private final AlertController.AlertParams P;
+
+        private int mTheme;
+
+        /**
+         * Constructor using a context for this builder and the {@link AlertDialog} it creates.
+         */
+        public Builder(Context context) {
+            this(context, resolveDialogTheme(context, 0));
+        }
+
+        /**
+         * Constructor using a context and theme for this builder and
+         * the {@link AlertDialog} it creates.  The actual theme
+         * that an AlertDialog uses is a private implementation, however you can
+         * here supply either the name of an attribute in the theme from which
+         * to get the dialog's style (such as {@link android.R.attr#alertDialogTheme}.
+         */
+        public Builder(Context context, int theme) {
+            P = new AlertController.AlertParams(new ContextThemeWrapper(
+                    context, resolveDialogTheme(context, theme)));
+            mTheme = theme;
+        }
+
+        /**
+         * Returns a {@link Context} with the appropriate theme for dialogs created by this
+         * Builder.
+         * Applications should use this Context for obtaining LayoutInflaters for inflating views
+         * that will be used in the resulting dialogs, as it will cause views to be inflated with
+         * the correct theme.
+         *
+         * @return A Context for built Dialogs.
+         */
+        public Context getContext() {
+            return P.mContext;
+        }
+
+        /**
+         * Set the title using the given resource id.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setTitle(int titleId) {
+            P.mTitle = P.mContext.getText(titleId);
+            return this;
+        }
+
+        /**
+         * Set the title displayed in the {@link Dialog}.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setTitle(CharSequence title) {
+            P.mTitle = title;
+            return this;
+        }
+
+        /**
+         * Set the title using the custom view {@code customTitleView}. The
+         * methods {@link #setTitle(int)} and {@link #setIcon(int)} should be
+         * sufficient for most titles, but this is provided if the title needs
+         * more customization. Using this will replace the title and icon set
+         * via the other methods.
+         *
+         * @param customTitleView The custom view to use as the title.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setCustomTitle(View customTitleView) {
+            P.mCustomTitleView = customTitleView;
+            return this;
+        }
+
+        /**
+         * Set the message to display using the given resource id.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setMessage(int messageId) {
+            P.mMessage = P.mContext.getText(messageId);
+            return this;
+        }
+
+        /**
+         * Set the message to display.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setMessage(CharSequence message) {
+            P.mMessage = message;
+            return this;
+        }
+
+        /**
+         * Set the resource id of the {@link Drawable} to be used in the title.
+         * <p>
+         * Takes precedence over values set using {@link #setIcon(Drawable)}.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setIcon(int iconId) {
+            P.mIconId = iconId;
+            return this;
+        }
+
+        /**
+         * Set the {@link Drawable} to be used in the title.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setIcon(Drawable icon) {
+            P.mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Set an icon as supplied by a theme attribute. e.g.
+         * {@link android.R.attr#alertDialogIcon}.
+         * <p>
+         * Takes precedence over values set using {@link #setIcon(int)} or
+         * {@link #setIcon(Drawable)}.
+         *
+         * @param attrId ID of a theme attribute that points to a drawable resource.
+         */
+        public Builder setIconAttribute(int attrId) {
+            TypedValue out = new TypedValue();
+            P.mContext.getTheme().resolveAttribute(attrId, out, true);
+            P.mIconId = out.resourceId;
+            return this;
+        }
+
+        /**
+         * Set a listener to be invoked when the positive button of the dialog is pressed.
+         *
+         * @param textId   The resource id of the text to display in the positive button
+         * @param listener The {@link DialogInterface.OnClickListener} to use.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setPositiveButton(int textId, final OnClickListener listener) {
+            P.mPositiveButtonText = P.mContext.getText(textId);
+            P.mPositiveButtonListener = listener;
+            return this;
+        }
+
+        /**
+         * Set a listener to be invoked when the positive button of the dialog is pressed.
+         *
+         * @param text     The text to display in the positive button
+         * @param listener The {@link DialogInterface.OnClickListener} to use.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
+            P.mPositiveButtonText = text;
+            P.mPositiveButtonListener = listener;
+            return this;
+        }
+
+        /**
+         * Set a listener to be invoked when the negative button of the dialog is pressed.
+         *
+         * @param textId   The resource id of the text to display in the negative button
+         * @param listener The {@link DialogInterface.OnClickListener} to use.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setNegativeButton(int textId, final OnClickListener listener) {
+            P.mNegativeButtonText = P.mContext.getText(textId);
+            P.mNegativeButtonListener = listener;
+            return this;
+        }
+
+        /**
+         * Set a listener to be invoked when the negative button of the dialog is pressed.
+         *
+         * @param text     The text to display in the negative button
+         * @param listener The {@link DialogInterface.OnClickListener} to use.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setNegativeButton(CharSequence text, final OnClickListener listener) {
+            P.mNegativeButtonText = text;
+            P.mNegativeButtonListener = listener;
+            return this;
+        }
+
+        /**
+         * Set a listener to be invoked when the neutral button of the dialog is pressed.
+         *
+         * @param textId   The resource id of the text to display in the neutral button
+         * @param listener The {@link DialogInterface.OnClickListener} to use.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setNeutralButton(int textId, final OnClickListener listener) {
+            P.mNeutralButtonText = P.mContext.getText(textId);
+            P.mNeutralButtonListener = listener;
+            return this;
+        }
+
+        /**
+         * Set a listener to be invoked when the neutral button of the dialog is pressed.
+         *
+         * @param text     The text to display in the neutral button
+         * @param listener The {@link DialogInterface.OnClickListener} to use.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setNeutralButton(CharSequence text, final OnClickListener listener) {
+            P.mNeutralButtonText = text;
+            P.mNeutralButtonListener = listener;
+            return this;
+        }
+
+        /**
+         * Sets whether the dialog is cancelable or not.  Default is true.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setCancelable(boolean cancelable) {
+            P.mCancelable = cancelable;
+            return this;
+        }
+
+        /**
+         * Sets the callback that will be called if the dialog is canceled.
+         *
+         * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than
+         * being canceled or one of the supplied choices being selected.
+         * If you are interested in listening for all cases where the dialog is dismissed
+         * and not just when it is canceled, see
+         * {@link #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
+         * setOnDismissListener}.</p>
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         * @see #setCancelable(boolean)
+         * @see #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
+         */
+        public Builder setOnCancelListener(OnCancelListener onCancelListener) {
+            P.mOnCancelListener = onCancelListener;
+            return this;
+        }
+
+        /**
+         * Sets the callback that will be called when the dialog is dismissed for any reason.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setOnDismissListener(OnDismissListener onDismissListener) {
+            P.mOnDismissListener = onDismissListener;
+            return this;
+        }
+
+        /**
+         * Sets the callback that will be called if a key is dispatched to the dialog.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setOnKeyListener(OnKeyListener onKeyListener) {
+            P.mOnKeyListener = onKeyListener;
+            return this;
+        }
+
+        /**
+         * Set a list of items to be displayed in the dialog as the content, you will be notified of
+         * the
+         * selected item via the supplied listener. This should be an array type i.e. R.array.foo
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setItems(int itemsId, final OnClickListener listener) {
+            P.mItems = P.mContext.getResources().getTextArray(itemsId);
+            P.mOnClickListener = listener;
+            return this;
+        }
+
+        /**
+         * Set a list of items to be displayed in the dialog as the content, you will be notified of
+         * the
+         * selected item via the supplied listener.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setItems(CharSequence[] items, final OnClickListener listener) {
+            P.mItems = items;
+            P.mOnClickListener = listener;
+            return this;
+        }
+
+        /**
+         * Set a list of items, which are supplied by the given {@link ListAdapter}, to be
+         * displayed in the dialog as the content, you will be notified of the
+         * selected item via the supplied listener.
+         *
+         * @param adapter  The {@link ListAdapter} to supply the list of items
+         * @param listener The listener that will be called when an item is clicked.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setAdapter(final ListAdapter adapter, final OnClickListener listener) {
+            P.mAdapter = adapter;
+            P.mOnClickListener = listener;
+            return this;
+        }
+
+        /**
+         * Set a list of items, which are supplied by the given {@link Cursor}, to be
+         * displayed in the dialog as the content, you will be notified of the
+         * selected item via the supplied listener.
+         *
+         * @param cursor      The {@link Cursor} to supply the list of items
+         * @param listener    The listener that will be called when an item is clicked.
+         * @param labelColumn The column name on the cursor containing the string to display
+         *                    in the label.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setCursor(final Cursor cursor, final OnClickListener listener,
+                String labelColumn) {
+            P.mCursor = cursor;
+            P.mLabelColumn = labelColumn;
+            P.mOnClickListener = listener;
+            return this;
+        }
+
+        /**
+         * Set a list of items to be displayed in the dialog as the content,
+         * you will be notified of the selected item via the supplied listener.
+         * This should be an array type, e.g. R.array.foo. The list will have
+         * a check mark displayed to the right of the text for each checked
+         * item. Clicking on an item in the list will not dismiss the dialog.
+         * Clicking on a button will dismiss the dialog.
+         *
+         * @param itemsId      the resource id of an array i.e. R.array.foo
+         * @param checkedItems specifies which items are checked. It should be null in which case
+         *                     no
+         *                     items are checked. If non null it must be exactly the same length as
+         *                     the array of
+         *                     items.
+         * @param listener     notified when an item on the list is clicked. The dialog will not be
+         *                     dismissed when an item is clicked. It will only be dismissed if
+         *                     clicked on a
+         *                     button, if no buttons are supplied it's up to the user to dismiss the
+         *                     dialog.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setMultiChoiceItems(int itemsId, boolean[] checkedItems,
+                final OnMultiChoiceClickListener listener) {
+            P.mItems = P.mContext.getResources().getTextArray(itemsId);
+            P.mOnCheckboxClickListener = listener;
+            P.mCheckedItems = checkedItems;
+            P.mIsMultiChoice = true;
+            return this;
+        }
+
+        /**
+         * Set a list of items to be displayed in the dialog as the content,
+         * you will be notified of the selected item via the supplied listener.
+         * The list will have a check mark displayed to the right of the text
+         * for each checked item. Clicking on an item in the list will not
+         * dismiss the dialog. Clicking on a button will dismiss the dialog.
+         *
+         * @param items        the text of the items to be displayed in the list.
+         * @param checkedItems specifies which items are checked. It should be null in which case
+         *                     no
+         *                     items are checked. If non null it must be exactly the same length as
+         *                     the array of
+         *                     items.
+         * @param listener     notified when an item on the list is clicked. The dialog will not be
+         *                     dismissed when an item is clicked. It will only be dismissed if
+         *                     clicked on a
+         *                     button, if no buttons are supplied it's up to the user to dismiss the
+         *                     dialog.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems,
+                final OnMultiChoiceClickListener listener) {
+            P.mItems = items;
+            P.mOnCheckboxClickListener = listener;
+            P.mCheckedItems = checkedItems;
+            P.mIsMultiChoice = true;
+            return this;
+        }
+
+        /**
+         * Set a list of items to be displayed in the dialog as the content,
+         * you will be notified of the selected item via the supplied listener.
+         * The list will have a check mark displayed to the right of the text
+         * for each checked item. Clicking on an item in the list will not
+         * dismiss the dialog. Clicking on a button will dismiss the dialog.
+         *
+         * @param cursor          the cursor used to provide the items.
+         * @param isCheckedColumn specifies the column name on the cursor to use to determine
+         *                        whether a checkbox is checked or not. It must return an integer
+         *                        value where 1
+         *                        means checked and 0 means unchecked.
+         * @param labelColumn     The column name on the cursor containing the string to display in
+         *                        the
+         *                        label.
+         * @param listener        notified when an item on the list is clicked. The dialog will not
+         *                        be
+         *                        dismissed when an item is clicked. It will only be dismissed if
+         *                        clicked on a
+         *                        button, if no buttons are supplied it's up to the user to dismiss
+         *                        the dialog.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setMultiChoiceItems(Cursor cursor, String isCheckedColumn,
+                String labelColumn,
+                final OnMultiChoiceClickListener listener) {
+            P.mCursor = cursor;
+            P.mOnCheckboxClickListener = listener;
+            P.mIsCheckedColumn = isCheckedColumn;
+            P.mLabelColumn = labelColumn;
+            P.mIsMultiChoice = true;
+            return this;
+        }
+
+        /**
+         * Set a list of items to be displayed in the dialog as the content, you will be notified
+         * of
+         * the selected item via the supplied listener. This should be an array type i.e.
+         * R.array.foo The list will have a check mark displayed to the right of the text for the
+         * checked item. Clicking on an item in the list will not dismiss the dialog. Clicking on a
+         * button will dismiss the dialog.
+         *
+         * @param itemsId     the resource id of an array i.e. R.array.foo
+         * @param checkedItem specifies which item is checked. If -1 no items are checked.
+         * @param listener    notified when an item on the list is clicked. The dialog will not be
+         *                    dismissed when an item is clicked. It will only be dismissed if
+         *                    clicked on a
+         *                    button, if no buttons are supplied it's up to the user to dismiss the
+         *                    dialog.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setSingleChoiceItems(int itemsId, int checkedItem,
+                final OnClickListener listener) {
+            P.mItems = P.mContext.getResources().getTextArray(itemsId);
+            P.mOnClickListener = listener;
+            P.mCheckedItem = checkedItem;
+            P.mIsSingleChoice = true;
+            return this;
+        }
+
+        /**
+         * Set a list of items to be displayed in the dialog as the content, you will be notified
+         * of
+         * the selected item via the supplied listener. The list will have a check mark displayed
+         * to
+         * the right of the text for the checked item. Clicking on an item in the list will not
+         * dismiss the dialog. Clicking on a button will dismiss the dialog.
+         *
+         * @param cursor      the cursor to retrieve the items from.
+         * @param checkedItem specifies which item is checked. If -1 no items are checked.
+         * @param labelColumn The column name on the cursor containing the string to display in the
+         *                    label.
+         * @param listener    notified when an item on the list is clicked. The dialog will not be
+         *                    dismissed when an item is clicked. It will only be dismissed if
+         *                    clicked on a
+         *                    button, if no buttons are supplied it's up to the user to dismiss the
+         *                    dialog.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setSingleChoiceItems(Cursor cursor, int checkedItem, String labelColumn,
+                final OnClickListener listener) {
+            P.mCursor = cursor;
+            P.mOnClickListener = listener;
+            P.mCheckedItem = checkedItem;
+            P.mLabelColumn = labelColumn;
+            P.mIsSingleChoice = true;
+            return this;
+        }
+
+        /**
+         * Set a list of items to be displayed in the dialog as the content, you will be notified
+         * of
+         * the selected item via the supplied listener. The list will have a check mark displayed
+         * to
+         * the right of the text for the checked item. Clicking on an item in the list will not
+         * dismiss the dialog. Clicking on a button will dismiss the dialog.
+         *
+         * @param items       the items to be displayed.
+         * @param checkedItem specifies which item is checked. If -1 no items are checked.
+         * @param listener    notified when an item on the list is clicked. The dialog will not be
+         *                    dismissed when an item is clicked. It will only be dismissed if
+         *                    clicked on a
+         *                    button, if no buttons are supplied it's up to the user to dismiss the
+         *                    dialog.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setSingleChoiceItems(CharSequence[] items, int checkedItem,
+                final OnClickListener listener) {
+            P.mItems = items;
+            P.mOnClickListener = listener;
+            P.mCheckedItem = checkedItem;
+            P.mIsSingleChoice = true;
+            return this;
+        }
+
+        /**
+         * Set a list of items to be displayed in the dialog as the content, you will be notified
+         * of
+         * the selected item via the supplied listener. The list will have a check mark displayed
+         * to
+         * the right of the text for the checked item. Clicking on an item in the list will not
+         * dismiss the dialog. Clicking on a button will dismiss the dialog.
+         *
+         * @param adapter     The {@link ListAdapter} to supply the list of items
+         * @param checkedItem specifies which item is checked. If -1 no items are checked.
+         * @param listener    notified when an item on the list is clicked. The dialog will not be
+         *                    dismissed when an item is clicked. It will only be dismissed if
+         *                    clicked on a
+         *                    button, if no buttons are supplied it's up to the user to dismiss the
+         *                    dialog.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setSingleChoiceItems(ListAdapter adapter, int checkedItem,
+                final OnClickListener listener) {
+            P.mAdapter = adapter;
+            P.mOnClickListener = listener;
+            P.mCheckedItem = checkedItem;
+            P.mIsSingleChoice = true;
+            return this;
+        }
+
+        /**
+         * Sets a listener to be invoked when an item in the list is selected.
+         *
+         * @param listener The listener to be invoked.
+         * @return This Builder object to allow for chaining of calls to set methods
+         * @see AdapterView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
+         */
+        public Builder setOnItemSelectedListener(
+                final AdapterView.OnItemSelectedListener listener) {
+            P.mOnItemSelectedListener = listener;
+            return this;
+        }
+
+        /**
+         * Set a custom view resource to be the contents of the Dialog. The
+         * resource will be inflated, adding all top-level views to the screen.
+         *
+         * @param layoutResId Resource ID to be inflated.
+         * @return This Builder object to allow for chaining of calls to set
+         * methods
+         */
+        public Builder setView(int layoutResId) {
+            P.mView = null;
+            P.mViewLayoutResId = layoutResId;
+            P.mViewSpacingSpecified = false;
+            return this;
+        }
+
+        /**
+         * Set a custom view to be the contents of the Dialog. If the supplied view is an instance
+         * of a {@link ListView} the light background will be used.
+         *
+         * @param view The view to use as the contents of the Dialog.
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setView(View view) {
+            P.mView = view;
+            P.mViewLayoutResId = 0;
+            P.mViewSpacingSpecified = false;
+            return this;
+        }
+
+        /**
+         * Set a custom view to be the contents of the Dialog, specifying the
+         * spacing to appear around that view. If the supplied view is an
+         * instance of a {@link ListView} the light background will be used.
+         *
+         * @param view              The view to use as the contents of the Dialog.
+         * @param viewSpacingLeft   Spacing between the left edge of the view and
+         *                          the dialog frame
+         * @param viewSpacingTop    Spacing between the top edge of the view and
+         *                          the dialog frame
+         * @param viewSpacingRight  Spacing between the right edge of the view
+         *                          and the dialog frame
+         * @param viewSpacingBottom Spacing between the bottom edge of the view
+         *                          and the dialog frame
+         * @return This Builder object to allow for chaining of calls to set
+         * methods
+         *
+         *
+         * This is currently hidden because it seems like people should just
+         * be able to put padding around the view.
+         * @hide
+         */
+        public Builder setView(View view, int viewSpacingLeft, int viewSpacingTop,
+                int viewSpacingRight, int viewSpacingBottom) {
+            P.mView = view;
+            P.mViewLayoutResId = 0;
+            P.mViewSpacingSpecified = true;
+            P.mViewSpacingLeft = viewSpacingLeft;
+            P.mViewSpacingTop = viewSpacingTop;
+            P.mViewSpacingRight = viewSpacingRight;
+            P.mViewSpacingBottom = viewSpacingBottom;
+            return this;
+        }
+
+        /**
+         * Sets the Dialog to use the inverse background, regardless of what the
+         * contents is.
+         *
+         * @param useInverseBackground Whether to use the inverse background
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setInverseBackgroundForced(boolean useInverseBackground) {
+            P.mForceInverseBackground = useInverseBackground;
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        public Builder setRecycleOnMeasureEnabled(boolean enabled) {
+            P.mRecycleOnMeasure = enabled;
+            return this;
+        }
+
+
+        /**
+         * Creates a {@link AlertDialog} with the arguments supplied to this builder. It does not
+         * {@link Dialog#show()} the dialog. This allows the user to do any extra processing
+         * before displaying the dialog. Use {@link #show()} if you don't have any other processing
+         * to do and want this to be created and displayed.
+         */
+        public AlertDialog create() {
+            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
+            P.apply(dialog.mAlert);
+            dialog.setCancelable(P.mCancelable);
+            if (P.mCancelable) {
+                dialog.setCanceledOnTouchOutside(true);
+            }
+            dialog.setOnCancelListener(P.mOnCancelListener);
+            dialog.setOnDismissListener(P.mOnDismissListener);
+            if (P.mOnKeyListener != null) {
+                dialog.setOnKeyListener(P.mOnKeyListener);
+            }
+            return dialog;
+        }
+
+        /**
+         * Creates a {@link AlertDialog} with the arguments supplied to this builder and
+         * {@link Dialog#show()}'s the dialog.
+         */
+        public AlertDialog show() {
+            AlertDialog dialog = create();
+            dialog.show();
+            return dialog;
+        }
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
new file mode 100644
index 0000000..dfd138d
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -0,0 +1,421 @@
+/*
+ * 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.v7.app;
+
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.support.annotation.CallSuper;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.NavUtils;
+import android.support.v4.app.TaskStackBuilder;
+import android.support.v7.view.ActionMode;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Base class for activities that use the
+ * <a href="{@docRoot}tools/extras/support-library.html">support library</a> action bar features.
+ *
+ * <p>You can add an {@link android.support.v7.app.ActionBar} to your activity when running on API level 7 or higher
+ * by extending this class for your activity and setting the activity theme to
+ * {@link android.support.v7.appcompat.R.style#Theme_AppCompat Theme.AppCompat} or a similar theme.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ *
+ * <p>For information about how to use the action bar, including how to add action items, navigation
+ * modes and more, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action
+ * Bar</a> API guide.</p>
+ * </div>
+ */
+public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
+        TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
+
+    private AppCompatDelegate mDelegate;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        getDelegate().installViewFactory();
+        super.onCreate(savedInstanceState);
+        getDelegate().onCreate(savedInstanceState);
+    }
+
+    @Override
+    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+        getDelegate().onPostCreate(savedInstanceState);
+    }
+
+    /**
+     * Support library version of {@link android.app.Activity#getActionBar}.
+     *
+     * <p>Retrieve a reference to this activity's ActionBar.
+     *
+     * @return The Activity's ActionBar, or null if it does not have one.
+     */
+    @Nullable
+    public ActionBar getSupportActionBar() {
+        return getDelegate().getSupportActionBar();
+    }
+
+    /**
+     * Set a {@link android.widget.Toolbar Toolbar} to act as the {@link android.support.v7.app.ActionBar} for this
+     * Activity window.
+     *
+     * <p>When set to a non-null value the {@link #getActionBar()} method will return
+     * an {@link android.support.v7.app.ActionBar} object that can be used to control the given toolbar as if it were
+     * a traditional window decor action bar. The toolbar's menu will be populated with the
+     * Activity's options menu and the navigation button will be wired through the standard
+     * {@link android.R.id#home home} menu select action.</p>
+     *
+     * <p>In order to use a Toolbar within the Activity's window content the application
+     * must not request the window feature {@link android.view.Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p>
+     *
+     * @param toolbar Toolbar to set as the Activity's action bar
+     */
+    public void setSupportActionBar(@Nullable Toolbar toolbar) {
+        getDelegate().setSupportActionBar(toolbar);
+    }
+
+    @Override
+    public MenuInflater getMenuInflater() {
+        return getDelegate().getMenuInflater();
+    }
+
+    @Override
+    public void setContentView(@LayoutRes int layoutResID) {
+        getDelegate().setContentView(layoutResID);
+    }
+
+    @Override
+    public void setContentView(View view) {
+        getDelegate().setContentView(view);
+    }
+
+    @Override
+    public void setContentView(View view, ViewGroup.LayoutParams params) {
+        getDelegate().setContentView(view, params);
+    }
+
+    @Override
+    public void addContentView(View view, ViewGroup.LayoutParams params) {
+        getDelegate().addContentView(view, params);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        getDelegate().onConfigurationChanged(newConfig);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        getDelegate().onStop();
+    }
+
+    @Override
+    protected void onPostResume() {
+        super.onPostResume();
+        getDelegate().onPostResume();
+    }
+
+    @Override
+    public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) {
+        if (super.onMenuItemSelected(featureId, item)) {
+            return true;
+        }
+
+        final ActionBar ab = getSupportActionBar();
+        if (item.getItemId() == android.R.id.home && ab != null &&
+                (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+            return onSupportNavigateUp();
+        }
+        return false;
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        getDelegate().onDestroy();
+    }
+
+    @Override
+    protected void onTitleChanged(CharSequence title, int color) {
+        super.onTitleChanged(title, color);
+        getDelegate().setTitle(title);
+    }
+
+    /**
+     * Enable extended support library window features.
+     * <p>
+     * This is a convenience for calling
+     * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
+     * </p>
+     *
+     * @param featureId The desired feature as defined in
+     * {@link android.view.Window} or {@link android.support.v4.view.WindowCompat}.
+     * @return Returns true if the requested feature is supported and now enabled.
+     *
+     * @see android.app.Activity#requestWindowFeature
+     * @see android.view.Window#requestFeature
+     */
+    public boolean supportRequestWindowFeature(int featureId) {
+        return getDelegate().requestWindowFeature(featureId);
+    }
+
+    @Override
+    public void supportInvalidateOptionsMenu() {
+        getDelegate().invalidateOptionsMenu();
+    }
+
+    /**
+     * @hide
+     */
+    public void invalidateOptionsMenu() {
+        getDelegate().invalidateOptionsMenu();
+    }
+
+    /**
+     * Notifies the Activity that a support action mode has been started.
+     * Activity subclasses overriding this method should call the superclass implementation.
+     *
+     * @param mode The new action mode.
+     */
+    @CallSuper
+    public void onSupportActionModeStarted(ActionMode mode) {
+    }
+
+    /**
+     * Notifies the activity that a support action mode has finished.
+     * Activity subclasses overriding this method should call the superclass implementation.
+     *
+     * @param mode The action mode that just finished.
+     */
+    @CallSuper
+    public void onSupportActionModeFinished(ActionMode mode) {
+    }
+
+    public ActionMode startSupportActionMode(ActionMode.Callback callback) {
+        return getDelegate().startSupportActionMode(callback);
+    }
+
+    /**
+     * @deprecated Progress bars are no longer provided in AppCompat.
+     */
+    @Deprecated
+    public void setSupportProgressBarVisibility(boolean visible) {
+    }
+
+    /**
+     * @deprecated Progress bars are no longer provided in AppCompat.
+     */
+    @Deprecated
+    public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
+    }
+
+    /**
+     * @deprecated Progress bars are no longer provided in AppCompat.
+     */
+    @Deprecated
+    public void setSupportProgressBarIndeterminate(boolean indeterminate) {
+    }
+
+    /**
+     * @deprecated Progress bars are no longer provided in AppCompat.
+     */
+    @Deprecated
+    public void setSupportProgress(int progress) {
+    }
+
+    /**
+     * Support version of {@link #onCreateNavigateUpTaskStack(android.app.TaskStackBuilder)}.
+     * This method will be called on all platform versions.
+     *
+     * Define the synthetic task stack that will be generated during Up navigation from
+     * a different task.
+     *
+     * <p>The default implementation of this method adds the parent chain of this activity
+     * as specified in the manifest to the supplied {@link android.support.v4.app.TaskStackBuilder}. Applications
+     * may choose to override this method to construct the desired task stack in a different
+     * way.</p>
+     *
+     * <p>This method will be invoked by the default implementation of {@link #onNavigateUp()}
+     * if {@link #shouldUpRecreateTask(android.content.Intent)} returns true when supplied with the intent
+     * returned by {@link #getParentActivityIntent()}.</p>
+     *
+     * <p>Applications that wish to supply extra Intent parameters to the parent stack defined
+     * by the manifest should override
+     * {@link #onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}.</p>
+     *
+     * @param builder An empty TaskStackBuilder - the application should add intents representing
+     *                the desired task stack
+     */
+    public void onCreateSupportNavigateUpTaskStack(TaskStackBuilder builder) {
+        builder.addParentStack(this);
+    }
+
+    /**
+     * Support version of {@link #onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder)}.
+     * This method will be called on all platform versions.
+     *
+     * Prepare the synthetic task stack that will be generated during Up navigation
+     * from a different task.
+     *
+     * <p>This method receives the {@link android.support.v4.app.TaskStackBuilder} with the constructed series of
+     * Intents as generated by {@link #onCreateSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}.
+     * If any extra data should be added to these intents before launching the new task,
+     * the application should override this method and add that data here.</p>
+     *
+     * @param builder A TaskStackBuilder that has been populated with Intents by
+     *                onCreateNavigateUpTaskStack.
+     */
+    public void onPrepareSupportNavigateUpTaskStack(TaskStackBuilder builder) {
+    }
+
+    /**
+     * This method is called whenever the user chooses to navigate Up within your application's
+     * activity hierarchy from the action bar.
+     *
+     * <p>If a parent was specified in the manifest for this activity or an activity-alias to it,
+     * default Up navigation will be handled automatically. See
+     * {@link #getSupportParentActivityIntent()} for how to specify the parent. If any activity
+     * along the parent chain requires extra Intent arguments, the Activity subclass
+     * should override the method {@link #onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}
+     * to supply those arguments.</p>
+     *
+     * <p>See <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and
+     * Back Stack</a> from the developer guide and
+     * <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> from the design guide
+     * for more information about navigating within your app.</p>
+     *
+     * <p>See the {@link android.support.v4.app.TaskStackBuilder} class and the Activity methods
+     * {@link #getSupportParentActivityIntent()}, {@link #supportShouldUpRecreateTask(android.content.Intent)}, and
+     * {@link #supportNavigateUpTo(android.content.Intent)} for help implementing custom Up navigation.</p>
+     *
+     * @return true if Up navigation completed successfully and this Activity was finished,
+     *         false otherwise.
+     */
+    public boolean onSupportNavigateUp() {
+        Intent upIntent = getSupportParentActivityIntent();
+
+        if (upIntent != null) {
+            if (supportShouldUpRecreateTask(upIntent)) {
+                TaskStackBuilder b = TaskStackBuilder.create(this);
+                onCreateSupportNavigateUpTaskStack(b);
+                onPrepareSupportNavigateUpTaskStack(b);
+                b.startActivities();
+
+                try {
+                    ActivityCompat.finishAffinity(this);
+                } catch (IllegalStateException e) {
+                    // This can only happen on 4.1+, when we don't have a parent or a result set.
+                    // In that case we should just finish().
+                    finish();
+                }
+            } else {
+                // This activity is part of the application's task, so simply
+                // navigate up to the hierarchical parent activity.
+                supportNavigateUpTo(upIntent);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Obtain an {@link android.content.Intent} that will launch an explicit target activity
+     * specified by sourceActivity's {@link android.support.v4.app.NavUtils#PARENT_ACTIVITY} &lt;meta-data&gt;
+     * element in the application's manifest. If the device is running
+     * Jellybean or newer, the android:parentActivityName attribute will be preferred
+     * if it is present.
+     *
+     * @return a new Intent targeting the defined parent activity of sourceActivity
+     */
+    @Nullable
+    public Intent getSupportParentActivityIntent() {
+        return NavUtils.getParentActivityIntent(this);
+    }
+
+    /**
+     * Returns true if sourceActivity should recreate the task when navigating 'up'
+     * by using targetIntent.
+     *
+     * <p>If this method returns false the app can trivially call
+     * {@link #supportNavigateUpTo(android.content.Intent)} using the same parameters to correctly perform
+     * up navigation. If this method returns false, the app should synthesize a new task stack
+     * by using {@link android.support.v4.app.TaskStackBuilder} or another similar mechanism to perform up navigation.</p>
+     *
+     * @param targetIntent An intent representing the target destination for up navigation
+     * @return true if navigating up should recreate a new task stack, false if the same task
+     *         should be used for the destination
+     */
+    public boolean supportShouldUpRecreateTask(Intent targetIntent) {
+        return NavUtils.shouldUpRecreateTask(this, targetIntent);
+    }
+
+    /**
+     * Navigate from sourceActivity to the activity specified by upIntent, finishing sourceActivity
+     * in the process. upIntent will have the flag {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} set
+     * by this method, along with any others required for proper up navigation as outlined
+     * in the Android Design Guide.
+     *
+     * <p>This method should be used when performing up navigation from within the same task
+     * as the destination. If up navigation should cross tasks in some cases, see
+     * {@link #supportShouldUpRecreateTask(android.content.Intent)}.</p>
+     *
+     * @param upIntent An intent representing the target destination for up navigation
+     */
+    public void supportNavigateUpTo(Intent upIntent) {
+        NavUtils.navigateUpTo(this, upIntent);
+    }
+
+    @Override
+    public void onContentChanged() {
+        // Call onSupportContentChanged() for legacy reasons
+        onSupportContentChanged();
+    }
+
+    /**
+     * @deprecated Use {@link #onContentChanged()} instead.
+     */
+    @Deprecated
+    public void onSupportContentChanged() {
+    }
+
+    @Nullable
+    @Override
+    public ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
+        return getDelegate().getDrawerToggleDelegate();
+    }
+
+    /**
+     * @return The {@link AppCompatDelegate} being used by this Activity.
+     */
+    public AppCompatDelegate getDelegate() {
+        if (mDelegate == null) {
+            mDelegate = AppCompatDelegate.create(this, this);
+        }
+        return mDelegate;
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatCallback.java b/v7/appcompat/src/android/support/v7/app/AppCompatCallback.java
new file mode 100644
index 0000000..dba77a2
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatCallback.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 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.v7.app;
+
+import android.support.v7.view.ActionMode;
+
+/**
+ * Implemented this in order for AppCompat to be able to callback in certain situations.
+ * <p>
+ * This should be provided to
+ * {@link AppCompatDelegate#create(android.app.Activity, AppCompatCallback)}.
+ */
+public interface AppCompatCallback {
+
+    /**
+     * Called when a support action mode has been started.
+     *
+     * @param mode The new action mode.
+     */
+    void onSupportActionModeStarted(ActionMode mode);
+
+    /**
+     * Called when a support action mode has finished.
+     *
+     * @param mode The action mode that just finished.
+     */
+    void onSupportActionModeFinished(ActionMode mode);
+
+}
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
new file mode 100644
index 0000000..ecb1178
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2014 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.v7.app;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentActivity;
+import android.support.v7.view.ActionMode;
+import android.support.v7.widget.Toolbar;
+import android.util.AttributeSet;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This class represents a delegate which you can use to extend AppCompat's support to any
+ * {@link android.app.Activity}.
+ * <p>
+ * When using an {@link AppCompatDelegate}, you should any methods exposed in it rather than the
+ * {@link android.app.Activity} method of the same name. This applies to:
+ * <ul>
+ *     <li>{@link #addContentView(android.view.View, android.view.ViewGroup.LayoutParams)}</li>
+ *     <li>{@link #setContentView(int)}</li>
+ *     <li>{@link #setContentView(android.view.View)}</li>
+ *     <li>{@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}</li>
+ *     <li>{@link #requestWindowFeature(int)}</li>
+ *     <li>{@link #invalidateOptionsMenu()}</li>
+ *     <li>{@link #startSupportActionMode(android.support.v7.view.ActionMode.Callback)}</li>
+ *     <li>{@link #setSupportActionBar(android.support.v7.widget.Toolbar)}</li>
+ *     <li>{@link #getSupportActionBar()}</li>
+ *     <li>{@link #getMenuInflater()}</li>
+ * </ul>
+ * There also some Activity lifecycle methods which should be proxied to the delegate:
+ * <ul>
+ *     <li>{@link #onCreate(android.os.Bundle)}</li>
+ *     <li>{@link #onPostCreate(android.os.Bundle)}</li>
+ *     <li>{@link #onConfigurationChanged(android.content.res.Configuration)}</li>
+ *     <li>{@link #setTitle(CharSequence)}</li>
+ *     <li>{@link #onStop()}</li>
+ *     <li>{@link #onDestroy()}</li>
+ * </ul>
+ * <p>
+ * An {@link Activity} can only be linked with one {@link AppCompatDelegate} instance,
+ * so the instance returned from {@link #create(Activity, AppCompatCallback)} should be kept
+ * until the Activity is destroyed.
+ */
+public abstract class AppCompatDelegate {
+
+    static final String TAG = "AppCompatDelegate";
+
+    /**
+     * Create a {@link android.support.v7.app.AppCompatDelegate} to use with {@code activity}.
+     *
+     * @param callback An optional callback for AppCompat specific events
+     */
+    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            return new AppCompatDelegateImplV11(activity, activity.getWindow(), callback);
+        } else {
+            return new AppCompatDelegateImplV7(activity, activity.getWindow(), callback);
+        }
+    }
+
+    /**
+     * Create a {@link android.support.v7.app.AppCompatDelegate} to use with {@code dialog}.
+     *
+     * @param callback An optional callback for AppCompat specific events
+     */
+    public static AppCompatDelegate create(Dialog dialog, AppCompatCallback callback) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            return new AppCompatDelegateImplV11(dialog.getContext(), dialog.getWindow(), callback);
+        } else {
+            return new AppCompatDelegateImplV7(dialog.getContext(), dialog.getWindow(), callback);
+        }
+    }
+
+    /**
+     * Private constructor
+     */
+    AppCompatDelegate() {}
+
+    /**
+     * Support library version of {@link Activity#getActionBar}.
+     *
+     * @return AppCompat's action bar, or null if it does not have one.
+     */
+    public abstract ActionBar getSupportActionBar();
+
+    /**
+     * Set a {@link Toolbar} to act as the {@link ActionBar} for this delegate.
+     *
+     * <p>When set to a non-null value the {@link #getSupportActionBar()} ()} method will return
+     * an {@link ActionBar} object that can be used to control the given toolbar as if it were
+     * a traditional window decor action bar. The toolbar's menu will be populated with the
+     * Activity's options menu and the navigation button will be wired through the standard
+     * {@link android.R.id#home home} menu select action.</p>
+     *
+     * <p>In order to use a Toolbar within the Activity's window content the application
+     * must not request the window feature
+     * {@link android.view.Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p>
+     *
+     * @param toolbar Toolbar to set as the Activity's action bar
+     */
+    public abstract void setSupportActionBar(Toolbar toolbar);
+
+    /**
+     * Return the value of this call from your {@link Activity#getMenuInflater()}
+     */
+    public abstract MenuInflater getMenuInflater();
+
+    /**
+     * Should be called from {@link Activity#onCreate Activity.onCreate()}
+     */
+    public abstract void onCreate(Bundle savedInstanceState);
+
+    /**
+     * Should be called from {@link Activity#onPostCreate(android.os.Bundle)}
+     */
+    public abstract void onPostCreate(Bundle savedInstanceState);
+
+    /**
+     * Should be called from
+     * {@link Activity#onConfigurationChanged}
+     */
+    public abstract void onConfigurationChanged(Configuration newConfig);
+
+    /**
+     * Should be called from {@link Activity#onStop Activity.onStop()}
+     */
+    public abstract void onStop();
+
+    /**
+     * Should be called from {@link Activity#onPostResume()}
+     */
+    public abstract void onPostResume();
+
+    /**
+     * Should be called instead of {@link Activity#setContentView(android.view.View)}}
+     */
+    public abstract void setContentView(View v);
+
+    /**
+     * Should be called instead of {@link Activity#setContentView(int)}}
+     */
+    public abstract void setContentView(int resId);
+
+    /**
+     * Should be called instead of
+     * {@link Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}}
+     */
+    public abstract void setContentView(View v, ViewGroup.LayoutParams lp);
+
+    /**
+     * Should be called instead of
+     * {@link Activity#addContentView(android.view.View, android.view.ViewGroup.LayoutParams)}}
+     */
+    public abstract void addContentView(View v, ViewGroup.LayoutParams lp);
+
+    /**
+     * Should be called from {@link Activity#onTitleChanged(CharSequence, int)}}
+     */
+    public abstract void setTitle(CharSequence title);
+
+    /**
+     * Should be called from {@link Activity#invalidateOptionsMenu()}} or
+     * {@link FragmentActivity#supportInvalidateOptionsMenu()}.
+     */
+    public abstract void invalidateOptionsMenu();
+
+    /**
+     * Should be called from {@link Activity#onDestroy()}
+     */
+    public abstract void onDestroy();
+
+    /**
+     * Returns an {@link ActionBarDrawerToggle.Delegate} which can be returned from your Activity
+     * if it implements {@link ActionBarDrawerToggle.DelegateProvider}.
+     */
+    public abstract ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
+
+    /**
+     * Enable extended window features.  This should be called instead of
+     * {@link android.app.Activity#requestWindowFeature(int)} or
+     * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
+     *
+     * @param featureId The desired feature as defined in {@link android.view.Window}.
+     * @return Returns true if the requested feature is supported and now
+     *         enabled.
+     */
+    public abstract boolean requestWindowFeature(int featureId);
+
+    /**
+     * Start an action mode.
+     *
+     * @param callback Callback that will manage lifecycle events for this context mode
+     * @return The ContextMode that was started, or null if it was canceled
+     */
+    public abstract ActionMode startSupportActionMode(ActionMode.Callback callback);
+
+    /**
+     * Installs AppCompat's {@link android.view.LayoutInflater} Factory so that it can replace
+     * the framework widgets with compatible tinted versions. This should be called before
+     * {@code super.onCreate()} as so:
+     * <pre class="prettyprint">
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     getDelegate().installViewFactory();
+     *     super.onCreate(savedInstanceState);
+     *     getDelegate().onCreate(savedInstanceState);
+     *
+     *     // ...
+     * }
+     * </pre>
+     * If you are using your own {@link android.view.LayoutInflater.Factory Factory} or
+     * {@link android.view.LayoutInflater.Factory2 Factory2} then you can omit this call, and instead call
+     * {@link #createView(android.view.View, String, android.content.Context, android.util.AttributeSet)}
+     * from your factory to return any compatible widgets.
+     */
+    public abstract void installViewFactory();
+
+    /**
+     * This should be called from a
+     * {@link android.support.v4.view.LayoutInflaterFactory LayoutInflaterFactory} in order
+     * to return tint-aware widgets.
+     * <p>
+     * This is only needed if you are using your own
+     * {@link android.view.LayoutInflater LayoutInflater} factory, and have therefore not
+     * installed the default factory via {@link #installViewFactory()}.
+     */
+    public abstract View createView(View parent, String name, @NonNull Context context,
+            @NonNull AttributeSet attrs);
+
+}
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
new file mode 100644
index 0000000..ac46f3c
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2013 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.v7.app;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.view.SupportMenuInflater;
+import android.support.v7.internal.view.WindowCallbackWrapper;
+import android.support.v7.internal.view.menu.MenuBuilder;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.support.v7.view.ActionMode;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.Window;
+
+abstract class AppCompatDelegateImplBase extends AppCompatDelegate {
+
+    final Context mContext;
+    final Window mWindow;
+    final Window.Callback mOriginalWindowCallback;
+    final AppCompatCallback mAppCompatCallback;
+
+    private ActionBar mActionBar;
+    private MenuInflater mMenuInflater;
+
+    // true if this activity has an action bar.
+    boolean mHasActionBar;
+    // true if this activity's action bar overlays other activity content.
+    boolean mOverlayActionBar;
+    // true if this any action modes should overlay the activity content
+    boolean mOverlayActionMode;
+    // true if this activity is floating (e.g. Dialog)
+    boolean mIsFloating;
+    // true if this activity has no title
+    boolean mWindowNoTitle;
+
+    private CharSequence mTitle;
+
+    private boolean mIsDestroyed;
+
+    AppCompatDelegateImplBase(Context context, Window window, AppCompatCallback callback) {
+        mContext = context;
+        mWindow = window;
+        mAppCompatCallback = callback;
+
+        mOriginalWindowCallback = mWindow.getCallback();
+        if (mOriginalWindowCallback instanceof AppCompatWindowCallback) {
+            throw new IllegalStateException(
+                    "AppCompat has already installed itself into the Window");
+        }
+        // Now install the new callback
+        mWindow.setCallback(new AppCompatWindowCallback(mOriginalWindowCallback));
+    }
+
+    abstract ActionBar createSupportActionBar();
+
+    @Override
+    public ActionBar getSupportActionBar() {
+        // The Action Bar should be lazily created as hasActionBar
+        // could change after onCreate
+        if (mHasActionBar) {
+            if (mActionBar == null) {
+                mActionBar = createSupportActionBar();
+            }
+        }
+        return mActionBar;
+    }
+
+    final ActionBar peekSupportActionBar() {
+        return mActionBar;
+    }
+
+    final void setSupportActionBar(ActionBar actionBar) {
+        mActionBar = actionBar;
+    }
+
+    @Override
+    public MenuInflater getMenuInflater() {
+        if (mMenuInflater == null) {
+            mMenuInflater = new SupportMenuInflater(getActionBarThemedContext());
+        }
+        return mMenuInflater;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme);
+
+        if (!a.hasValue(R.styleable.Theme_windowActionBar)) {
+            a.recycle();
+            throw new IllegalStateException(
+                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
+        }
+
+        if (a.getBoolean(R.styleable.Theme_windowActionBar, false)) {
+            mHasActionBar = true;
+        }
+        if (a.getBoolean(R.styleable.Theme_windowActionBarOverlay, false)) {
+            mOverlayActionBar = true;
+        }
+        if (a.getBoolean(R.styleable.Theme_windowActionModeOverlay, false)) {
+            mOverlayActionMode = true;
+        }
+        mIsFloating = a.getBoolean(R.styleable.Theme_android_windowIsFloating, false);
+        mWindowNoTitle = a.getBoolean(R.styleable.Theme_windowNoTitle, false);
+        a.recycle();
+    }
+
+    // Methods used to create and respond to options menu
+    abstract boolean onPanelClosed(int featureId, Menu menu);
+
+    abstract boolean onMenuOpened(int featureId, Menu menu);
+
+    abstract boolean dispatchKeyEvent(KeyEvent event);
+
+    abstract boolean onKeyShortcut(int keyCode, KeyEvent event);
+
+    @Override
+    public final ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
+        return new ActionBarDrawableToggleImpl();
+    }
+
+    final Context getActionBarThemedContext() {
+        Context context = null;
+
+        // If we have an action bar, let it return a themed context
+        ActionBar ab = getSupportActionBar();
+        if (ab != null) {
+            context = ab.getThemedContext();
+        }
+
+        if (context == null) {
+            context = mContext;
+        }
+        return context;
+    }
+
+    private class ActionBarDrawableToggleImpl implements ActionBarDrawerToggle.Delegate {
+        @Override
+        public Drawable getThemeUpIndicator() {
+            final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
+                    getActionBarThemedContext(), null, new int[]{ R.attr.homeAsUpIndicator });
+            final Drawable result = a.getDrawable(0);
+            a.recycle();
+            return result;
+        }
+
+        @Override
+        public Context getActionBarThemedContext() {
+            return AppCompatDelegateImplBase.this.getActionBarThemedContext();
+        }
+
+        @Override
+        public boolean isNavigationVisible() {
+            final ActionBar ab = getSupportActionBar();
+            return ab != null && (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0;
+        }
+
+        @Override
+        public void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
+            ActionBar ab = getSupportActionBar();
+            if (ab != null) {
+                ab.setHomeAsUpIndicator(upDrawable);
+                ab.setHomeActionContentDescription(contentDescRes);
+            }
+        }
+
+        @Override
+        public void setActionBarDescription(int contentDescRes) {
+            ActionBar ab = getSupportActionBar();
+            if (ab != null) {
+                ab.setHomeActionContentDescription(contentDescRes);
+            }
+        }
+    }
+
+    abstract ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback);
+
+    @Override
+    public final void onDestroy() {
+        mIsDestroyed = true;
+    }
+
+    final boolean isDestroyed() {
+        return mIsDestroyed;
+    }
+
+    final Window.Callback getWindowCallback() {
+        return mWindow.getCallback();
+    }
+
+    @Override
+    public final void setTitle(CharSequence title) {
+        mTitle = title;
+        onTitleChanged(title);
+    }
+
+    abstract void onTitleChanged(CharSequence title);
+
+    final CharSequence getTitle() {
+        // If the original window callback is an Activity, we'll use it's title
+        if (mOriginalWindowCallback instanceof Activity) {
+            return ((Activity) mOriginalWindowCallback).getTitle();
+        }
+        // Else, we'll return the title we have recorded ourselves
+        return mTitle;
+    }
+
+    private class AppCompatWindowCallback extends WindowCallbackWrapper {
+        AppCompatWindowCallback(Window.Callback callback) {
+            super(callback);
+        }
+
+        @Override
+        public boolean dispatchKeyEvent(KeyEvent event) {
+            if (AppCompatDelegateImplBase.this.dispatchKeyEvent(event)) {
+                return true;
+            }
+            return super.dispatchKeyEvent(event);
+        }
+
+        @Override
+        public boolean onCreatePanelMenu(int featureId, Menu menu) {
+            if (featureId == Window.FEATURE_OPTIONS_PANEL && !(menu instanceof MenuBuilder)) {
+                // If this is an options menu but it's not an AppCompat menu, we eat the event
+                // and return false
+                return false;
+            }
+            return super.onCreatePanelMenu(featureId, menu);
+        }
+
+        @Override
+        public boolean onPreparePanel(int featureId, View view, Menu menu) {
+            if (featureId == Window.FEATURE_OPTIONS_PANEL && !(menu instanceof MenuBuilder)) {
+                // If this is an options menu but it's not an AppCompat menu, we eat the event
+                // and return false
+                return false;
+            }
+
+            if (featureId == Window.FEATURE_OPTIONS_PANEL && bypassPrepareOptionsPanelIfNeeded()) {
+                // If this is an options menu and we need to bypass onPreparePanel, do so
+                if (mOriginalWindowCallback instanceof Activity) {
+                    return ((Activity) mOriginalWindowCallback).onPrepareOptionsMenu(menu);
+                } else if (mOriginalWindowCallback instanceof Dialog) {
+                    return ((Dialog) mOriginalWindowCallback).onPrepareOptionsMenu(menu);
+                }
+                return false;
+            }
+
+            // Else, defer to the default handling
+            return super.onPreparePanel(featureId, view, menu);
+        }
+
+        @Override
+        public boolean onMenuOpened(int featureId, Menu menu) {
+            if (AppCompatDelegateImplBase.this.onMenuOpened(featureId, menu)) {
+                return true;
+            }
+            return super.onMenuOpened(featureId, menu);
+        }
+
+        @Override
+        public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+            if (AppCompatDelegateImplBase.this.onKeyShortcut(event.getKeyCode(), event)) {
+                return true;
+            }
+            return super.dispatchKeyShortcutEvent(event);
+        }
+
+        @Override
+        public void onContentChanged() {
+            // We purposely do not propagate this call as this is called when we install
+            // our sub-decor rather than the user's content
+        }
+
+        @Override
+        public void onPanelClosed(int featureId, Menu menu) {
+            if (AppCompatDelegateImplBase.this.onPanelClosed(featureId, menu)) {
+                return;
+            }
+            super.onPanelClosed(featureId, menu);
+        }
+
+        /**
+         * For the options menu, we may need to call onPrepareOptionsMenu() directly,
+         * bypassing onPreparePanel(). This is because onPreparePanel() in certain situations
+         * calls menu.hasVisibleItems(), which interferes with any initial invisible items.
+         *
+         * @return true if onPrepareOptionsMenu should be called directly.
+         */
+        private boolean bypassPrepareOptionsPanelIfNeeded() {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN
+                    && mOriginalWindowCallback instanceof Activity) {
+                // For Activities, we only need to bypass onPreparePanel if we're running pre-JB
+                return true;
+            } else if (mOriginalWindowCallback instanceof Dialog) {
+                // For Dialogs, we always need to bypass onPreparePanel
+                return true;
+            }
+            return false;
+        }
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegateHC.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
similarity index 64%
rename from v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegateHC.java
rename to v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
index 97f2b22..a480ee6 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegateHC.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
@@ -21,25 +21,28 @@
 import android.os.Build;
 import android.support.v7.internal.view.SupportActionModeWrapper;
 import android.support.v7.internal.widget.NativeActionModeAwareLayout;
+import android.util.AttributeSet;
 import android.view.ActionMode;
-import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
 
 @TargetApi(Build.VERSION_CODES.HONEYCOMB)
-class ActionBarActivityDelegateHC extends ActionBarActivityDelegateBase
+class AppCompatDelegateImplV11 extends AppCompatDelegateImplV7
         implements NativeActionModeAwareLayout.OnActionModeForChildListener {
 
     private NativeActionModeAwareLayout mNativeActionModeAwareLayout;
 
-    ActionBarActivityDelegateHC(ActionBarActivity activity) {
-        super(activity);
+    AppCompatDelegateImplV11(Context context, Window window, AppCompatCallback callback) {
+        super(context, window, callback);
     }
 
     @Override
-    void onSubDecorInstalled() {
+    void onSubDecorInstalled(ViewGroup subDecor) {
         // NativeActionModeAwareLayout is used to notify us when a native Action Mode is started
-        mNativeActionModeAwareLayout = (NativeActionModeAwareLayout) mActivity
-                .findViewById(android.R.id.content);
+        mNativeActionModeAwareLayout = (NativeActionModeAwareLayout)
+                subDecor.findViewById(android.R.id.content);
 
         // Can be null when using FEATURE_ACTION_BAR_OVERLAY
         if (mNativeActionModeAwareLayout != null) {
@@ -58,8 +61,25 @@
 
         if (supportActionMode != null) {
             // If we received a support action mode, wrap and return it
-            return new SupportActionModeWrapper(mActivity, supportActionMode);
+            return new SupportActionModeWrapper(mContext, supportActionMode);
         }
         return null;
     }
+
+    View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
+        // First let super have a try, this allows FragmentActivity to inflate any support
+        // fragments
+        final View view = super.callActivityOnCreateView(parent, name, context, attrs);
+        if (view != null) {
+            return view;
+        }
+
+        // Now, let the Activity's LayoutInflater.Factory2 method try...
+        if (mOriginalWindowCallback instanceof LayoutInflater.Factory2) {
+            return ((LayoutInflater.Factory2) mOriginalWindowCallback)
+                    .onCreateView(parent, name, context, attrs);
+        }
+
+        return null;
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegateBase.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
similarity index 79%
rename from v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegateBase.java
rename to v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
index 0c7d7c9..0d4aeca 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegateBase.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
@@ -16,6 +16,8 @@
 
 package android.support.v7.app;
 
+import android.app.Activity;
+import android.app.Dialog;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -29,42 +31,37 @@
 import android.os.Parcelable;
 import android.support.annotation.NonNull;
 import android.support.v4.app.NavUtils;
+import android.support.v4.view.LayoutInflaterCompat;
+import android.support.v4.view.LayoutInflaterFactory;
 import android.support.v4.view.OnApplyWindowInsetsListener;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewConfigurationCompat;
 import android.support.v4.view.WindowInsetsCompat;
 import android.support.v7.appcompat.R;
+import android.support.v7.internal.app.AppCompatViewInflater;
 import android.support.v7.internal.app.ToolbarActionBar;
-import android.support.v7.internal.app.WindowCallback;
 import android.support.v7.internal.app.WindowDecorActionBar;
+import android.support.v7.internal.view.ContextThemeWrapper;
 import android.support.v7.internal.view.StandaloneActionMode;
 import android.support.v7.internal.view.menu.ListMenuPresenter;
 import android.support.v7.internal.view.menu.MenuBuilder;
 import android.support.v7.internal.view.menu.MenuPresenter;
 import android.support.v7.internal.view.menu.MenuView;
 import android.support.v7.internal.widget.ActionBarContextView;
+import android.support.v7.internal.widget.ContentFrameLayout;
 import android.support.v7.internal.widget.DecorContentParent;
 import android.support.v7.internal.widget.FitWindowsViewGroup;
-import android.support.v7.internal.widget.TintAutoCompleteTextView;
-import android.support.v7.internal.widget.TintButton;
-import android.support.v7.internal.widget.TintCheckBox;
-import android.support.v7.internal.widget.TintCheckedTextView;
-import android.support.v7.internal.widget.TintEditText;
 import android.support.v7.internal.widget.TintManager;
-import android.support.v7.internal.widget.TintMultiAutoCompleteTextView;
-import android.support.v7.internal.widget.TintRadioButton;
-import android.support.v7.internal.widget.TintRatingBar;
-import android.support.v7.internal.widget.TintSpinner;
 import android.support.v7.internal.widget.ViewStubCompat;
 import android.support.v7.internal.widget.ViewUtils;
 import android.support.v7.view.ActionMode;
 import android.support.v7.widget.Toolbar;
+import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -81,6 +78,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 import android.widget.PopupWindow;
+import android.widget.TextView;
 
 import static android.support.v4.view.WindowCompat.FEATURE_ACTION_BAR;
 import static android.support.v4.view.WindowCompat.FEATURE_ACTION_BAR_OVERLAY;
@@ -89,9 +87,8 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.Window.FEATURE_OPTIONS_PANEL;
 
-class ActionBarActivityDelegateBase extends ActionBarActivityDelegate
-        implements MenuBuilder.Callback {
-    private static final String TAG = "ActionBarActivityDelegateBase";
+class AppCompatDelegateImplV7 extends AppCompatDelegateImplBase
+        implements MenuBuilder.Callback, LayoutInflaterFactory {
 
     private DecorContentParent mDecorContentParent;
     private ActionMenuPresenterCallback mActionMenuPresenterCallback;
@@ -107,10 +104,9 @@
     private ViewGroup mWindowDecor;
     private ViewGroup mSubDecor;
 
+    private TextView mTitleView;
     private View mStatusGuard;
 
-    private CharSequence mTitleToSet;
-
     // Used to keep track of Progress Bar Window features
     private boolean mFeatureProgress, mFeatureIndeterminateProgress;
 
@@ -140,29 +136,33 @@
     private Rect mTempRect1;
     private Rect mTempRect2;
 
-    ActionBarActivityDelegateBase(ActionBarActivity activity) {
-        super(activity);
+    private AppCompatViewInflater mAppCompatViewInflater;
+
+    AppCompatDelegateImplV7(Context context, Window window, AppCompatCallback callback) {
+        super(context, window, callback);
     }
 
     @Override
-    void onCreate(Bundle savedInstanceState) {
+    public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mWindowDecor = (ViewGroup) mActivity.getWindow().getDecorView();
+        mWindowDecor = (ViewGroup) mWindow.getDecorView();
 
-        if (NavUtils.getParentActivityName(mActivity) != null) {
-            // Peek at the Action Bar and update it if it already exists
-            ActionBar ab = peekSupportActionBar();
-            if (ab == null) {
-                mEnableDefaultActionBarUp = true;
-            } else {
-                ab.setDefaultDisplayHomeAsUpEnabled(true);
+        if (mOriginalWindowCallback instanceof Activity) {
+            if (NavUtils.getParentActivityName((Activity) mOriginalWindowCallback) != null) {
+                // Peek at the Action Bar and update it if it already exists
+                ActionBar ab = peekSupportActionBar();
+                if (ab == null) {
+                    mEnableDefaultActionBarUp = true;
+                } else {
+                    ab.setDefaultDisplayHomeAsUpEnabled(true);
+                }
             }
         }
     }
 
     @Override
-    void onPostCreate(Bundle savedInstanceState) {
+    public void onPostCreate(Bundle savedInstanceState) {
         // Make sure that the sub decor is installed
         ensureSubDecor();
     }
@@ -170,13 +170,25 @@
     @Override
     public ActionBar createSupportActionBar() {
         ensureSubDecor();
-        ActionBar ab = new WindowDecorActionBar(mActivity, mOverlayActionBar);
-        ab.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
+        ActionBar ab = null;
+        if (mOriginalWindowCallback instanceof Activity) {
+            ab = new WindowDecorActionBar((Activity) mOriginalWindowCallback, mOverlayActionBar);
+        } else if (mOriginalWindowCallback instanceof Dialog) {
+            ab = new WindowDecorActionBar((Dialog) mOriginalWindowCallback);
+        }
+        if (ab != null) {
+            ab.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
+        }
         return ab;
     }
 
     @Override
-    void setSupportActionBar(Toolbar toolbar) {
+    public void setSupportActionBar(Toolbar toolbar) {
+        if (!(mOriginalWindowCallback instanceof Activity)) {
+            // Only Activities support custom Action Bars
+            return;
+        }
+
         final ActionBar ab = getSupportActionBar();
         if (ab instanceof WindowDecorActionBar) {
             throw new IllegalStateException("This Activity already has an action bar supplied " +
@@ -184,12 +196,10 @@
                     "windowActionBar to false in your theme to use a Toolbar instead.");
         }
 
-        // Need to make sure we give the action bar the default window callback. Otherwise multiple
-        // setSupportActionBar() calls lead to memory leaks
-        ToolbarActionBar tbab = new ToolbarActionBar(toolbar, mActivity.getTitle(),
-                mActivity.getWindow(), mDefaultWindowCallback);
+        ToolbarActionBar tbab = new ToolbarActionBar(toolbar, ((Activity) mContext).getTitle(),
+                mWindow);
         setSupportActionBar(tbab);
-        setWindowCallback(tbab.getWrappedWindowCallback());
+        mWindow.setCallback(tbab.getWrappedWindowCallback());
         tbab.invalidateOptionsMenu();
     }
 
@@ -226,88 +236,90 @@
     @Override
     public void setContentView(View v) {
         ensureSubDecor();
-        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
+        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
         contentParent.removeAllViews();
         contentParent.addView(v);
-        mActivity.onSupportContentChanged();
+        mOriginalWindowCallback.onContentChanged();
     }
 
     @Override
     public void setContentView(int resId) {
         ensureSubDecor();
-        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
+        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
         contentParent.removeAllViews();
-        mActivity.getLayoutInflater().inflate(resId, contentParent);
-        mActivity.onSupportContentChanged();
+        LayoutInflater.from(mContext).inflate(resId, contentParent);
+        mOriginalWindowCallback.onContentChanged();
     }
 
     @Override
     public void setContentView(View v, ViewGroup.LayoutParams lp) {
         ensureSubDecor();
-        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
+        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
         contentParent.removeAllViews();
         contentParent.addView(v, lp);
-        mActivity.onSupportContentChanged();
+        mOriginalWindowCallback.onContentChanged();
     }
 
     @Override
     public void addContentView(View v, ViewGroup.LayoutParams lp) {
         ensureSubDecor();
-        ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content);
+        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
         contentParent.addView(v, lp);
-        mActivity.onSupportContentChanged();
+        mOriginalWindowCallback.onContentChanged();
     }
 
-    @Override
-    public void onContentChanged() {
-        // Ignore all calls to this method as we call onSupportContentChanged manually above
-    }
-
-    final void ensureSubDecor() {
+    private void ensureSubDecor() {
         if (!mSubDecorInstalled) {
-            if (mHasActionBar) {
-                /**
-                 * This needs some explanation. As we can not use the android:theme attribute
-                 * pre-L, we emulate it by manually creating a LayoutInflater using a
-                 * ContextThemeWrapper pointing to actionBarTheme.
-                 */
-                TypedValue outValue = new TypedValue();
-                mActivity.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
+            final LayoutInflater inflater = LayoutInflater.from(mContext);
 
-                Context themedContext;
-                if (outValue.resourceId != 0) {
-                    themedContext = new ContextThemeWrapper(mActivity, outValue.resourceId);
-                } else {
-                    themedContext = mActivity;
-                }
+            if (!mWindowNoTitle) {
+                if (mIsFloating) {
+                    // If we're floating, inflate the dialog title decor
+                    mSubDecor = (ViewGroup) inflater.inflate(
+                            R.layout.abc_dialog_title_material, null);
+                } else if (mHasActionBar) {
+                    /**
+                     * This needs some explanation. As we can not use the android:theme attribute
+                     * pre-L, we emulate it by manually creating a LayoutInflater using a
+                     * ContextThemeWrapper pointing to actionBarTheme.
+                     */
+                    TypedValue outValue = new TypedValue();
+                    mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
 
-                // Now inflate the view using the themed context and set it as the content view
-                mSubDecor = (ViewGroup) LayoutInflater.from(themedContext)
-                        .inflate(R.layout.abc_screen_toolbar, null);
+                    Context themedContext;
+                    if (outValue.resourceId != 0) {
+                        themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
+                    } else {
+                        themedContext = mContext;
+                    }
 
-                mDecorContentParent = (DecorContentParent) mSubDecor
-                        .findViewById(R.id.decor_content_parent);
-                mDecorContentParent.setWindowCallback(getWindowCallback());
+                    // Now inflate the view using the themed context and set it as the content view
+                    mSubDecor = (ViewGroup) LayoutInflater.from(themedContext)
+                            .inflate(R.layout.abc_screen_toolbar, null);
 
-                /**
-                 * Propagate features to DecorContentParent
-                 */
-                if (mOverlayActionBar) {
-                    mDecorContentParent.initFeature(FEATURE_ACTION_BAR_OVERLAY);
-                }
-                if (mFeatureProgress) {
-                    mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
-                }
-                if (mFeatureIndeterminateProgress) {
-                    mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+                    mDecorContentParent = (DecorContentParent) mSubDecor
+                            .findViewById(R.id.decor_content_parent);
+                    mDecorContentParent.setWindowCallback(getWindowCallback());
+
+                    /**
+                     * Propagate features to DecorContentParent
+                     */
+                    if (mOverlayActionBar) {
+                        mDecorContentParent.initFeature(FEATURE_ACTION_BAR_OVERLAY);
+                    }
+                    if (mFeatureProgress) {
+                        mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
+                    }
+                    if (mFeatureIndeterminateProgress) {
+                        mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+                    }
                 }
             } else {
                 if (mOverlayActionMode) {
-                    mSubDecor = (ViewGroup) LayoutInflater.from(mActivity)
-                            .inflate(R.layout.abc_screen_simple_overlay_action_mode, null);
+                    mSubDecor = (ViewGroup) inflater.inflate(
+                            R.layout.abc_screen_simple_overlay_action_mode, null);
                 } else {
-                    mSubDecor = (ViewGroup) LayoutInflater.from(mActivity)
-                            .inflate(R.layout.abc_screen_simple, null);
+                    mSubDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
                 }
 
                 if (Build.VERSION.SDK_INT >= 21) {
@@ -322,14 +334,15 @@
                                     final int newTop = updateStatusGuard(top);
 
                                     if (top != newTop) {
-                                        return insets.replaceSystemWindowInsets(
+                                        insets = insets.replaceSystemWindowInsets(
                                                 insets.getSystemWindowInsetLeft(),
                                                 newTop,
                                                 insets.getSystemWindowInsetRight(),
                                                 insets.getSystemWindowInsetBottom());
-                                    } else {
-                                        return insets;
                                     }
+
+                                    // Now apply the insets on our view
+                                    return ViewCompat.onApplyWindowInsets(v, insets);
                                 }
                             });
                 } else {
@@ -344,11 +357,20 @@
                 }
             }
 
+            if (mSubDecor == null) {
+                throw new IllegalArgumentException(
+                        "AppCompat does not support the current theme features");
+            }
+
+            if (mDecorContentParent == null) {
+                mTitleView = (TextView) mSubDecor.findViewById(R.id.title);
+            }
+
             // Make the decor optionally fit system windows, like the window's decor
             ViewUtils.makeOptionalFitsSystemWindows(mSubDecor);
 
-            final ViewGroup decorContent = (ViewGroup) mActivity.findViewById(android.R.id.content);
-            final ViewGroup abcContent = (ViewGroup) mSubDecor.findViewById(
+            final ViewGroup decorContent = (ViewGroup) mWindow.findViewById(android.R.id.content);
+            final ContentFrameLayout abcContent = (ContentFrameLayout) mSubDecor.findViewById(
                     R.id.action_bar_activity_content);
 
             // There might be Views already added to the Window's content view so we need to
@@ -359,8 +381,8 @@
                 abcContent.addView(child);
             }
 
-            // Now set the Activity's content view with the decor
-            mActivity.superSetContentView(mSubDecor);
+            // Now set the Window's content view with the decor
+            mWindow.setContentView(mSubDecor);
 
             // Change our content FrameLayout to use the android.R.id.content id.
             // Useful for fragments.
@@ -373,15 +395,15 @@
                 ((FrameLayout) decorContent).setForeground(null);
             }
 
-            // A title was set before we've install the decor so set it now.
-            if (mTitleToSet != null && mDecorContentParent != null) {
-                mDecorContentParent.setWindowTitle(mTitleToSet);
-                mTitleToSet = null;
+            // If a title was set before we installed the decor, propogate it now
+            CharSequence title = getTitle();
+            if (!TextUtils.isEmpty(title)) {
+                onTitleChanged(title);
             }
 
-            applyFixedSizeWindow();
+            applyFixedSizeWindow(abcContent);
 
-            onSubDecorInstalled();
+            onSubDecorInstalled(mSubDecor);
 
             mSubDecorInstalled = true;
 
@@ -397,65 +419,45 @@
         }
     }
 
-    void onSubDecorInstalled() {}
+    void onSubDecorInstalled(ViewGroup subDecor) {}
 
-    private void applyFixedSizeWindow() {
-        TypedArray a = mActivity.obtainStyledAttributes(R.styleable.Theme);
+    private void applyFixedSizeWindow(ContentFrameLayout contentFrameLayout) {
+        // This is a bit weird. In the framework, the window sizing attributes control
+        // the decor view's size, meaning that any padding is inset for the min/max widths below.
+        // We don't control measurement at that level, so we need to workaround it by making sure
+        // that the decor view's padding is taken into account.
+        contentFrameLayout.setDecorPadding(mWindowDecor.getPaddingLeft(),
+                mWindowDecor.getPaddingTop(), mWindowDecor.getPaddingRight(),
+                mWindowDecor.getPaddingBottom());
 
-        TypedValue mFixedWidthMajor = null;
-        TypedValue mFixedWidthMinor = null;
-        TypedValue mFixedHeightMajor = null;
-        TypedValue mFixedHeightMinor = null;
+
+        TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme);
+        a.getValue(R.styleable.Theme_windowMinWidthMajor, contentFrameLayout.getMinWidthMajor());
+        a.getValue(R.styleable.Theme_windowMinWidthMinor, contentFrameLayout.getMinWidthMinor());
 
         if (a.hasValue(R.styleable.Theme_windowFixedWidthMajor)) {
-            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
-            a.getValue(R.styleable.Theme_windowFixedWidthMajor, mFixedWidthMajor);
+            a.getValue(R.styleable.Theme_windowFixedWidthMajor,
+                    contentFrameLayout.getFixedWidthMajor());
         }
         if (a.hasValue(R.styleable.Theme_windowFixedWidthMinor)) {
-            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
-            a.getValue(R.styleable.Theme_windowFixedWidthMinor, mFixedWidthMinor);
+            a.getValue(R.styleable.Theme_windowFixedWidthMinor,
+                    contentFrameLayout.getFixedWidthMinor());
         }
         if (a.hasValue(R.styleable.Theme_windowFixedHeightMajor)) {
-            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
-            a.getValue(R.styleable.Theme_windowFixedHeightMajor, mFixedHeightMajor);
+            a.getValue(R.styleable.Theme_windowFixedHeightMajor,
+                    contentFrameLayout.getFixedHeightMajor());
         }
         if (a.hasValue(R.styleable.Theme_windowFixedHeightMinor)) {
-            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
-            a.getValue(R.styleable.Theme_windowFixedHeightMinor, mFixedHeightMinor);
+            a.getValue(R.styleable.Theme_windowFixedHeightMinor,
+                    contentFrameLayout.getFixedHeightMinor());
         }
-
-        final DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics();
-        final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
-        int w = ViewGroup.LayoutParams.MATCH_PARENT;
-        int h = ViewGroup.LayoutParams.MATCH_PARENT;
-
-        final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
-        if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
-            if (tvw.type == TypedValue.TYPE_DIMENSION) {
-                w = (int) tvw.getDimension(metrics);
-            } else if (tvw.type == TypedValue.TYPE_FRACTION) {
-                w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
-            }
-        }
-
-        final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
-        if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
-            if (tvh.type == TypedValue.TYPE_DIMENSION) {
-                h = (int) tvh.getDimension(metrics);
-            } else if (tvh.type == TypedValue.TYPE_FRACTION) {
-                h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
-            }
-        }
-
-        if (w != ViewGroup.LayoutParams.MATCH_PARENT || h != ViewGroup.LayoutParams.MATCH_PARENT) {
-            mActivity.getWindow().setLayout(w, h);
-        }
-
         a.recycle();
+
+        contentFrameLayout.requestLayout();
     }
 
     @Override
-    public boolean supportRequestWindowFeature(int featureId) {
+    public boolean requestWindowFeature(int featureId) {
         switch (featureId) {
             case FEATURE_ACTION_BAR:
                 throwFeatureRequestIfSubDecorInstalled();
@@ -477,50 +479,43 @@
                 throwFeatureRequestIfSubDecorInstalled();
                 mFeatureIndeterminateProgress = true;
                 return true;
+            case Window.FEATURE_NO_TITLE:
+                throwFeatureRequestIfSubDecorInstalled();
+                mWindowNoTitle = true;
+                return true;
         }
 
-        return mActivity.requestWindowFeature(featureId);
+        return mWindow.requestFeature(featureId);
     }
 
     @Override
-    public void onTitleChanged(CharSequence title) {
+    void onTitleChanged(CharSequence title) {
         if (mDecorContentParent != null) {
             mDecorContentParent.setWindowTitle(title);
         } else if (getSupportActionBar() != null) {
             getSupportActionBar().setWindowTitle(title);
-        } else {
-            mTitleToSet = title;
+        } else if (mTitleView != null) {
+            mTitleView.setText(title);
         }
     }
 
     @Override
-    public boolean onCreatePanelMenu(int featureId, Menu menu) {
-        if (featureId != Window.FEATURE_OPTIONS_PANEL) {
-            return getWindowCallback().onCreatePanelMenu(featureId, menu);
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onPreparePanel(int featureId, View view, Menu menu) {
-        if (featureId != Window.FEATURE_OPTIONS_PANEL) {
-            return getWindowCallback().onPreparePanel(featureId, view, menu);
-        }
-        return false;
-    }
-
-    @Override
-    public void onPanelClosed(final int featureId, Menu menu) {
+    boolean onPanelClosed(final int featureId, Menu menu) {
         if (featureId == FEATURE_ACTION_BAR) {
             ActionBar ab = getSupportActionBar();
             if (ab != null) {
                 ab.dispatchMenuVisibilityChanged(false);
             }
-        } else if (!isDestroyed()) {
-            // Only pass it through to the Activity's super impl if it's not ACTION_BAR. This is
-            // because ICS+ will try and create a framework action bar due to this call
-            mActivity.superOnPanelClosed(featureId, menu);
+            return true;
+        } else if (featureId == FEATURE_OPTIONS_PANEL) {
+            // Make sure that the options panel is closed. This is mainly used when we're using a
+            // ToolbarActionBar
+            PanelFeatureState st = getPanelState(featureId, true);
+            if (st.isOpen) {
+                closePanel(st, false);
+            }
         }
+        return false;
     }
 
     @Override
@@ -531,14 +526,13 @@
                 ab.dispatchMenuVisibilityChanged(true);
             }
             return true;
-        } else {
-            return mActivity.superOnMenuOpened(featureId, menu);
         }
+        return false;
     }
 
     @Override
     public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
-        final WindowCallback cb = getWindowCallback();
+        final Window.Callback cb = getWindowCallback();
         if (cb != null && !isDestroyed()) {
             final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
             if (panel != null) {
@@ -568,8 +562,8 @@
         ActionBar ab = getSupportActionBar();
         if (ab != null) {
             mActionMode = ab.startActionMode(wrappedCallback);
-            if (mActionMode != null) {
-                mActivity.onSupportActionModeStarted(mActionMode);
+            if (mActionMode != null && mAppCompatCallback != null) {
+                mAppCompatCallback.onSupportActionModeStarted(mActionMode);
             }
         }
 
@@ -582,7 +576,7 @@
     }
 
     @Override
-    public void supportInvalidateOptionsMenu() {
+    public void invalidateOptionsMenu() {
         final ActionBar ab = getSupportActionBar();
         if (ab != null && ab.invalidateOptionsMenu()) return;
 
@@ -607,9 +601,9 @@
                 mActionModePopup.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
 
                 TypedValue heightValue = new TypedValue();
-                mActivity.getTheme().resolveAttribute(R.attr.actionBarSize, heightValue, true);
+                mContext.getTheme().resolveAttribute(R.attr.actionBarSize, heightValue, true);
                 final int height = TypedValue.complexToDimensionPixelSize(heightValue.data,
-                        mActivity.getResources().getDisplayMetrics());
+                        mContext.getResources().getDisplayMetrics());
                 mActionModeView.setContentHeight(height);
                 mActionModePopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
                 mShowActionModePopup = new Runnable() {
@@ -620,7 +614,7 @@
                     }
                 };
             } else {
-                ViewStubCompat stub = (ViewStubCompat) mActivity
+                ViewStubCompat stub = (ViewStubCompat) mSubDecor
                         .findViewById(R.id.action_mode_bar_stub);
                 if (stub != null) {
                     // Set the layout inflater so that it is inflated with the action bar's context
@@ -640,7 +634,7 @@
                 mActionModeView.setVisibility(View.VISIBLE);
                 mActionMode = mode;
                 if (mActionModePopup != null) {
-                    mActivity.getWindow().getDecorView().post(mShowActionModePopup);
+                    mWindow.getDecorView().post(mShowActionModePopup);
                 }
                 mActionModeView.sendAccessibilityEvent(
                         AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -652,14 +646,13 @@
                 mActionMode = null;
             }
         }
-        if (mActionMode != null && mActivity != null) {
-            mActivity.onSupportActionModeStarted(mActionMode);
+        if (mActionMode != null && mAppCompatCallback != null) {
+            mAppCompatCallback.onSupportActionModeStarted(mActionMode);
         }
         return mActionMode;
     }
 
-    @Override
-    public boolean onBackPressed() {
+    boolean onBackPressed() {
         // Back cancels action modes first.
         if (mActionMode != null) {
             mActionMode.finish();
@@ -672,35 +665,11 @@
             return true;
         }
 
+        // Let the call through...
         return false;
     }
 
     @Override
-    void setSupportProgressBarVisibility(boolean visible) {
-        // noop
-    }
-
-    @Override
-    void setSupportProgressBarIndeterminateVisibility(boolean visible) {
-        // noop
-    }
-
-    @Override
-    void setSupportProgressBarIndeterminate(boolean indeterminate) {
-        // noop
-    }
-
-    @Override
-    void setSupportProgress(int progress) {
-        // noop
-    }
-
-    @Override
-    int getHomeAsUpIndicatorAttrId() {
-        return R.attr.homeAsUpIndicator;
-    }
-
-    @Override
     boolean onKeyShortcut(int keyCode, KeyEvent ev) {
         // Let the Action Bar have a chance at handling the shortcut
         ActionBar ab = getSupportActionBar();
@@ -746,7 +715,7 @@
         return isDown ? onKeyDown(keyCode, event) : onKeyUp(keyCode, event);
     }
 
-    protected boolean onKeyUp(int keyCode, KeyEvent event) {
+    boolean onKeyUp(int keyCode, KeyEvent event) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_MENU:
                 onKeyUpPanel(Window.FEATURE_OPTIONS_PANEL, event);
@@ -757,15 +726,19 @@
                     closePanel(st, true);
                     return true;
                 }
+                if (onBackPressed()) {
+                    return true;
+                }
                 break;
         }
         return false;
     }
 
-    protected boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_MENU) {
-            onKeyDownPanel(Window.FEATURE_OPTIONS_PANEL, event);
-            return true;
+    boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_MENU:
+                onKeyDownPanel(Window.FEATURE_OPTIONS_PANEL, event);
+                return true;
         }
 
         // On API v7-10 we need to manually call onKeyShortcut() as this is not called
@@ -777,29 +750,57 @@
     }
 
     @Override
-    View createView(final String name, @NonNull Context context, @NonNull AttributeSet attrs) {
-        if (Build.VERSION.SDK_INT < 21) {
-            // If we're running pre-L, we need to 'inject' our tint aware Views in place of the
-            // standard framework versions
-            switch (name) {
-                case "EditText":
-                    return new TintEditText(context, attrs);
-                case "Spinner":
-                    return new TintSpinner(context, attrs);
-                case "CheckBox":
-                    return new TintCheckBox(context, attrs);
-                case "RadioButton":
-                    return new TintRadioButton(context, attrs);
-                case "CheckedTextView":
-                    return new TintCheckedTextView(context, attrs);
-                case "AutoCompleteTextView":
-                    return new TintAutoCompleteTextView(context, attrs);
-                case "MultiAutoCompleteTextView":
-                    return new TintMultiAutoCompleteTextView(context, attrs);
-                case "RatingBar":
-                    return new TintRatingBar(context, attrs);
-                case "Button":
-                    return new TintButton(context, attrs);
+    public View createView(View parent, final String name, @NonNull Context context,
+            @NonNull AttributeSet attrs) {
+        final boolean isPre21 = Build.VERSION.SDK_INT < 21;
+
+        if (mAppCompatViewInflater == null) {
+            mAppCompatViewInflater = new AppCompatViewInflater(mContext);
+        }
+
+        // We only want the View to inherit it's context from the parent if it is from the
+        // apps content, and not part of our sub-decor
+        final boolean inheritContext = isPre21 && mSubDecorInstalled && parent != null
+                && parent.getId() != android.R.id.content;
+
+        return mAppCompatViewInflater.createView(parent, name, context, attrs,
+                inheritContext, isPre21);
+    }
+
+    @Override
+    public void installViewFactory() {
+        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+        if (layoutInflater.getFactory() == null) {
+            LayoutInflaterCompat.setFactory(layoutInflater, this);
+        } else {
+            Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+                    + " so we can not install AppCompat's");
+        }
+    }
+
+    /**
+     * From {@link android.support.v4.view.LayoutInflaterFactory}
+     */
+    @Override
+    public final View onCreateView(View parent, String name,
+            Context context, AttributeSet attrs) {
+        // First let the Activity's Factory try and inflate the view
+        final View view = callActivityOnCreateView(parent, name, context, attrs);
+        if (view != null) {
+            return view;
+        }
+
+        // If the Factory didn't handle it, let our createView() method try
+        return createView(parent, name, context, attrs);
+    }
+
+    View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
+        // Let the Activity's LayoutInflater.Factory try and handle it
+        if (mOriginalWindowCallback instanceof LayoutInflater.Factory) {
+            final View result = ((LayoutInflater.Factory) mOriginalWindowCallback)
+                    .onCreateView(name, context, attrs);
+            if (result != null) {
+                return result;
             }
         }
         return null;
@@ -814,7 +815,7 @@
         // Don't open an options panel for honeycomb apps on xlarge devices.
         // (The app should be using an action bar for menu items.)
         if (st.featureId == FEATURE_OPTIONS_PANEL) {
-            Context context = mActivity;
+            Context context = mContext;
             Configuration config = context.getResources().getConfiguration();
             boolean isXLarge = (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) ==
                     Configuration.SCREENLAYOUT_SIZE_XLARGE;
@@ -826,14 +827,14 @@
             }
         }
 
-        WindowCallback cb = getWindowCallback();
+        Window.Callback cb = getWindowCallback();
         if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) {
             // Callback doesn't want the menu to open, reset any state
             closePanel(st, true);
             return;
         }
 
-        final WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
+        final WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         if (wm == null) {
             return;
         }
@@ -914,10 +915,10 @@
 
     private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) {
         if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() &&
-                (!ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity)) ||
+                (!ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mContext)) ||
                         mDecorContentParent.isOverflowMenuShowPending())) {
 
-            final WindowCallback cb = getWindowCallback();
+            final Window.Callback cb = getWindowCallback();
 
             if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) {
                 if (cb != null && !isDestroyed()) {
@@ -957,7 +958,7 @@
     }
 
     private boolean initializePanelMenu(final PanelFeatureState st) {
-        Context context = mActivity;
+        Context context = mContext;
 
         // If we have an action bar, initialize the menu with the right theme.
         if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR) &&
@@ -1035,7 +1036,7 @@
             closePanel(mPreparedPanel, false);
         }
 
-        final WindowCallback cb = getWindowCallback();
+        final Window.Callback cb = getWindowCallback();
 
         if (cb != null) {
             st.createdPanelView = cb.onCreatePanelView(st.featureId);
@@ -1069,7 +1070,7 @@
                 // Creating the panel menu will involve a lot of manipulation;
                 // don't dispatch change events to presenters until we're done.
                 st.menu.stopDispatchingItemsChanged();
-                if (!getWindowCallback().onCreatePanelMenu(st.featureId, st.menu)) {
+                if (!cb.onCreatePanelMenu(st.featureId, st.menu)) {
                     // Ditch the menu created above
                     st.setMenu(null);
 
@@ -1129,7 +1130,7 @@
 
         mClosingActionMenu = true;
         mDecorContentParent.dismissPopups();
-        WindowCallback cb = getWindowCallback();
+        Window.Callback cb = getWindowCallback();
         if (cb != null && !isDestroyed()) {
             cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
         }
@@ -1147,21 +1148,23 @@
             return;
         }
 
-        final WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
-        if ((wm != null) && st.isOpen) {
-            if (st.decorView != null) {
-                wm.removeView(st.decorView);
-            }
+        final boolean wasOpen = st.isOpen;
 
-            if (doCallback) {
-                callOnPanelClosed(st.featureId, st, null);
-            }
+        final WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        if (wm != null && wasOpen && st.decorView != null) {
+            wm.removeView(st.decorView);
         }
 
         st.isPrepared = false;
         st.isHandled = false;
         st.isOpen = false;
 
+        if (wasOpen && doCallback) {
+            // If the panel was open and we should callback, do so. This should be done after
+            // isOpen is updated to ensure that we do not get into an infinite recursion
+            callOnPanelClosed(st.featureId, st, null);
+        }
+
         // This view is no longer shown, so null it out
         st.shownPanelView = null;
 
@@ -1194,7 +1197,7 @@
         final PanelFeatureState st = getPanelState(featureId, true);
         if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
                 mDecorContentParent.canShowOverflowMenu() &&
-                !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity))) {
+                !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mContext))) {
             if (!mDecorContentParent.isOverflowMenuShowing()) {
                 if (!isDestroyed() && preparePanel(st, event)) {
                     playSoundEffect = mDecorContentParent.showOverflowMenu();
@@ -1231,7 +1234,7 @@
         }
 
         if (playSoundEffect) {
-            AudioManager audioManager = (AudioManager) mActivity.getSystemService(
+            AudioManager audioManager = (AudioManager) mContext.getSystemService(
                     Context.AUDIO_SERVICE);
             if (audioManager != null) {
                 audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
@@ -1261,7 +1264,10 @@
         if ((panel != null) && (!panel.isOpen))
             return;
 
-        getWindowCallback().onPanelClosed(featureId, menu);
+        Window.Callback cb = getWindowCallback();
+        if (cb != null) {
+            cb.onPanelClosed(featureId, menu);
+        }
     }
 
     private PanelFeatureState findMenuPanel(Menu menu) {
@@ -1293,7 +1299,7 @@
         return st;
     }
 
-    final boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
+    private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
             int flags) {
         if (event.isSystem()) {
             return false;
@@ -1385,8 +1391,8 @@
                         mlp.topMargin = insetTop;
 
                         if (mStatusGuard == null) {
-                            mStatusGuard = new View(mActivity);
-                            mStatusGuard.setBackgroundColor(mActivity.getResources()
+                            mStatusGuard = new View(mContext);
+                            mStatusGuard.setBackgroundColor(mContext.getResources()
                                     .getColor(R.color.abc_input_method_navigation_guard));
                             mSubDecor.addView(mStatusGuard, -1,
                                     new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
@@ -1433,10 +1439,14 @@
     private void throwFeatureRequestIfSubDecorInstalled() {
         if (mSubDecorInstalled) {
             throw new AndroidRuntimeException(
-                    "supportRequestWindowFeature() must be called before adding content");
+                    "Window feature must be requested before adding content");
         }
     }
 
+    ViewGroup getSubDecor() {
+        return mSubDecor;
+    }
+
     /**
      * Clears out internal reference when the action mode is destroyed.
      */
@@ -1462,7 +1472,7 @@
         public void onDestroyActionMode(ActionMode mode) {
             mWrapped.onDestroyActionMode(mode);
             if (mActionModePopup != null) {
-                mActivity.getWindow().getDecorView().removeCallbacks(mShowActionModePopup);
+                mWindow.getDecorView().removeCallbacks(mShowActionModePopup);
                 mActionModePopup.dismiss();
             } else if (mActionModeView != null) {
                 mActionModeView.setVisibility(View.GONE);
@@ -1473,12 +1483,8 @@
             if (mActionModeView != null) {
                 mActionModeView.removeAllViews();
             }
-            if (mActivity != null) {
-                try {
-                    mActivity.onSupportActionModeFinished(mActionMode);
-                } catch (AbstractMethodError ame) {
-                    // Older apps might not implement this callback method.
-                }
+            if (mAppCompatCallback != null) {
+                mAppCompatCallback.onSupportActionModeFinished(mActionMode);
             }
             mActionMode = null;
         }
@@ -1497,7 +1503,6 @@
                 } else {
                     // Close the panel and only do the callback if the menu is being
                     // closed completely, not if opening a sub menu
-                    mActivity.closeOptionsMenu();
                     closePanel(panel, allMenusAreClosing);
                 }
             }
@@ -1506,7 +1511,7 @@
         @Override
         public boolean onOpenSubMenu(MenuBuilder subMenu) {
             if (subMenu == null && mHasActionBar) {
-                WindowCallback cb = getWindowCallback();
+                Window.Callback cb = getWindowCallback();
                 if (cb != null && !isDestroyed()) {
                     cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);
                 }
@@ -1518,7 +1523,7 @@
     private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
         @Override
         public boolean onOpenSubMenu(MenuBuilder subMenu) {
-            WindowCallback cb = getWindowCallback();
+            Window.Callback cb = getWindowCallback();
             if (cb != null) {
                 cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu);
             }
@@ -1758,7 +1763,7 @@
 
         @Override
         public boolean dispatchKeyEvent(KeyEvent event) {
-            return ActionBarActivityDelegateBase.this.dispatchKeyEvent(event);
+            return AppCompatDelegateImplV7.this.dispatchKeyEvent(event);
         }
 
         @Override
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java b/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java
new file mode 100644
index 0000000..9039342
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java
@@ -0,0 +1,163 @@
+/*
+ * 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.v7.app;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.v7.appcompat.R;
+import android.support.v7.view.ActionMode;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Base class for AppCompat themed {@link android.app.Dialog}s.
+ */
+public class AppCompatDialog extends Dialog implements AppCompatCallback {
+
+    private AppCompatDelegate mDelegate;
+
+    public AppCompatDialog(Context context) {
+        this(context, 0);
+    }
+
+    public AppCompatDialog(Context context, int theme) {
+        super(context, getThemeResId(context, theme));
+
+        // This is a bit weird, but Dialog's are typically created and setup before being shown,
+        // which means that we can't rely on onCreate() being called before a content view is set.
+        // To workaround this, we call onCreate(null) in the ctor, and then again as usual in
+        // onCreate().
+        getDelegate().onCreate(null);
+    }
+
+    protected AppCompatDialog(Context context, boolean cancelable,
+            OnCancelListener cancelListener) {
+        super(context, cancelable, cancelListener);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        getDelegate().installViewFactory();
+        super.onCreate(savedInstanceState);
+        getDelegate().onCreate(savedInstanceState);
+    }
+
+    /**
+     * Support library version of {@link android.app.Dialog#getActionBar}.
+     *
+     * <p>Retrieve a reference to this dialog's ActionBar.
+     *
+     * @return The Dialog's ActionBar, or null if it does not have one.
+     */
+    public ActionBar getSupportActionBar() {
+        return getDelegate().getSupportActionBar();
+    }
+
+    @Override
+    public void setContentView(@LayoutRes int layoutResID) {
+        getDelegate().setContentView(layoutResID);
+    }
+
+    @Override
+    public void setContentView(View view) {
+        getDelegate().setContentView(view);
+    }
+
+    @Override
+    public void setContentView(View view, ViewGroup.LayoutParams params) {
+        getDelegate().setContentView(view, params);
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        super.setTitle(title);
+        getDelegate().setTitle(title);
+    }
+
+    @Override
+    public void setTitle(int titleId) {
+        super.setTitle(titleId);
+        getDelegate().setTitle(getContext().getString(titleId));
+    }
+
+    @Override
+    public void addContentView(View view, ViewGroup.LayoutParams params) {
+        getDelegate().addContentView(view, params);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        getDelegate().onStop();
+    }
+
+    /**
+     * Enable extended support library window features.
+     * <p>
+     * This is a convenience for calling
+     * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
+     * </p>
+     *
+     * @param featureId The desired feature as defined in {@link android.view.Window} or
+     *                  {@link android.support.v4.view.WindowCompat}.
+     * @return Returns true if the requested feature is supported and now enabled.
+     *
+     * @see android.app.Dialog#requestWindowFeature
+     * @see android.view.Window#requestFeature
+     */
+    public boolean supportRequestWindowFeature(int featureId) {
+        return getDelegate().requestWindowFeature(featureId);
+    }
+
+    /**
+     * @hide
+     */
+    public void invalidateOptionsMenu() {
+        getDelegate().invalidateOptionsMenu();
+    }
+
+    /**
+     * @return The {@link AppCompatDelegate} being used by this Dialog.
+     */
+    public AppCompatDelegate getDelegate() {
+        if (mDelegate == null) {
+            mDelegate = AppCompatDelegate.create(this, this);
+        }
+        return mDelegate;
+    }
+
+    private static int getThemeResId(Context context, int themeId) {
+        if (themeId == 0) {
+            // If the provided theme is 0, then retrieve the dialogTheme from our theme
+            TypedValue outValue = new TypedValue();
+            context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
+            themeId = outValue.resourceId;
+        }
+        return themeId;
+    }
+
+    @Override
+    public void onSupportActionModeStarted(ActionMode mode) {
+    }
+
+    @Override
+    public void onSupportActionModeFinished(ActionMode mode) {
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableUtils.java b/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableUtils.java
new file mode 100644
index 0000000..ffe68d5
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableUtils.java
@@ -0,0 +1,40 @@
+/*
+ * 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.v7.graphics.drawable;
+
+import android.graphics.PorterDuff;
+import android.os.Build;
+
+/**
+ * @hide
+ */
+public class DrawableUtils {
+
+    public static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
+        switch (value) {
+            case 3: return PorterDuff.Mode.SRC_OVER;
+            case 5: return PorterDuff.Mode.SRC_IN;
+            case 9: return PorterDuff.Mode.SRC_ATOP;
+            case 14: return PorterDuff.Mode.MULTIPLY;
+            case 15: return PorterDuff.Mode.SCREEN;
+            case 16: return Build.VERSION.SDK_INT >= 11 ? PorterDuff.Mode.valueOf("ADD")
+                    : defaultMode;
+            default: return defaultMode;
+        }
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/DrawableWrapper.java b/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableWrapper.java
similarity index 89%
rename from v7/appcompat/src/android/support/v7/internal/widget/DrawableWrapper.java
rename to v7/appcompat/src/android/support/v7/graphics/drawable/DrawableWrapper.java
index ad15064..b97d07c 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/DrawableWrapper.java
+++ b/v7/appcompat/src/android/support/v7/graphics/drawable/DrawableWrapper.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.v7.internal.widget;
+package android.support.v7.graphics.drawable;
 
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
@@ -23,17 +23,18 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.DrawableContainer;
-import android.os.Build;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.view.View;
 
 /**
- * Base wrapper that delegates all calls to another {@link Drawable}. The wrapped {@link Drawable}
- * <em>must</em> be fully released from any {@link View} before wrapping, otherwise internal {@link
- * Drawable.Callback} may be dropped.
+ * Drawable which delegates all calls to it's wrapped {@link Drawable}.
+ * <p>
+ * The wrapped {@link Drawable} <em>must</em> be fully released from any {@link View}
+ * before wrapping, otherwise internal {@link Drawable.Callback} may be dropped.
+ *
+ * @hide
  */
-class DrawableWrapper extends Drawable implements Drawable.Callback {
+public class DrawableWrapper extends Drawable implements Drawable.Callback {
 
     private Drawable mDrawable;
 
@@ -47,9 +48,8 @@
     }
 
     @Override
-    public void setBounds(int left, int top, int right, int bottom) {
-        super.setBounds(left, top, right, bottom);
-        mDrawable.setBounds(left, top, right, bottom);
+    protected void onBoundsChange(Rect bounds) {
+        mDrawable.setBounds(bounds);
     }
 
     @Override
diff --git a/v7/appcompat/src/android/support/v7/internal/app/AppCompatViewInflater.java b/v7/appcompat/src/android/support/v7/internal/app/AppCompatViewInflater.java
new file mode 100644
index 0000000..c1157d8
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/internal/app/AppCompatViewInflater.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 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.v7.internal.app;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.AppCompatAutoCompleteTextView;
+import android.support.v7.widget.AppCompatButton;
+import android.support.v7.widget.AppCompatCheckBox;
+import android.support.v7.widget.AppCompatCheckedTextView;
+import android.support.v7.widget.AppCompatEditText;
+import android.support.v7.widget.AppCompatMultiAutoCompleteTextView;
+import android.support.v7.widget.AppCompatRadioButton;
+import android.support.v7.widget.AppCompatRatingBar;
+import android.support.v7.widget.AppCompatSpinner;
+import android.support.v7.internal.widget.ViewUtils;
+import android.support.v7.widget.AppCompatTextView;
+import android.util.AttributeSet;
+import android.view.InflateException;
+import android.view.View;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class is responsible for manually inflating our tinted widgets which are used on devices
+ * running {@link android.os.Build.VERSION_CODES#KITKAT KITKAT} or below. As such, this class
+ * should only be used when running on those devices.
+ * <p>This class two main responsibilities: the first is to 'inject' our tinted views in place of
+ * the framework versions in layout inflation; the second is backport the {@code android:theme}
+ * functionality for any inflated widgets. This include theme inheritance from it's parent.
+ *
+ * @hide
+ */
+public class AppCompatViewInflater {
+
+    static final Class<?>[] sConstructorSignature = new Class[] {
+            Context.class, AttributeSet.class};
+
+    private static final Map<String, Constructor<? extends View>> sConstructorMap = new HashMap<>();
+
+    private final Context mContext;
+    private final Object[] mConstructorArgs = new Object[2];
+
+    public AppCompatViewInflater(Context context) {
+        mContext = context;
+    }
+
+    public final View createView(View parent, final String name, @NonNull Context context,
+            @NonNull AttributeSet attrs, boolean inheritContext, boolean themeContext) {
+        final Context originalContext = context;
+
+        // We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
+        // by using the parent's context
+        if (inheritContext && parent != null) {
+            context = parent.getContext();
+        }
+        if (themeContext) {
+            // We then apply the theme on the context, if specified
+            context = ViewUtils.themifyContext(context, attrs, true, true);
+        }
+
+        // We need to 'inject' our tint aware Views in place of the standard framework versions
+        switch (name) {
+            case "EditText":
+                return new AppCompatEditText(context, attrs);
+            case "Spinner":
+                return new AppCompatSpinner(context, attrs);
+            case "CheckBox":
+                return new AppCompatCheckBox(context, attrs);
+            case "RadioButton":
+                return new AppCompatRadioButton(context, attrs);
+            case "CheckedTextView":
+                return new AppCompatCheckedTextView(context, attrs);
+            case "AutoCompleteTextView":
+                return new AppCompatAutoCompleteTextView(context, attrs);
+            case "MultiAutoCompleteTextView":
+                return new AppCompatMultiAutoCompleteTextView(context, attrs);
+            case "RatingBar":
+                return new AppCompatRatingBar(context, attrs);
+            case "Button":
+                return new AppCompatButton(context, attrs);
+            case "TextView":
+                return new AppCompatTextView(context, attrs);
+        }
+
+        if (originalContext != context) {
+            // If the original context does not equal our themed context, then we need to manually
+            // inflate it using the name so that app:theme takes effect.
+            return createViewFromTag(context, name, attrs);
+        }
+
+        return null;
+    }
+
+    private View createViewFromTag(Context context, String name, AttributeSet attrs) {
+        if (name.equals("view")) {
+            name = attrs.getAttributeValue(null, "class");
+        }
+
+        try {
+            mConstructorArgs[0] = context;
+            mConstructorArgs[1] = attrs;
+
+            if (-1 == name.indexOf('.')) {
+                // try the android.widget prefix first...
+                return createView(name, "android.widget.");
+            } else {
+                return createView(name, null);
+            }
+        } catch (Exception e) {
+            // We do not want to catch these, lets return null and let the actual LayoutInflater
+            // try
+            return null;
+        } finally {
+            // Don't retain static reference on context.
+            mConstructorArgs[0] = null;
+            mConstructorArgs[1] = null;
+        }
+    }
+
+    private View createView(String name, String prefix)
+            throws ClassNotFoundException, InflateException {
+        Constructor<? extends View> constructor = sConstructorMap.get(name);
+
+        try {
+            if (constructor == null) {
+                // Class not found in the cache, see if it's real, and try to add it
+                Class<? extends View> clazz = mContext.getClassLoader().loadClass(
+                        prefix != null ? (prefix + name) : name).asSubclass(View.class);
+
+                constructor = clazz.getConstructor(sConstructorSignature);
+                sConstructorMap.put(name, constructor);
+            }
+            constructor.setAccessible(true);
+            return constructor.newInstance(mConstructorArgs);
+        } catch (Exception e) {
+            // We do not want to catch these, lets return null and let the actual LayoutInflater
+            // try
+            return null;
+        }
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/internal/app/ToolbarActionBar.java b/v7/appcompat/src/android/support/v7/internal/app/ToolbarActionBar.java
index 5c63889..9653f42 100644
--- a/v7/appcompat/src/android/support/v7/internal/app/ToolbarActionBar.java
+++ b/v7/appcompat/src/android/support/v7/internal/app/ToolbarActionBar.java
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 package android.support.v7.internal.app;
 
 import android.content.Context;
@@ -25,15 +24,14 @@
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.WindowCompat;
 import android.support.v7.app.ActionBar;
+import android.support.v7.internal.view.WindowCallbackWrapper;
 import android.support.v7.appcompat.R;
 import android.support.v7.internal.view.menu.ListMenuPresenter;
 import android.support.v7.internal.view.menu.MenuBuilder;
 import android.support.v7.internal.view.menu.MenuPresenter;
 import android.support.v7.internal.widget.DecorToolbar;
 import android.support.v7.internal.widget.ToolbarWidgetWrapper;
-import android.support.v7.view.ActionMode;
 import android.support.v7.widget.Toolbar;
-import android.support.v7.widget.WindowCallbackWrapper;
 import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
@@ -52,7 +50,7 @@
 public class ToolbarActionBar extends ActionBar {
     private DecorToolbar mDecorToolbar;
     private boolean mToolbarMenuPrepared;
-    private WindowCallback mWindowCallback;
+    private Window.Callback mWindowCallback;
     private boolean mMenuCallbackSet;
 
     private boolean mLastMenuVisibility;
@@ -77,10 +75,9 @@
                 }
             };
 
-    public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window window,
-            WindowCallback windowCallback) {
+    public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window window) {
         mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false);
-        mWindowCallback = new ToolbarCallbackWrapper(windowCallback);
+        mWindowCallback = new ToolbarCallbackWrapper(window.getCallback());
         mDecorToolbar.setWindowCallback(mWindowCallback);
         toolbar.setOnMenuItemClickListener(mMenuClicker);
         mDecorToolbar.setWindowTitle(title);
@@ -88,7 +85,7 @@
         mWindow = window;
     }
 
-    public WindowCallback getWrappedWindowCallback() {
+    public Window.Callback getWrappedWindowCallback() {
         return mWindowCallback;
     }
 
@@ -200,11 +197,6 @@
     }
 
     @Override
-    public ActionMode startActionMode(ActionMode.Callback callback) {
-        return mWindowCallback.startActionMode(callback);
-    }
-
-    @Override
     public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
         mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback));
     }
@@ -540,7 +532,7 @@
     }
 
     private class ToolbarCallbackWrapper extends WindowCallbackWrapper {
-        public ToolbarCallbackWrapper(WindowCallback wrapped) {
+        public ToolbarCallbackWrapper(Window.Callback wrapped) {
             super(wrapped);
         }
 
@@ -559,9 +551,7 @@
             switch (featureId) {
                 case Window.FEATURE_OPTIONS_PANEL:
                     final Menu menu = mDecorToolbar.getMenu();
-                    if (mWindowCallback != null &&
-                            mWindowCallback.onPreparePanel(featureId, null, menu) &&
-                            mWindowCallback.onMenuOpened(featureId, menu)) {
+                    if (onPreparePanel(featureId, null, menu) && onMenuOpened(featureId, menu)) {
                         return getListMenuView(menu);
                     }
                     break;
@@ -612,9 +602,6 @@
             if (mWindowCallback != null) {
                 mWindowCallback.onPanelClosed(Window.FEATURE_OPTIONS_PANEL, menu);
             }
-
-            // Close the options panel
-            mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
         }
 
         @Override
diff --git a/v7/appcompat/src/android/support/v7/internal/app/WindowCallback.java b/v7/appcompat/src/android/support/v7/internal/app/WindowCallback.java
deleted file mode 100644
index 5137f16..0000000
--- a/v7/appcompat/src/android/support/v7/internal/app/WindowCallback.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.app;
-
-import android.support.v7.view.ActionMode;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-
-/**
- * Interface which allows us to intercept and control calls to {@link android.view.Window.Callback}.
- * Used by ActionBarActivityDelegates.
- *
- * @hide
- */
-public interface WindowCallback {
-
-    boolean onMenuItemSelected(int featureId, MenuItem menuItem);
-
-    boolean onCreatePanelMenu(int featureId, Menu menu);
-
-    boolean onPreparePanel(int featureId, View menuView, Menu menu);
-
-    void onPanelClosed(int featureId, Menu menu);
-
-    boolean onMenuOpened(int featureId, Menu menu);
-
-    ActionMode startActionMode(ActionMode.Callback callback);
-
-    View onCreatePanelView(int featureId);
-
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/app/WindowDecorActionBar.java b/v7/appcompat/src/android/support/v7/internal/app/WindowDecorActionBar.java
index 6eccb1d..56549b1 100644
--- a/v7/appcompat/src/android/support/v7/internal/app/WindowDecorActionBar.java
+++ b/v7/appcompat/src/android/support/v7/internal/app/WindowDecorActionBar.java
@@ -16,6 +16,7 @@
 
 package android.support.v7.internal.app;
 
+import android.app.Activity;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -31,7 +32,6 @@
 import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
 import android.support.v4.view.ViewPropertyAnimatorUpdateListener;
 import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarActivity;
 import android.support.v7.appcompat.R;
 import android.support.v7.internal.view.ActionBarPolicy;
 import android.support.v7.internal.view.ViewPropertyAnimatorCompatSet;
@@ -83,7 +83,7 @@
 
     private Context mContext;
     private Context mThemedContext;
-    private FragmentActivity mActivity;
+    private Activity mActivity;
     private Dialog mDialog;
 
     private ActionBarOverlayLayout mOverlayLayout;
@@ -169,7 +169,7 @@
                 }
             };
 
-    public WindowDecorActionBar(ActionBarActivity activity, boolean overlayMode) {
+    public WindowDecorActionBar(Activity activity, boolean overlayMode) {
         mActivity = activity;
         Window window = activity.getWindow();
         View decor = window.getDecorView();
@@ -616,8 +616,14 @@
             return;
         }
 
-        final FragmentTransaction trans = mDecorToolbar.getViewGroup().isInEditMode() ? null :
-                mActivity.getSupportFragmentManager().beginTransaction().disallowAddToBackStack();
+        final FragmentTransaction trans;
+        if (mActivity instanceof FragmentActivity && !mDecorToolbar.getViewGroup().isInEditMode()) {
+            // If we're not in edit mode and our Activity is a FragmentActivity, start a tx
+            trans = ((FragmentActivity) mActivity).getSupportFragmentManager()
+                    .beginTransaction().disallowAddToBackStack();
+        } else {
+            trans = null;
+        }
 
         if (mSelectedTab == tab) {
             if (mSelectedTab != null) {
@@ -1001,6 +1007,13 @@
 
         @Override
         public void invalidate() {
+            if (mActionMode != this) {
+                // Not the active action mode - no-op. It's possible we are
+                // currently deferring onDestroy, so the app doesn't yet know we
+                // are going away and is trying to use us. That's also a no-op.
+                return;
+            }
+
             mMenu.stopDispatchingItemsChanged();
             try {
                 mCallback.onPrepareActionMode(this, mMenu);
@@ -1348,7 +1361,7 @@
 
     TintManager getTintManager() {
         if (mTintManager == null) {
-            mTintManager = new TintManager(mContext);
+            mTintManager = TintManager.get(mContext);
         }
         return mTintManager;
     }
diff --git a/v7/appcompat/src/android/support/v7/internal/view/ContextThemeWrapper.java b/v7/appcompat/src/android/support/v7/internal/view/ContextThemeWrapper.java
new file mode 100644
index 0000000..e414203
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/internal/view/ContextThemeWrapper.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014 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.v7.internal.view;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.support.v7.appcompat.R;
+import android.view.LayoutInflater;
+
+/**
+ * A ContextWrapper that allows you to modify the theme from what is in the
+ * wrapped context.
+ *
+ * @hide
+ */
+public class ContextThemeWrapper extends ContextWrapper {
+    private int mThemeResource;
+    private Resources.Theme mTheme;
+    private LayoutInflater mInflater;
+
+    public ContextThemeWrapper(Context base, int themeres) {
+        super(base);
+        mThemeResource = themeres;
+    }
+
+    @Override
+    public void setTheme(int resid) {
+        mThemeResource = resid;
+        initializeTheme();
+    }
+
+    public int getThemeResId() {
+        return mThemeResource;
+    }
+
+    @Override
+    public Resources.Theme getTheme() {
+        if (mTheme != null) {
+            return mTheme;
+        }
+
+        if (mThemeResource == 0) {
+            mThemeResource = R.style.Theme_AppCompat_Light;
+        }
+        initializeTheme();
+
+        return mTheme;
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
+            if (mInflater == null) {
+                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
+            }
+            return mInflater;
+        }
+        return getBaseContext().getSystemService(name);
+    }
+
+    /**
+     * Called by {@link #setTheme} and {@link #getTheme} to apply a theme
+     * resource to the current Theme object.  Can override to change the
+     * default (simple) behavior.  This method will not be called in multiple
+     * threads simultaneously.
+     *
+     * @param theme The Theme object being modified.
+     * @param resid The theme style resource being applied to <var>theme</var>.
+     * @param first Set to true if this is the first time a style is being
+     *              applied to <var>theme</var>.
+     */
+    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
+        theme.applyStyle(resid, true);
+    }
+
+    private void initializeTheme() {
+        final boolean first = mTheme == null;
+        if (first) {
+            mTheme = getResources().newTheme();
+            Resources.Theme theme = getBaseContext().getTheme();
+            if (theme != null) {
+                mTheme.setTo(theme);
+            }
+        }
+        onApplyThemeResource(mTheme, mThemeResource, first);
+    }
+}
+
diff --git a/v7/appcompat/src/android/support/v7/internal/view/SupportMenuInflater.java b/v7/appcompat/src/android/support/v7/internal/view/SupportMenuInflater.java
index 84a07da..662d4eb 100644
--- a/v7/appcompat/src/android/support/v7/internal/view/SupportMenuInflater.java
+++ b/v7/appcompat/src/android/support/v7/internal/view/SupportMenuInflater.java
@@ -22,12 +22,16 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
 import android.support.v4.internal.view.SupportMenu;
 import android.support.v4.view.ActionProvider;
 import android.support.v4.view.MenuItemCompat;
 import android.support.v7.appcompat.R;
+import android.support.v7.graphics.drawable.DrawableUtils;
 import android.support.v7.internal.view.menu.MenuItemImpl;
 import android.support.v7.internal.view.menu.MenuItemWrapperICS;
 import android.util.AttributeSet;
@@ -317,6 +321,11 @@
 
         private ActionProvider itemActionProvider;
 
+        private ColorStateList itemIconTintList;
+        private boolean itemIconTintListSet;
+        private PorterDuff.Mode itemIconTintMode;
+        private boolean itemIconTintModeSet;
+
         private static final int defaultGroupId = NO_ID;
         private static final int defaultItemId = NO_ID;
         private static final int defaultItemCategory = 0;
@@ -408,6 +417,22 @@
                 itemActionProvider = null;
             }
 
+            if (a.hasValue(R.styleable.MenuItem_iconTint)) {
+                itemIconTintList = a.getColorStateList(R.styleable.MenuItem_iconTint);
+                itemIconTintListSet = true;
+            } else {
+                itemIconTintList = null;
+                itemIconTintListSet = false;
+            }
+            if (a.hasValue(R.styleable.MenuItem_iconTintMode)) {
+                itemIconTintMode = DrawableUtils.parseTintMode(
+                        a.getInt(R.styleable.MenuItem_iconTintMode, -1), null);
+                itemIconTintModeSet = true;
+            } else {
+                itemIconTintMode = null;
+                itemIconTintModeSet = false;
+            }
+
             a.recycle();
 
             itemAdded = false;
@@ -472,6 +497,13 @@
             if (itemActionProvider != null) {
                 MenuItemCompat.setActionProvider(item, itemActionProvider);
             }
+
+            if (itemIconTintListSet) {
+                MenuItemCompat.setIconTintList(item, itemIconTintList);
+            }
+            if (itemIconTintModeSet) {
+                MenuItemCompat.setIconTintMode(item, itemIconTintMode);
+            }
         }
 
         public void addItem() {
diff --git a/v7/appcompat/src/android/support/v7/internal/view/WindowCallbackWrapper.java b/v7/appcompat/src/android/support/v7/internal/view/WindowCallbackWrapper.java
new file mode 100644
index 0000000..87718e6
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/internal/view/WindowCallbackWrapper.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2014 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.v7.internal.view;
+
+import android.view.ActionMode;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * A simple decorator stub for Window.Callback that passes through any calls
+ * to the wrapped instance as a base implementation. Call super.foo() to call into
+ * the wrapped callback for any subclasses.
+ *
+ * @hide
+ */
+public class WindowCallbackWrapper implements Window.Callback {
+
+    final Window.Callback mWrapped;
+
+    public WindowCallbackWrapper(Window.Callback wrapped) {
+        if (wrapped == null) {
+            throw new IllegalArgumentException("Window callback may not be null");
+        }
+        mWrapped = wrapped;
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        return mWrapped.dispatchKeyEvent(event);
+    }
+
+    @Override
+    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+        return mWrapped.dispatchKeyShortcutEvent(event);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        return mWrapped.dispatchTouchEvent(event);
+    }
+
+    @Override
+    public boolean dispatchTrackballEvent(MotionEvent event) {
+        return mWrapped.dispatchTrackballEvent(event);
+    }
+
+    @Override
+    public boolean dispatchGenericMotionEvent(MotionEvent event) {
+        return mWrapped.dispatchGenericMotionEvent(event);
+    }
+
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        return mWrapped.dispatchPopulateAccessibilityEvent(event);
+    }
+
+    @Override
+    public View onCreatePanelView(int featureId) {
+        return mWrapped.onCreatePanelView(featureId);
+    }
+
+    @Override
+    public boolean onCreatePanelMenu(int featureId, Menu menu) {
+        return mWrapped.onCreatePanelMenu(featureId, menu);
+    }
+
+    @Override
+    public boolean onPreparePanel(int featureId, View view, Menu menu) {
+        return mWrapped.onPreparePanel(featureId, view, menu);
+    }
+
+    @Override
+    public boolean onMenuOpened(int featureId, Menu menu) {
+        return mWrapped.onMenuOpened(featureId, menu);
+    }
+
+    @Override
+    public boolean onMenuItemSelected(int featureId, MenuItem item) {
+        return mWrapped.onMenuItemSelected(featureId, item);
+    }
+
+    @Override
+    public void onWindowAttributesChanged(WindowManager.LayoutParams attrs) {
+        mWrapped.onWindowAttributesChanged(attrs);
+    }
+
+    @Override
+    public void onContentChanged() {
+        mWrapped.onContentChanged();
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        mWrapped.onWindowFocusChanged(hasFocus);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        mWrapped.onAttachedToWindow();
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        mWrapped.onDetachedFromWindow();
+    }
+
+    @Override
+    public void onPanelClosed(int featureId, Menu menu) {
+        mWrapped.onPanelClosed(featureId, menu);
+    }
+
+    @Override
+    public boolean onSearchRequested() {
+        return mWrapped.onSearchRequested();
+    }
+
+    @Override
+    public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
+        return mWrapped.onWindowStartingActionMode(callback);
+    }
+
+    @Override
+    public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
+        return mWrapped.onWindowStartingActionMode(callback, type);
+    }
+
+    @Override
+    public void onActionModeStarted(ActionMode mode) {
+        mWrapped.onActionModeStarted(mode);
+    }
+
+    @Override
+    public void onActionModeFinished(ActionMode mode) {
+        mWrapped.onActionModeFinished(mode);
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItem.java b/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItem.java
index b466a0b..bf1db65 100644
--- a/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItem.java
+++ b/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItem.java
@@ -18,8 +18,11 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.view.ActionProvider;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.support.v4.view.MenuItemCompat;
@@ -46,6 +49,7 @@
 
     private Drawable mIconDrawable;
     private int mIconResId = NO_ICON;
+    private TintInfo mIconTintInfo;
 
     private Context mContext;
 
@@ -162,12 +166,14 @@
     public MenuItem setIcon(Drawable icon) {
         mIconDrawable = icon;
         mIconResId = NO_ICON;
+        applyIconTint();
         return this;
     }
 
     public MenuItem setIcon(int iconRes) {
         mIconResId = iconRes;
         mIconDrawable = ContextCompat.getDrawable(mContext, iconRes);
+        applyIconTint();
         return this;
     }
 
@@ -293,4 +299,48 @@
         // No need to save the listener; ActionMenuItem does not support collapsing items.
         return this;
     }
+
+    @Override
+    public MenuItem setIconTintList(ColorStateList tintList) {
+        if (mIconTintInfo == null) {
+            mIconTintInfo = new TintInfo();
+        }
+        mIconTintInfo.mTintList = tintList;
+        mIconTintInfo.mHasTintList = true;
+        applyIconTint();
+        return this;
+    }
+
+    @Override
+    public MenuItem setIconTintMode(PorterDuff.Mode tintMode) {
+        if (mIconTintInfo == null) {
+            mIconTintInfo = new TintInfo();
+        }
+        mIconTintInfo.mTintMode = tintMode;
+        mIconTintInfo.mHasTintMode = true;
+        applyIconTint();
+        return this;
+    }
+
+    private void applyIconTint() {
+        final TintInfo tintInfo = mIconTintInfo;
+        if (mIconDrawable != null && tintInfo != null) {
+            if (tintInfo.mHasTintList || tintInfo.mHasTintMode) {
+                mIconDrawable = DrawableCompat.wrap(mIconDrawable.mutate());
+                if (tintInfo.mHasTintList) {
+                    DrawableCompat.setTintList(mIconDrawable, tintInfo.mTintList);
+                }
+                if (tintInfo.mHasTintMode) {
+                    DrawableCompat.setTintMode(mIconDrawable, tintInfo.mTintMode);
+                }
+            }
+        }
+    }
+
+    private static class TintInfo {
+        ColorStateList mTintList;
+        PorterDuff.Mode mTintMode;
+        boolean mHasTintMode;
+        boolean mHasTintList;
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java b/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java
index 0893673..05c92c9 100644
--- a/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java
+++ b/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java
@@ -26,24 +26,20 @@
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
-import android.support.v7.internal.text.AllCapsTransformationMethod;
-import android.support.v7.internal.widget.CompatTextView;
 import android.support.v7.widget.ActionMenuView;
+import android.support.v7.widget.AppCompatTextView;
 import android.support.v7.widget.ListPopupWindow;
 import android.text.TextUtils;
-import android.text.method.TransformationMethod;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.Toast;
 
-import java.util.Locale;
-
 /**
  * @hide
  */
-public class ActionMenuItemView extends CompatTextView
+public class ActionMenuItemView extends AppCompatTextView
         implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
         ActionMenuView.ActionMenuChildView {
 
@@ -89,8 +85,6 @@
         setOnClickListener(this);
         setOnLongClickListener(this);
 
-        setTransformationMethod(new AllCapsTransformationMethod(context));
-
         mSavedPaddingLeft = -1;
     }
 
diff --git a/v7/appcompat/src/android/support/v7/internal/view/menu/MenuItemImpl.java b/v7/appcompat/src/android/support/v7/internal/view/menu/MenuItemImpl.java
index a2e9783..5ebf394 100644
--- a/v7/appcompat/src/android/support/v7/internal/view/menu/MenuItemImpl.java
+++ b/v7/appcompat/src/android/support/v7/internal/view/menu/MenuItemImpl.java
@@ -19,9 +19,11 @@
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
-import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.view.ActionProvider;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.support.v4.view.MenuItemCompat;
@@ -66,6 +68,11 @@
      */
     private int mIconResId = NO_ICON;
 
+    /**
+     * Tint info for the icon
+     */
+    private TintInfo mIconTintInfo;
+
     /** The menu to which this item belongs */
     private MenuBuilder mMenu;
     /** If this item should launch a sub menu, this is the sub menu to launch */
@@ -118,19 +125,6 @@
      */
     MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
             CharSequence title, int showAsAction) {
-
-        /*if (sPrependShortcutLabel == null) {
-          // This is instantiated from the UI thread, so no chance of sync issues
-          sPrependShortcutLabel = menu.getContext().getResources().getString(
-              com.android.internal.R.string.prepend_shortcut_label);
-          sEnterShortcutLabel = menu.getContext().getResources().getString(
-              com.android.internal.R.string.menu_enter_shortcut_label);
-          sDeleteShortcutLabel = menu.getContext().getResources().getString(
-              com.android.internal.R.string.menu_delete_shortcut_label);
-          sSpaceShortcutLabel = menu.getContext().getResources().getString(
-              com.android.internal.R.string.menu_space_shortcut_label);
-        }*/
-
         mMenu = menu;
         mId = id;
         mGroup = group;
@@ -419,10 +413,10 @@
         }
 
         if (mIconResId != NO_ICON) {
-            Drawable icon = TintManager.getDrawable(mMenu.getContext(), mIconResId);
+            mIconDrawable = TintManager.getDrawable(mMenu.getContext(), mIconResId);
             mIconResId = NO_ICON;
-            mIconDrawable = icon;
-            return icon;
+            applyIconTint();
+            return mIconDrawable;
         }
 
         return null;
@@ -432,6 +426,7 @@
     public MenuItem setIcon(Drawable icon) {
         mIconResId = NO_ICON;
         mIconDrawable = icon;
+        applyIconTint();
         mMenu.onItemsChanged(false);
 
         return this;
@@ -538,7 +533,7 @@
 
     @Override
     public String toString() {
-        return mTitle.toString();
+        return mTitle != null ? mTitle.toString() : null;
     }
 
     void setMenuInfo(ContextMenuInfo menuInfo) {
@@ -715,6 +710,28 @@
         return this;
     }
 
+    @Override
+    public MenuItem setIconTintList(ColorStateList tintList) {
+        if (mIconTintInfo == null) {
+            mIconTintInfo = new TintInfo();
+        }
+        mIconTintInfo.mTintList = tintList;
+        mIconTintInfo.mHasTintList = true;
+        applyIconTint();
+        return this;
+    }
+
+    @Override
+    public MenuItem setIconTintMode(PorterDuff.Mode tintMode) {
+        if (mIconTintInfo == null) {
+            mIconTintInfo = new TintInfo();
+        }
+        mIconTintInfo.mTintMode = tintMode;
+        mIconTintInfo.mHasTintMode = true;
+        applyIconTint();
+        return this;
+    }
+
     public boolean hasCollapsibleActionView() {
         if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0) {
             if (mActionView == null && mActionProvider != null) {
@@ -740,4 +757,26 @@
         throw new UnsupportedOperationException(
                 "This is not supported, use MenuItemCompat.setOnActionExpandListener()");
     }
+
+    private void applyIconTint() {
+        final TintInfo tintInfo = mIconTintInfo;
+        if (mIconDrawable != null && tintInfo != null) {
+            if (tintInfo.mHasTintList || tintInfo.mHasTintMode) {
+                mIconDrawable = DrawableCompat.wrap(mIconDrawable.mutate());
+                if (tintInfo.mHasTintList) {
+                    DrawableCompat.setTintList(mIconDrawable, tintInfo.mTintList);
+                }
+                if (tintInfo.mHasTintMode) {
+                    DrawableCompat.setTintMode(mIconDrawable, tintInfo.mTintMode);
+                }
+            }
+        }
+    }
+
+    private static class TintInfo {
+        ColorStateList mTintList;
+        PorterDuff.Mode mTintMode;
+        boolean mHasTintMode;
+        boolean mHasTintList;
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/internal/view/menu/MenuItemWrapperICS.java b/v7/appcompat/src/android/support/v7/internal/view/menu/MenuItemWrapperICS.java
index 3e6a99a..e86a0f3 100644
--- a/v7/appcompat/src/android/support/v7/internal/view/menu/MenuItemWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/internal/view/menu/MenuItemWrapperICS.java
@@ -19,6 +19,8 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.v4.internal.view.SupportMenuItem;
@@ -27,6 +29,7 @@
 import android.support.v7.view.CollapsibleActionView;
 import android.util.Log;
 import android.view.ContextMenu;
+import android.view.Menu;
 import android.view.MenuItem;
 import android.view.SubMenu;
 import android.view.View;
@@ -293,6 +296,18 @@
         return this;
     }
 
+    @Override
+    public MenuItem setIconTintList(ColorStateList tint) {
+        mWrappedObject.setIconTintList(tint);
+        return this;
+    }
+
+    @Override
+    public MenuItem setIconTintMode(PorterDuff.Mode tintMode) {
+        mWrappedObject.setIconTintMode(tintMode);
+        return this;
+    }
+
     public void setExclusiveCheckable(boolean checkable) {
         try {
             if (mSetExclusiveCheckableMethod == null) {
diff --git a/v7/appcompat/src/android/support/v7/internal/view/menu/MenuPopupHelper.java b/v7/appcompat/src/android/support/v7/internal/view/menu/MenuPopupHelper.java
index af7deef..bdcc79a 100644
--- a/v7/appcompat/src/android/support/v7/internal/view/menu/MenuPopupHelper.java
+++ b/v7/appcompat/src/android/support/v7/internal/view/menu/MenuPopupHelper.java
@@ -121,6 +121,10 @@
         mDropDownGravity = gravity;
     }
 
+    public int getGravity() {
+        return mDropDownGravity;
+    }
+
     public void show() {
         if (!tryShow()) {
             throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/ActionBarOverlayLayout.java b/v7/appcompat/src/android/support/v7/internal/widget/ActionBarOverlayLayout.java
index 9d9673d..6f2ebdf 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/ActionBarOverlayLayout.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ActionBarOverlayLayout.java
@@ -31,7 +31,6 @@
 import android.support.v4.widget.ScrollerCompat;
 import android.support.v7.appcompat.R;
 import android.support.v7.internal.VersionUtils;
-import android.support.v7.internal.app.WindowCallback;
 import android.support.v7.internal.view.menu.MenuPresenter;
 import android.support.v7.widget.Toolbar;
 import android.util.AttributeSet;
@@ -668,7 +667,7 @@
     }
 
     @Override
-    public void setWindowCallback(WindowCallback cb) {
+    public void setWindowCallback(Window.Callback cb) {
         pullChildren();
         mDecorToolbar.setWindowCallback(cb);
     }
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/AppCompatPopupWindow.java b/v7/appcompat/src/android/support/v7/internal/widget/AppCompatPopupWindow.java
index 028570b..d71d606 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/AppCompatPopupWindow.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/AppCompatPopupWindow.java
@@ -18,35 +18,50 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.v4.widget.PopupWindowCompat;
 import android.support.v7.appcompat.R;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
+import android.view.ViewTreeObserver.OnScrollChangedListener;
 import android.widget.PopupWindow;
 
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+
 /**
  * @hide
  */
 public class AppCompatPopupWindow extends PopupWindow {
 
-    private final boolean mOverlapAnchor;
+    private static final String TAG = "AppCompatPopupWindow";
+    private static final boolean COMPAT_OVERLAP_ANCHOR = Build.VERSION.SDK_INT < 21;
+
+    private boolean mOverlapAnchor;
 
     public AppCompatPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
         TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
                 R.styleable.PopupWindow, defStyleAttr, 0);
-        mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
+        if (a.hasValue(R.styleable.PopupWindow_overlapAnchor)) {
+            setSupportOverlapAnchor(a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false));
+        }
         // We re-set this for tinting purposes
         setBackgroundDrawable(a.getDrawable(R.styleable.PopupWindow_android_popupBackground));
         a.recycle();
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            // For devices pre-ICS, we need to wrap the internal OnScrollChangedListener
+            // due to NPEs.
+            wrapOnScrollChangedListener(this);
+        }
     }
 
     @Override
     public void showAsDropDown(View anchor, int xoff, int yoff) {
-        if (Build.VERSION.SDK_INT < 21 && mOverlapAnchor) {
+        if (COMPAT_OVERLAP_ANCHOR && mOverlapAnchor) {
             // If we're pre-L, emulate overlapAnchor by modifying the yOff
             yoff -= anchor.getHeight();
         }
@@ -56,7 +71,7 @@
     @TargetApi(Build.VERSION_CODES.KITKAT)
     @Override
     public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
-        if (Build.VERSION.SDK_INT < 21 && mOverlapAnchor) {
+        if (COMPAT_OVERLAP_ANCHOR && mOverlapAnchor) {
             // If we're pre-L, emulate overlapAnchor by modifying the yOff
             yoff -= anchor.getHeight();
         }
@@ -65,10 +80,67 @@
 
     @Override
     public void update(View anchor, int xoff, int yoff, int width, int height) {
-        if (Build.VERSION.SDK_INT < 21 && mOverlapAnchor) {
+        if (COMPAT_OVERLAP_ANCHOR && mOverlapAnchor) {
             // If we're pre-L, emulate overlapAnchor by modifying the yOff
             yoff -= anchor.getHeight();
         }
         super.update(anchor, xoff, yoff, width, height);
     }
+
+    private static void wrapOnScrollChangedListener(final PopupWindow popup) {
+        try {
+            final Field fieldAnchor = PopupWindow.class.getDeclaredField("mAnchor");
+            fieldAnchor.setAccessible(true);
+
+            final Field fieldListener = PopupWindow.class
+                    .getDeclaredField("mOnScrollChangedListener");
+            fieldListener.setAccessible(true);
+
+            final OnScrollChangedListener originalListener =
+                    (OnScrollChangedListener) fieldListener.get(popup);
+
+            // Now set a new listener, wrapping the original and only proxying the call when
+            // we have an anchor view.
+            fieldListener.set(popup, new OnScrollChangedListener() {
+                @Override
+                public void onScrollChanged() {
+                    try {
+                        WeakReference<View> mAnchor = (WeakReference<View>) fieldAnchor.get(popup);
+                        if (mAnchor == null || mAnchor.get() == null) {
+                            return;
+                        } else {
+                            originalListener.onScrollChanged();
+                        }
+                    } catch (IllegalAccessException e) {
+                        // Oh well...
+                    }
+                }
+            });
+        } catch (Exception e) {
+            Log.d(TAG, "Exception while installing workaround OnScrollChangedListener", e);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void setSupportOverlapAnchor(boolean overlapAnchor) {
+        if (COMPAT_OVERLAP_ANCHOR) {
+            mOverlapAnchor = overlapAnchor;
+        } else {
+            PopupWindowCompat.setOverlapAnchor(this, overlapAnchor);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public boolean getSupportOverlapAnchor() {
+        if (COMPAT_OVERLAP_ANCHOR) {
+            return mOverlapAnchor;
+        } else {
+            return PopupWindowCompat.getOverlapAnchor(this);
+        }
+    }
+
 }
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/CompatTextView.java b/v7/appcompat/src/android/support/v7/internal/widget/CompatTextView.java
deleted file mode 100644
index 50f50fc..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/CompatTextView.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2013 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.v7.internal.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.support.v7.appcompat.R;
-import android.support.v7.internal.text.AllCapsTransformationMethod;
-import android.text.method.TransformationMethod;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView;
-
-import java.util.Locale;
-
-/**
- * @hide
- */
-public class CompatTextView extends TextView {
-
-    public CompatTextView(Context context) {
-        this(context, null);
-    }
-
-    public CompatTextView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public CompatTextView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        boolean allCaps = false;
-
-        TypedArray style = context
-                .obtainStyledAttributes(attrs, R.styleable.CompatTextView, defStyle, 0);
-        allCaps = style.getBoolean(R.styleable.CompatTextView_textAllCaps, false);
-        style.recycle();
-
-        // Framework impl also checks TextAppearance for textAllCaps. This isn't needed for our
-        // purposes so has been omitted.
-
-        if (allCaps) {
-            setTransformationMethod(new AllCapsTransformationMethod(context));
-        }
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/ContentFrameLayout.java b/v7/appcompat/src/android/support/v7/internal/widget/ContentFrameLayout.java
index 1f140d1..7de9c8b 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/ContentFrameLayout.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ContentFrameLayout.java
@@ -18,14 +18,30 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
 import android.widget.FrameLayout;
 
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.getMode;
+
 /**
  * @hide
  */
 public class ContentFrameLayout extends FrameLayout {
 
+    private TypedValue mMinWidthMajor;
+    private TypedValue mMinWidthMinor;
+    private TypedValue mFixedWidthMajor;
+    private TypedValue mFixedWidthMinor;
+    private TypedValue mFixedHeightMajor;
+    private TypedValue mFixedHeightMinor;
+
+    private final Rect mDecorPadding;
+
     public ContentFrameLayout(Context context) {
         this(context, null);
     }
@@ -36,6 +52,7 @@
 
     public ContentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        mDecorPadding = new Rect();
     }
 
     /**
@@ -45,4 +62,123 @@
         fitSystemWindows(insets);
     }
 
+    /**
+     * Notify this view of the window decor view's padding. We use these values when working out
+     * our size for the window size attributes.
+     *
+     * @hide
+     */
+    public void setDecorPadding(int left, int top, int right, int bottom) {
+        mDecorPadding.set(left, top, right, bottom);
+        if (ViewCompat.isLaidOut(this)) {
+            requestLayout();
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+        final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
+
+        final int widthMode = getMode(widthMeasureSpec);
+        final int heightMode = getMode(heightMeasureSpec);
+
+        boolean fixedWidth = false;
+        if (widthMode == AT_MOST) {
+            final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
+            if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
+                int w = 0;
+                if (tvw.type == TypedValue.TYPE_DIMENSION) {
+                    w = (int) tvw.getDimension(metrics);
+                } else if (tvw.type == TypedValue.TYPE_FRACTION) {
+                    w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
+                }
+                if (w > 0) {
+                    w -= (mDecorPadding.left + mDecorPadding.right);
+                    final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            Math.min(w, widthSize), EXACTLY);
+                    fixedWidth = true;
+                }
+            }
+        }
+
+        if (heightMode == AT_MOST) {
+            final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
+            if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
+                int h = 0;
+                if (tvh.type == TypedValue.TYPE_DIMENSION) {
+                    h = (int) tvh.getDimension(metrics);
+                } else if (tvh.type == TypedValue.TYPE_FRACTION) {
+                    h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
+                }
+                if (h > 0) {
+                    h -= (mDecorPadding.top + mDecorPadding.bottom);
+                    final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            Math.min(h, heightSize), EXACTLY);
+                }
+            }
+        }
+
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        int width = getMeasuredWidth();
+        boolean measure = false;
+
+        widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
+
+        if (!fixedWidth && widthMode == AT_MOST) {
+            final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
+            if (tv != null && tv.type != TypedValue.TYPE_NULL) {
+                int min = 0;
+                if (tv.type == TypedValue.TYPE_DIMENSION) {
+                    min = (int) tv.getDimension(metrics);
+                } else if (tv.type == TypedValue.TYPE_FRACTION) {
+                    min = (int) tv.getFraction(metrics.widthPixels, metrics.widthPixels);
+                }
+                if (min > 0) {
+                    min -= (mDecorPadding.left + mDecorPadding.right);
+                }
+                if (width < min) {
+                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
+                    measure = true;
+                }
+            }
+        }
+
+        if (measure) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    public TypedValue getMinWidthMajor() {
+        if (mMinWidthMajor == null) mMinWidthMajor = new TypedValue();
+        return mMinWidthMajor;
+    }
+
+    public TypedValue getMinWidthMinor() {
+        if (mMinWidthMinor == null) mMinWidthMinor = new TypedValue();
+        return mMinWidthMinor;
+    }
+
+    public TypedValue getFixedWidthMajor() {
+        if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
+        return mFixedWidthMajor;
+    }
+
+    public TypedValue getFixedWidthMinor() {
+        if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
+        return mFixedWidthMinor;
+    }
+
+    public TypedValue getFixedHeightMajor() {
+        if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
+        return mFixedHeightMajor;
+    }
+
+    public TypedValue getFixedHeightMinor() {
+        if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
+        return mFixedHeightMinor;
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/DecorContentParent.java b/v7/appcompat/src/android/support/v7/internal/widget/DecorContentParent.java
index e218efd..e4400ac 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/DecorContentParent.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/DecorContentParent.java
@@ -19,10 +19,10 @@
 
 import android.graphics.drawable.Drawable;
 import android.os.Parcelable;
-import android.support.v7.internal.app.WindowCallback;
 import android.support.v7.internal.view.menu.MenuPresenter;
 import android.util.SparseArray;
 import android.view.Menu;
+import android.view.Window;
 
 /**
  * Implemented by the top-level decor layout for a window. DecorContentParent offers
@@ -31,7 +31,7 @@
  * @hide
  */
 public interface DecorContentParent {
-    void setWindowCallback(WindowCallback cb);
+    void setWindowCallback(Window.Callback cb);
     void setWindowTitle(CharSequence title);
     CharSequence getTitle();
     void initFeature(int windowFeature);
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/DecorToolbar.java b/v7/appcompat/src/android/support/v7/internal/widget/DecorToolbar.java
index dfeb34c..c601e64 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/DecorToolbar.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/DecorToolbar.java
@@ -20,13 +20,13 @@
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.os.Parcelable;
-import android.support.v7.internal.app.WindowCallback;
 import android.support.v7.internal.view.menu.MenuBuilder;
 import android.support.v7.internal.view.menu.MenuPresenter;
 import android.util.SparseArray;
 import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.Window;
 import android.widget.SpinnerAdapter;
 
 /**
@@ -42,7 +42,7 @@
     boolean isSplit();
     boolean hasExpandedActionView();
     void collapseActionView();
-    void setWindowCallback(WindowCallback cb);
+    void setWindowCallback(Window.Callback cb);
     void setWindowTitle(CharSequence title);
     CharSequence getTitle();
     void setTitle(CharSequence title);
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/DialogTitle.java b/v7/appcompat/src/android/support/v7/internal/widget/DialogTitle.java
new file mode 100644
index 0000000..446d70d
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/internal/widget/DialogTitle.java
@@ -0,0 +1,77 @@
+/* 
+ * Copyright (C) 2015 Google Inc.
+ *
+ * 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.v7.internal.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.v7.appcompat.R;
+import android.text.Layout;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+/**
+ * Used by dialogs to change the font size and number of lines to try to fit
+ * the text to the available space.
+ *
+ * @hide
+ */
+public class DialogTitle extends TextView {
+
+    public DialogTitle(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public DialogTitle(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public DialogTitle(Context context) {
+        super(context);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        final Layout layout = getLayout();
+        if (layout != null) {
+            final int lineCount = layout.getLineCount();
+            if (lineCount > 0) {
+                final int ellipsisCount = layout.getEllipsisCount(lineCount - 1);
+                if (ellipsisCount > 0) {
+                    setSingleLine(false);
+                    setMaxLines(2);
+
+                    final TypedArray a = getContext().obtainStyledAttributes(null,
+                            R.styleable.TextAppearance,
+                            android.R.attr.textAppearanceMedium,
+                            android.R.style.TextAppearance_Medium);
+                    final int textSize = a.getDimensionPixelSize(
+                            R.styleable.TextAppearance_android_textSize, 0);
+                    if (textSize != 0) {
+                        // textSize is already expressed in pixels
+                        setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+                    }
+                    a.recycle();
+
+                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/DrawableUtils.java b/v7/appcompat/src/android/support/v7/internal/widget/DrawableUtils.java
new file mode 100644
index 0000000..1ff5e4d
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/internal/widget/DrawableUtils.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 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.v7.internal.widget;
+
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * @hide
+ */
+public class DrawableUtils {
+
+    private static final String TAG = "DrawableUtils";
+
+    public static final Rect INSETS_NONE = new Rect();
+
+    private static Class<?> sInsetsClazz;
+
+    static {
+        if (Build.VERSION.SDK_INT >= 18) {
+            try {
+                sInsetsClazz = Class.forName("android.graphics.Insets");
+            } catch (ClassNotFoundException e) {
+                // Oh well...
+            }
+        }
+    }
+
+    private DrawableUtils() {}
+
+    /**
+     * Allows us to get the optical insets for a {@link Drawable}. Since this is hidden we need to
+     * use reflection. Since the {@code Insets} class is hidden also, we return a Rect instead.
+     */
+    public static Rect getOpticalBounds(Drawable drawable) {
+        if (sInsetsClazz != null) {
+            try {
+                // If the Drawable is wrapped, we need to manually unwrap it and process
+                // the wrapped drawable.
+                drawable = DrawableCompat.unwrap(drawable);
+
+                final Method getOpticalInsetsMethod = drawable.getClass()
+                        .getMethod("getOpticalInsets");
+                final Object insets = getOpticalInsetsMethod.invoke(drawable);
+
+                if (insets != null) {
+                    // If the drawable has some optical insets, let's copy them into a Rect
+                    final Rect result = new Rect();
+
+                    for (Field field : sInsetsClazz.getFields()) {
+                        switch (field.getName()) {
+                            case "left":
+                               result.left = field.getInt(insets);
+                                break;
+                            case "top":
+                                result.top = field.getInt(insets);
+                                break;
+                            case "right":
+                                result.right = field.getInt(insets);
+                                break;
+                            case "bottom":
+                                result.bottom = field.getInt(insets);
+                                break;
+                        }
+                    }
+                    return result;
+                }
+            } catch (Exception e) {
+                // Eugh, we hit some kind of reflection issue...
+                Log.e(TAG, "Couldn't obtain the optical insets. Ignoring.");
+            }
+        }
+
+        // If we reach here, either we're running on a device pre-v18, the Drawable didn't have
+        // any optical insets, or a reflection issue, so we'll just return an empty rect
+        return INSETS_NONE;
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/ListViewCompat.java b/v7/appcompat/src/android/support/v7/internal/widget/ListViewCompat.java
index c75d2ce..e2e6c4c 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/ListViewCompat.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ListViewCompat.java
@@ -21,6 +21,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v7.graphics.drawable.DrawableWrapper;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -74,11 +75,14 @@
 
     @Override
     public void setSelector(Drawable sel) {
-        mSelector = new GateKeeperDrawable(sel);
+        mSelector = sel != null ? new GateKeeperDrawable(sel) : null;
         super.setSelector(mSelector);
 
-        Rect padding = new Rect();
-        sel.getPadding(padding);
+        final Rect padding = new Rect();
+        if (sel != null) {
+            sel.getPadding(padding);
+        }
+
         mSelectionLeftPadding = padding.left;
         mSelectionTopPadding = padding.top;
         mSelectionRightPadding = padding.right;
@@ -88,7 +92,8 @@
     @Override
     protected void drawableStateChanged() {
         super.drawableStateChanged();
-        mSelector.setEnabled(true);
+
+        setSelectorEnabled(true);
         updateSelectorStateCompat();
     }
 
@@ -120,8 +125,10 @@
     protected void drawSelectorCompat(Canvas canvas) {
         if (!mSelectorRect.isEmpty()) {
             final Drawable selector = getSelector();
-            selector.setBounds(mSelectorRect);
-            selector.draw(canvas);
+            if (selector != null) {
+                selector.setBounds(mSelectorRect);
+                selector.draw(canvas);
+            }
         }
     }
 
@@ -322,7 +329,9 @@
     }
 
     protected void setSelectorEnabled(boolean enabled) {
-        mSelector.setEnabled(enabled);
+        if (mSelector != null) {
+            mSelector.setEnabled(enabled);
+        }
     }
 
     private static class GateKeeperDrawable extends DrawableWrapper {
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/ScrollingTabContainerView.java b/v7/appcompat/src/android/support/v7/internal/widget/ScrollingTabContainerView.java
index 1426208..7c5c2cb 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/ScrollingTabContainerView.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ScrollingTabContainerView.java
@@ -23,10 +23,10 @@
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewPropertyAnimatorCompat;
 import android.support.v4.view.ViewPropertyAnimatorListener;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v7.app.ActionBar;
 import android.support.v7.appcompat.R;
 import android.support.v7.internal.view.ActionBarPolicy;
+import android.support.v7.widget.AppCompatTextView;
 import android.support.v7.widget.LinearLayoutCompat;
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
@@ -41,7 +41,6 @@
 import android.widget.BaseAdapter;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -482,7 +481,7 @@
                 final boolean hasText = !TextUtils.isEmpty(text);
                 if (hasText) {
                     if (mTextView == null) {
-                        TextView textView = new CompatTextView(getContext(), null,
+                        TextView textView = new AppCompatTextView(getContext(), null,
                                 R.attr.actionBarTabTextStyle);
                         textView.setEllipsize(TruncateAt.END);
                         LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/SpinnerCompat.java b/v7/appcompat/src/android/support/v7/internal/widget/SpinnerCompat.java
index fb6ce4c..b2b8af2 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/SpinnerCompat.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/SpinnerCompat.java
@@ -164,7 +164,9 @@
                 R.styleable.Spinner, defStyle, 0);
 
         // Need to reset this for tinting purposes
-        setBackgroundDrawable(a.getDrawable(R.styleable.Spinner_android_background));
+        if (a.hasValue(R.styleable.Spinner_android_background)) {
+            setBackgroundDrawable(a.getDrawable(R.styleable.Spinner_android_background));
+        }
 
         if (mode == MODE_THEME) {
             mode = a.getInt(R.styleable.Spinner_spinnerMode, MODE_DIALOG);
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/ThemeUtils.java b/v7/appcompat/src/android/support/v7/internal/widget/ThemeUtils.java
new file mode 100644
index 0000000..ed5b093
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ThemeUtils.java
@@ -0,0 +1,117 @@
+/*
+ * 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.v7.internal.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.support.v4.graphics.ColorUtils;
+import android.util.TypedValue;
+
+/**
+ * @hide
+ */
+public class ThemeUtils {
+
+    private static final ThreadLocal<TypedValue> TL_TYPED_VALUE = new ThreadLocal<>();
+
+    static final int[] DISABLED_STATE_SET = new int[]{-android.R.attr.state_enabled};
+    static final int[] FOCUSED_STATE_SET = new int[]{android.R.attr.state_focused};
+    static final int[] ACTIVATED_STATE_SET = new int[]{android.R.attr.state_activated};
+    static final int[] PRESSED_STATE_SET = new int[]{android.R.attr.state_pressed};
+    static final int[] CHECKED_STATE_SET = new int[]{android.R.attr.state_checked};
+    static final int[] SELECTED_STATE_SET = new int[]{android.R.attr.state_selected};
+    static final int[] NOT_PRESSED_OR_FOCUSED_STATE_SET = new int[]{
+            -android.R.attr.state_pressed, -android.R.attr.state_focused};
+    static final int[] EMPTY_STATE_SET = new int[0];
+
+    private static final int[] TEMP_ARRAY = new int[1];
+
+    public static ColorStateList createDisabledStateList(int textColor, int disabledTextColor) {
+        // Now create a new ColorStateList with the default color, and the new disabled
+        // color
+        final int[][] states = new int[2][];
+        final int[] colors = new int[2];
+        int i = 0;
+
+        // Disabled state
+        states[i] = DISABLED_STATE_SET;
+        colors[i] = disabledTextColor;
+        i++;
+
+        // Default state
+        states[i] = EMPTY_STATE_SET;
+        colors[i] = textColor;
+        i++;
+
+        return new ColorStateList(states, colors);
+    }
+
+    public static int getThemeAttrColor(Context context, int attr) {
+        TEMP_ARRAY[0] = attr;
+        TypedArray a = context.obtainStyledAttributes(null, TEMP_ARRAY);
+        try {
+            return a.getColor(0, 0);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    public static ColorStateList getThemeAttrColorStateList(Context context, int attr) {
+        TEMP_ARRAY[0] = attr;
+        TypedArray a = context.obtainStyledAttributes(null, TEMP_ARRAY);
+        try {
+            return a.getColorStateList(0);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    public static int getDisabledThemeAttrColor(Context context, int attr) {
+        final ColorStateList csl = getThemeAttrColorStateList(context, attr);
+        if (csl != null && csl.isStateful()) {
+            // If the CSL is stateful, we'll assume it has a disabled state and use it
+            return csl.getColorForState(DISABLED_STATE_SET, csl.getDefaultColor());
+        } else {
+            // Else, we'll generate the color using disabledAlpha from the theme
+
+            final TypedValue tv = getTypedValue();
+            // Now retrieve the disabledAlpha value from the theme
+            context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true);
+            final float disabledAlpha = tv.getFloat();
+
+            return getThemeAttrColor(context, attr, disabledAlpha);
+        }
+    }
+
+    private static TypedValue getTypedValue() {
+        TypedValue typedValue = TL_TYPED_VALUE.get();
+        if (typedValue == null) {
+            typedValue = new TypedValue();
+            TL_TYPED_VALUE.set(typedValue);
+        }
+        return typedValue;
+    }
+
+    static int getThemeAttrColor(Context context, int attr, float alpha) {
+        final int color = getThemeAttrColor(context, attr);
+        final int originalAlpha = Color.alpha(color);
+        return ColorUtils.setAlphaComponent(color, Math.round(originalAlpha * alpha));
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintAutoCompleteTextView.java b/v7/appcompat/src/android/support/v7/internal/widget/TintAutoCompleteTextView.java
deleted file mode 100644
index 92c1b40..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintAutoCompleteTextView.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.widget;
-
-import android.content.Context;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.widget.AutoCompleteTextView;
-import android.widget.EditText;
-
-/**
- * An tint aware {@link android.widget.AutoCompleteTextView}.
- *
- * @hide
- */
-public class TintAutoCompleteTextView extends AutoCompleteTextView {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.background,
-            android.R.attr.popupBackground
-    };
-
-    private final TintManager mTintManager;
-
-    public TintAutoCompleteTextView(Context context) {
-        this(context, null);
-    }
-
-    public TintAutoCompleteTextView(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.autoCompleteTextViewStyle);
-    }
-
-    public TintAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
-                defStyleAttr, 0);
-        setBackgroundDrawable(a.getDrawable(0));
-        if (a.hasValue(1)) {
-            setDropDownBackgroundDrawable(a.getDrawable(1));
-        }
-        a.recycle();
-
-        mTintManager = a.getTintManager();
-    }
-
-    @Override
-    public void setDropDownBackgroundResource(int id) {
-        setDropDownBackgroundDrawable(mTintManager.getDrawable(id));
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintButton.java b/v7/appcompat/src/android/support/v7/internal/widget/TintButton.java
deleted file mode 100644
index fc822cd..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintButton.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.Button;
-
-/**
- * An tint aware {@link android.widget.Button}
- *
- * @hide
- */
-public class TintButton extends Button {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.background,
-            android.R.attr.textAppearance
-    };
-
-    private final TintManager mTintManager;
-
-    public TintButton(Context context) {
-        this(context, null);
-    }
-
-    public TintButton(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.buttonStyle);
-    }
-
-    public TintButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
-                defStyleAttr, 0);
-        if (a.hasValue(0)) {
-            setBackgroundDrawable(a.getDrawable(0));
-        }
-
-        // Keep the TintManager in case we need it later
-        mTintManager = a.getTintManager();
-    }
-
-    @Override
-    public void setBackgroundResource(int resid) {
-        setBackgroundDrawable(mTintManager.getDrawable(resid));
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintCheckBox.java b/v7/appcompat/src/android/support/v7/internal/widget/TintCheckBox.java
deleted file mode 100644
index 37b0409..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintCheckBox.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.CheckBox;
-
-/**
- * An tint aware {@link android.widget.CheckBox}.
- *
- * @hide
- */
-public class TintCheckBox extends CheckBox {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.button
-    };
-
-    private final TintManager mTintManager;
-
-    public TintCheckBox(Context context) {
-        this(context, null);
-    }
-
-    public TintCheckBox(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.checkboxStyle);
-    }
-
-    public TintCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
-                defStyleAttr, 0);
-        setButtonDrawable(a.getDrawable(0));
-        a.recycle();
-
-        mTintManager = a.getTintManager();
-    }
-
-    @Override
-    public void setButtonDrawable(int resid) {
-        setButtonDrawable(mTintManager.getDrawable(resid));
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintCheckedTextView.java b/v7/appcompat/src/android/support/v7/internal/widget/TintCheckedTextView.java
deleted file mode 100644
index 57aa126..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintCheckedTextView.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.CheckedTextView;
-import android.widget.RadioButton;
-
-/**
- * An tint aware {@link android.widget.CheckedTextView}.
- *
- * @hide
- */
-public class TintCheckedTextView extends CheckedTextView {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.checkMark
-    };
-
-    private final TintManager mTintManager;
-
-    public TintCheckedTextView(Context context) {
-        this(context, null);
-    }
-
-    public TintCheckedTextView(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.checkedTextViewStyle);
-    }
-
-    public TintCheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
-                defStyleAttr, 0);
-        setCheckMarkDrawable(a.getDrawable(0));
-        a.recycle();
-
-        mTintManager = a.getTintManager();
-    }
-
-    @Override
-    public void setCheckMarkDrawable(int resid) {
-        setCheckMarkDrawable(mTintManager.getDrawable(resid));
-    }
-
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintContextWrapper.java b/v7/appcompat/src/android/support/v7/internal/widget/TintContextWrapper.java
new file mode 100644
index 0000000..3fdd971
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/internal/widget/TintContextWrapper.java
@@ -0,0 +1,79 @@
+/*
+ * 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.v7.internal.widget;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+
+/**
+ * A {@link android.content.ContextWrapper} which returns a tint-aware
+ * {@link android.content.res.Resources} instance from {@link #getResources()}.
+ *
+ * @hide
+ */
+public class TintContextWrapper extends ContextWrapper {
+
+    public static Context wrap(Context context) {
+        if (!(context instanceof TintContextWrapper)) {
+            context = new TintContextWrapper(context);
+        }
+        return context;
+    }
+
+    private Resources mResources;
+
+    private TintContextWrapper(Context base) {
+        super(base);
+    }
+
+    @Override
+    public Resources getResources() {
+        if (mResources == null) {
+            mResources = new TintResources(super.getResources(), TintManager.get(this));
+        }
+        return mResources;
+    }
+
+    /**
+     * This class allows us to intercept calls so that we can tint resources (if applicable).
+     */
+    static class TintResources extends ResourcesWrapper {
+
+        private final TintManager mTintManager;
+
+        public TintResources(Resources resources, TintManager tintManager) {
+            super(resources);
+            mTintManager = tintManager;
+        }
+
+        /**
+         * We intercept this call so that we tint the result (if applicable). This is needed for
+         * things like {@link android.graphics.drawable.DrawableContainer}s which can retrieve
+         * their children via this method.
+         */
+        @Override
+        public Drawable getDrawable(int id) throws NotFoundException {
+            Drawable d = super.getDrawable(id);
+            if (d != null) {
+                mTintManager.tintDrawableUsingColorFilter(id, d);
+            }
+            return d;
+        }
+    }
+}
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintDrawableWrapper.java b/v7/appcompat/src/android/support/v7/internal/widget/TintDrawableWrapper.java
deleted file mode 100644
index 41a4c42..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintDrawableWrapper.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.Drawable;
-
-/**
- * A {@link DrawableWrapper} which updates it's color filter using a {@link ColorStateList}.
- */
-class TintDrawableWrapper extends DrawableWrapper {
-
-    private final ColorStateList mTintStateList;
-    private final PorterDuff.Mode mTintMode;
-
-    private int mCurrentColor;
-
-    public TintDrawableWrapper(Drawable drawable, ColorStateList tintStateList) {
-        this(drawable, tintStateList, TintManager.DEFAULT_MODE);
-    }
-
-    public TintDrawableWrapper(Drawable drawable, ColorStateList tintStateList,
-            PorterDuff.Mode tintMode) {
-        super(drawable);
-        mTintStateList = tintStateList;
-        mTintMode = tintMode;
-    }
-
-    @Override
-    public boolean isStateful() {
-        return (mTintStateList != null && mTintStateList.isStateful()) || super.isStateful();
-    }
-
-    @Override
-    public boolean setState(int[] stateSet) {
-        boolean handled = super.setState(stateSet);
-        handled = updateTint(stateSet) || handled;
-        return handled;
-    }
-
-    private boolean updateTint(int[] state) {
-        if (mTintStateList != null) {
-            final int color = mTintStateList.getColorForState(state, mCurrentColor);
-            if (color != mCurrentColor) {
-                if (color != Color.TRANSPARENT) {
-                    setColorFilter(color, mTintMode);
-                } else {
-                    clearColorFilter();
-                }
-                mCurrentColor = color;
-                return true;
-            }
-        }
-        return false;
-    }
-
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintEditText.java b/v7/appcompat/src/android/support/v7/internal/widget/TintEditText.java
deleted file mode 100644
index bb53da3..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintEditText.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.widget;
-
-import android.content.Context;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.widget.EditText;
-
-/**
- * An tint aware {@link android.widget.EditText}.
- *
- * @hide
- */
-public class TintEditText extends EditText {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.background
-    };
-
-    public TintEditText(Context context) {
-        this(context, null);
-    }
-
-    public TintEditText(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.editTextStyle);
-    }
-
-    public TintEditText(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
-                defStyleAttr, 0);
-        setBackgroundDrawable(a.getDrawable(0));
-        a.recycle();
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintImageButton.java b/v7/appcompat/src/android/support/v7/internal/widget/TintImageButton.java
new file mode 100644
index 0000000..d4adc21
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/internal/widget/TintImageButton.java
@@ -0,0 +1,65 @@
+/*
+ * 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.v7.internal.widget;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.ImageButton;
+
+/**
+ * An tint aware {@link android.widget.ImageButton}
+ *
+ * @hide
+ */
+public class TintImageButton extends TintImageView {
+
+    public TintImageButton(Context context) {
+        this(context, null);
+    }
+
+    public TintImageButton(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TintImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        setFocusable(true);
+    }
+
+    @Override
+    protected boolean onSetAlpha(int alpha) {
+        return false;
+    }
+
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setClassName(ImageButton.class.getName());
+    }
+
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(ImageButton.class.getName());
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintImageView.java b/v7/appcompat/src/android/support/v7/internal/widget/TintImageView.java
index 0d9df4c..617c7b8 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintImageView.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/TintImageView.java
@@ -17,7 +17,13 @@
 package android.support.v7.internal.widget;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.support.annotation.DrawableRes;
+import android.support.annotation.Nullable;
+import android.support.v4.graphics.drawable.DrawableCompat;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 
@@ -35,6 +41,11 @@
 
     private final TintManager mTintManager;
 
+    private ColorStateList mDrawableTintList = null;
+    private PorterDuff.Mode mDrawableTintMode = null;
+    private boolean mHasDrawableTint = false;
+    private boolean mHasDrawableTintMode = false;
+
     public TintImageView(Context context) {
         this(context, null);
     }
@@ -46,7 +57,7 @@
     public TintImageView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
+        TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs, TINT_ATTRS,
                 defStyleAttr, 0);
         if (a.length() > 0) {
             if (a.hasValue(0)) {
@@ -67,4 +78,46 @@
         // Intercept this call and instead retrieve the Drawable via the tint manager
         setImageDrawable(mTintManager.getDrawable(resId));
     }
+
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+        super.setImageDrawable(drawable);
+        applyImageTint();
+    }
+
+    public void setImageTintList(@Nullable ColorStateList tint) {
+        if (Build.VERSION.SDK_INT >= 21) {
+            super.setImageTintList(tint);
+        } else {
+            mDrawableTintList = tint;
+            mHasDrawableTint = true;
+            applyImageTint();
+        }
+    }
+
+    public void setImageTintMode(@Nullable PorterDuff.Mode tintMode) {
+        if (Build.VERSION.SDK_INT >= 21) {
+            super.setImageTintMode(tintMode);
+        } else {
+            mDrawableTintMode = tintMode;
+            mHasDrawableTintMode = true;
+            applyImageTint();
+        }
+    }
+
+    private void applyImageTint() {
+        Drawable drawable = getDrawable();
+        if (drawable != null && (mHasDrawableTint || mHasDrawableTintMode)) {
+            drawable = DrawableCompat.wrap(drawable.mutate());
+            if (mHasDrawableTint) {
+                DrawableCompat.setTintList(drawable, mDrawableTintList);
+            }
+            if (mHasDrawableTintMode) {
+                DrawableCompat.setTintMode(drawable, mDrawableTintMode);
+            }
+            // Drawable may have changed, make sure we re-set
+            super.setImageDrawable(drawable);
+        }
+    }
+
 }
diff --git a/v4/api21/android/support/v4/view/ViewGroupCompatApi21.java b/v7/appcompat/src/android/support/v7/internal/widget/TintInfo.java
similarity index 63%
copy from v4/api21/android/support/v4/view/ViewGroupCompatApi21.java
copy to v7/appcompat/src/android/support/v7/internal/widget/TintInfo.java
index 5ebd187..8eea38d 100644
--- a/v4/api21/android/support/v4/view/ViewGroupCompatApi21.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/TintInfo.java
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package android.support.v4.view;
+package android.support.v7.internal.widget;
 
-import android.view.ViewGroup;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
 
-class ViewGroupCompatApi21 {
-
-    public static void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-        group.setTransitionGroup(isTransitionGroup);
-    }
-
-    public static boolean isTransitionGroup(ViewGroup group) {
-        return group.isTransitionGroup();
-    }
+/**
+ * @hide
+ */
+public class TintInfo {
+    public ColorStateList mTintList;
+    public PorterDuff.Mode mTintMode;
+    public boolean mHasTintMode;
+    public boolean mHasTintList;
 }
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java b/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
index 52e3206..777f5a0 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
@@ -18,32 +18,54 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Build;
 import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.util.LruCache;
 import android.support.v7.appcompat.R;
 import android.util.Log;
-import android.util.TypedValue;
+import android.util.SparseArray;
+import android.view.View;
+
+import java.lang.ref.WeakReference;
+import java.util.WeakHashMap;
+
+import static android.support.v7.internal.widget.ThemeUtils.getDisabledThemeAttrColor;
+import static android.support.v7.internal.widget.ThemeUtils.getThemeAttrColor;
+import static android.support.v7.internal.widget.ThemeUtils.getThemeAttrColorStateList;
 
 /**
  * @hide
  */
-public class TintManager {
+public final class TintManager {
 
-    private static final String TAG = TintManager.class.getSimpleName();
+    public static final boolean SHOULD_BE_USED = Build.VERSION.SDK_INT < 21;
+
+    private static final String TAG = "TintManager";
     private static final boolean DEBUG = false;
+    private static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
 
-    static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
-
+    private static final WeakHashMap<Context, TintManager> INSTANCE_CACHE = new WeakHashMap<>();
     private static final ColorFilterLruCache COLOR_FILTER_CACHE = new ColorFilterLruCache(6);
 
     /**
      * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal},
-     * using the default mode.
+     * using the default mode using a raw color filter.
+     */
+    private static final int[] COLORFILTER_TINT_COLOR_CONTROL_NORMAL = {
+            R.drawable.abc_textfield_search_default_mtrl_alpha,
+            R.drawable.abc_textfield_default_mtrl_alpha,
+            R.drawable.abc_ab_share_pack_mtrl_alpha
+    };
+
+    /**
+     * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal}, using
+     * {@link DrawableCompat}'s tinting functionality.
      */
     private static final int[] TINT_COLOR_CONTROL_NORMAL = {
             R.drawable.abc_ic_ab_back_mtrl_am_alpha,
@@ -57,27 +79,25 @@
             R.drawable.abc_ic_menu_selectall_mtrl_alpha,
             R.drawable.abc_ic_menu_paste_mtrl_am_alpha,
             R.drawable.abc_ic_menu_moreoverflow_mtrl_alpha,
-            R.drawable.abc_ic_voice_search_api_mtrl_alpha,
-            R.drawable.abc_textfield_search_default_mtrl_alpha,
-            R.drawable.abc_textfield_default_mtrl_alpha,
-            R.drawable.abc_ab_share_pack_mtrl_alpha
+            R.drawable.abc_ic_voice_search_api_mtrl_alpha
     };
 
     /**
      * Drawables which should be tinted with the value of {@code R.attr.colorControlActivated},
-     * using the default mode.
+     * using a color filter.
      */
-    private static final int[] TINT_COLOR_CONTROL_ACTIVATED = {
+    private static final int[] COLORFILTER_COLOR_CONTROL_ACTIVATED = {
             R.drawable.abc_textfield_activated_mtrl_alpha,
             R.drawable.abc_textfield_search_activated_mtrl_alpha,
-            R.drawable.abc_cab_background_top_mtrl_alpha
+            R.drawable.abc_cab_background_top_mtrl_alpha,
+            R.drawable.abc_text_cursor_mtrl_alpha
     };
 
     /**
      * Drawables which should be tinted with the value of {@code android.R.attr.colorBackground},
-     * using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode.
+     * using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode and a color filter.
      */
-    private static final int[] TINT_COLOR_BACKGROUND_MULTIPLY = {
+    private static final int[] COLORFILTER_COLOR_BACKGROUND_MULTIPLY = {
             R.drawable.abc_popup_background_mtrl_mult,
             R.drawable.abc_cab_background_internal_bg,
             R.drawable.abc_menu_hardkey_panel_mtrl_mult
@@ -95,81 +115,96 @@
             R.drawable.abc_btn_check_material,
             R.drawable.abc_btn_radio_material,
             R.drawable.abc_spinner_textfield_background_material,
-            R.drawable.abc_ratingbar_full_material
+            R.drawable.abc_ratingbar_full_material,
+            R.drawable.abc_switch_track_mtrl_alpha,
+            R.drawable.abc_switch_thumb_material,
+            R.drawable.abc_btn_default_mtrl_shape,
+            R.drawable.abc_btn_borderless_material
     };
 
-    /**
-     * Drawables which contain other drawables which should be tinted. The child drawable IDs
-     * should be defined in one of the arrays above.
-     */
-    private static final int[] CONTAINERS_WITH_TINT_CHILDREN = {
-            R.drawable.abc_cab_background_top_material
-    };
-
-    private final Context mContext;
-    private final Resources mResources;
-    private final TypedValue mTypedValue;
-
+    private final WeakReference<Context> mContextRef;
+    private SparseArray<ColorStateList> mTintLists;
     private ColorStateList mDefaultColorStateList;
-    private ColorStateList mSwitchThumbStateList;
-    private ColorStateList mSwitchTrackStateList;
-    private ColorStateList mButtonStateList;
 
     /**
-     * A helper method to instantiate a {@link TintManager} and then call {@link #getDrawable(int)}.
+     * A helper method to get a {@link TintManager} and then call {@link #getDrawable(int)}.
      * This method should not be used routinely.
      */
     public static Drawable getDrawable(Context context, int resId) {
         if (isInTintList(resId)) {
-            return new TintManager(context).getDrawable(resId);
+            return TintManager.get(context).getDrawable(resId);
         } else {
             return ContextCompat.getDrawable(context, resId);
         }
     }
 
-    public TintManager(Context context) {
-        mContext = context;
-        mResources = new TintResources(context.getResources(), this);
-        mTypedValue = new TypedValue();
+    /**
+     * Get a {@link android.support.v7.internal.widget.TintManager} instance.
+     */
+    public static TintManager get(Context context) {
+        TintManager tm = INSTANCE_CACHE.get(context);
+        if (tm == null) {
+            tm = new TintManager(context);
+            INSTANCE_CACHE.put(context, tm);
+        }
+        return tm;
+    }
+
+    private TintManager(Context context) {
+        mContextRef = new WeakReference<>(context);
     }
 
     public Drawable getDrawable(int resId) {
-        Drawable drawable = ContextCompat.getDrawable(mContext, resId);
+        final Context context = mContextRef.get();
+        if (context == null) return null;
+
+        Drawable drawable = ContextCompat.getDrawable(context, resId);
 
         if (drawable != null) {
-            drawable = drawable.mutate();
+            if (Build.VERSION.SDK_INT >= 8) {
+                // Mutate can cause NPEs on 2.1
+                drawable = drawable.mutate();
+            }
 
-            if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
-                drawable = new TintDrawableWrapper(drawable, getDefaultColorStateList());
-            } else if (resId == R.drawable.abc_switch_track_mtrl_alpha) {
-                drawable = new TintDrawableWrapper(drawable, getSwitchTrackColorStateList());
-            } else if (resId == R.drawable.abc_switch_thumb_material) {
-                drawable = new TintDrawableWrapper(drawable, getSwitchThumbColorStateList(),
-                        PorterDuff.Mode.MULTIPLY);
-            } else if (resId == R.drawable.abc_btn_default_mtrl_shape) {
-                drawable = new TintDrawableWrapper(drawable, getButtonColorStateList());
-            } else if (arrayContains(CONTAINERS_WITH_TINT_CHILDREN, resId)) {
-                drawable = mResources.getDrawable(resId);
+            final ColorStateList tintList = getTintList(resId);
+            if (tintList != null) {
+                // First wrap the Drawable and set the tint list
+                drawable = DrawableCompat.wrap(drawable);
+                DrawableCompat.setTintList(drawable, tintList);
+
+                // If there is a blending mode specified for the drawable, use it
+                final PorterDuff.Mode tintMode = getTintMode(resId);
+                if (tintMode != null) {
+                    DrawableCompat.setTintMode(drawable, tintMode);
+                }
+            } else if (resId == R.drawable.abc_cab_background_top_material) {
+                return new LayerDrawable(new Drawable[] {
+                        getDrawable(R.drawable.abc_cab_background_internal_bg),
+                        getDrawable(R.drawable.abc_cab_background_top_mtrl_alpha)
+                });
             } else {
-                tintDrawable(resId, drawable);
+                tintDrawableUsingColorFilter(resId, drawable);
             }
         }
         return drawable;
     }
 
-    void tintDrawable(final int resId, final Drawable drawable) {
+    public final void tintDrawableUsingColorFilter(final int resId, Drawable drawable) {
+        final Context context = mContextRef.get();
+        if (context == null) return;
+
         PorterDuff.Mode tintMode = null;
         boolean colorAttrSet = false;
         int colorAttr = 0;
         int alpha = -1;
 
-        if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) {
+        if (arrayContains(COLORFILTER_TINT_COLOR_CONTROL_NORMAL, resId)) {
             colorAttr = R.attr.colorControlNormal;
             colorAttrSet = true;
-        } else if (arrayContains(TINT_COLOR_CONTROL_ACTIVATED, resId)) {
+        } else if (arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, resId)) {
             colorAttr = R.attr.colorControlActivated;
             colorAttrSet = true;
-        } else if (arrayContains(TINT_COLOR_BACKGROUND_MULTIPLY, resId)) {
+        } else if (arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, resId)) {
             colorAttr = android.R.attr.colorBackground;
             colorAttrSet = true;
             tintMode = PorterDuff.Mode.MULTIPLY;
@@ -180,29 +215,15 @@
         }
 
         if (colorAttrSet) {
-            if (tintMode == null) {
-                tintMode = DEFAULT_MODE;
-            }
-            final int color = getThemeAttrColor(colorAttr);
-
-            // First, lets see if the cache already contains the color filter
-            PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, tintMode);
-
-            if (filter == null) {
-                // Cache miss, so create a color filter and add it to the cache
-                filter = new PorterDuffColorFilter(color, tintMode);
-                COLOR_FILTER_CACHE.put(color, tintMode, filter);
-            }
-
-            // Finally set the color filter
-            drawable.setColorFilter(filter);
+            final int color = getThemeAttrColor(context, colorAttr);
+            setPorterDuffColorFilter(drawable, color, tintMode);
 
             if (alpha != -1) {
                 drawable.setAlpha(alpha);
             }
 
             if (DEBUG) {
-                Log.d(TAG, "Tinted Drawable ID: " + mResources.getResourceName(resId) +
+                Log.d(TAG, "Tinted Drawable: " + context.getResources().getResourceName(resId) +
                         " with color: #" + Integer.toHexString(color));
             }
         }
@@ -218,54 +239,105 @@
     }
 
     private static boolean isInTintList(int drawableId) {
-        return arrayContains(TINT_COLOR_BACKGROUND_MULTIPLY, drawableId) ||
-                arrayContains(TINT_COLOR_CONTROL_NORMAL, drawableId) ||
-                arrayContains(TINT_COLOR_CONTROL_ACTIVATED, drawableId) ||
+        return arrayContains(TINT_COLOR_CONTROL_NORMAL, drawableId) ||
+                arrayContains(COLORFILTER_TINT_COLOR_CONTROL_NORMAL, drawableId) ||
+                arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, drawableId) ||
                 arrayContains(TINT_COLOR_CONTROL_STATE_LIST, drawableId) ||
-                arrayContains(CONTAINERS_WITH_TINT_CHILDREN, drawableId);
+                arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, drawableId) ||
+                drawableId == R.drawable.abc_cab_background_top_material;
     }
 
-    private ColorStateList getDefaultColorStateList() {
+    final PorterDuff.Mode getTintMode(final int resId) {
+        PorterDuff.Mode mode = null;
+
+        if (resId == R.drawable.abc_switch_thumb_material) {
+            mode = PorterDuff.Mode.MULTIPLY;
+        }
+
+        return mode;
+    }
+
+    public final ColorStateList getTintList(int resId) {
+        final Context context = mContextRef.get();
+        if (context == null) return null;
+
+        // Try the cache first (if it exists)
+        ColorStateList tint = mTintLists != null ? mTintLists.get(resId) : null;
+
+        if (tint == null) {
+            // ...if the cache did not contain a color state list, try and create one
+            if (resId == R.drawable.abc_edit_text_material) {
+                tint = createEditTextColorStateList(context);
+            } else if (resId == R.drawable.abc_switch_track_mtrl_alpha) {
+                tint = createSwitchTrackColorStateList(context);
+            } else if (resId == R.drawable.abc_switch_thumb_material) {
+                tint = createSwitchThumbColorStateList(context);
+            } else if (resId == R.drawable.abc_btn_default_mtrl_shape
+                    || resId == R.drawable.abc_btn_borderless_material) {
+                tint = createButtonColorStateList(context);
+            } else if (resId == R.drawable.abc_spinner_mtrl_am_alpha
+                    || resId == R.drawable.abc_spinner_textfield_background_material) {
+                tint = createSpinnerColorStateList(context);
+            } else if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) {
+                tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal);
+            } else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
+                tint = getDefaultColorStateList(context);
+            }
+
+            if (tint != null) {
+                if (mTintLists == null) {
+                    // If our tint list cache hasn't been set up yet, create it
+                    mTintLists = new SparseArray<>();
+                }
+                // Add any newly created ColorStateList to the cache
+                mTintLists.append(resId, tint);
+            }
+        }
+        return tint;
+    }
+
+    private ColorStateList getDefaultColorStateList(Context context) {
         if (mDefaultColorStateList == null) {
             /**
              * Generate the default color state list which uses the colorControl attributes.
              * Order is important here. The default enabled state needs to go at the bottom.
              */
 
-            final int colorControlNormal = getThemeAttrColor(R.attr.colorControlNormal);
-            final int colorControlActivated = getThemeAttrColor(R.attr.colorControlActivated);
+            final int colorControlNormal = getThemeAttrColor(context, R.attr.colorControlNormal);
+            final int colorControlActivated = getThemeAttrColor(context,
+                    R.attr.colorControlActivated);
 
             final int[][] states = new int[7][];
             final int[] colors = new int[7];
             int i = 0;
 
             // Disabled state
-            states[i] = new int[] { -android.R.attr.state_enabled };
-            colors[i] = getDisabledThemeAttrColor(R.attr.colorControlNormal);
+            states[i] = ThemeUtils.DISABLED_STATE_SET;
+            colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
             i++;
 
-            states[i] = new int[] { android.R.attr.state_focused };
+            states[i] = ThemeUtils.FOCUSED_STATE_SET;
             colors[i] = colorControlActivated;
             i++;
 
-            states[i] = new int[] { android.R.attr.state_activated };
+            states[i] = ThemeUtils.ACTIVATED_STATE_SET;
             colors[i] = colorControlActivated;
             i++;
 
-            states[i] = new int[] { android.R.attr.state_pressed };
+            states[i] = ThemeUtils.PRESSED_STATE_SET;
             colors[i] = colorControlActivated;
             i++;
 
-            states[i] = new int[] { android.R.attr.state_checked };
+            states[i] = ThemeUtils.CHECKED_STATE_SET;
             colors[i] = colorControlActivated;
             i++;
 
-            states[i] = new int[] { android.R.attr.state_selected };
+            states[i] = ThemeUtils.SELECTED_STATE_SET;
             colors[i] = colorControlActivated;
             i++;
 
             // Default enabled state
-            states[i] = new int[0];
+            states[i] = ThemeUtils.EMPTY_STATE_SET;
             colors[i] = colorControlNormal;
             i++;
 
@@ -274,111 +346,144 @@
         return mDefaultColorStateList;
     }
 
-    private ColorStateList getSwitchTrackColorStateList() {
-        if (mSwitchTrackStateList == null) {
-            final int[][] states = new int[3][];
-            final int[] colors = new int[3];
-            int i = 0;
+    private ColorStateList createSwitchTrackColorStateList(Context context) {
+        final int[][] states = new int[3][];
+        final int[] colors = new int[3];
+        int i = 0;
+
+        // Disabled state
+        states[i] = ThemeUtils.DISABLED_STATE_SET;
+        colors[i] = getThemeAttrColor(context, android.R.attr.colorForeground, 0.1f);
+        i++;
+
+        states[i] = ThemeUtils.CHECKED_STATE_SET;
+        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated, 0.3f);
+        i++;
+
+        // Default enabled state
+        states[i] = ThemeUtils.EMPTY_STATE_SET;
+        colors[i] = getThemeAttrColor(context, android.R.attr.colorForeground, 0.3f);
+        i++;
+
+        return new ColorStateList(states, colors);
+    }
+
+    private ColorStateList createSwitchThumbColorStateList(Context context) {
+        final int[][] states = new int[3][];
+        final int[] colors = new int[3];
+        int i = 0;
+
+        final ColorStateList thumbColor = getThemeAttrColorStateList(context,
+                R.attr.colorSwitchThumbNormal);
+
+        if (thumbColor != null && thumbColor.isStateful()) {
+            // If colorSwitchThumbNormal is a valid ColorStateList, extract the default and
+            // disabled colors from it
 
             // Disabled state
-            states[i] = new int[] { -android.R.attr.state_enabled };
-            colors[i] = getThemeAttrColor(android.R.attr.colorForeground, 0.1f);
+            states[i] = ThemeUtils.DISABLED_STATE_SET;
+            colors[i] = thumbColor.getColorForState(states[i], 0);
             i++;
 
-            states[i] = new int[] { android.R.attr.state_checked };
-            colors[i] = getThemeAttrColor(R.attr.colorControlActivated, 0.3f);
+            states[i] = ThemeUtils.CHECKED_STATE_SET;
+            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
             i++;
 
             // Default enabled state
-            states[i] = new int[0];
-            colors[i] = getThemeAttrColor(android.R.attr.colorForeground, 0.3f);
+            states[i] = ThemeUtils.EMPTY_STATE_SET;
+            colors[i] = thumbColor.getDefaultColor();
             i++;
-
-            mSwitchTrackStateList = new ColorStateList(states, colors);
-        }
-        return mSwitchTrackStateList;
-    }
-
-    private ColorStateList getSwitchThumbColorStateList() {
-        if (mSwitchThumbStateList == null) {
-            final int[][] states = new int[3][];
-            final int[] colors = new int[3];
-            int i = 0;
+        } else {
+            // Else we'll use an approximation using the default disabled alpha
 
             // Disabled state
-            states[i] = new int[] { -android.R.attr.state_enabled };
-            colors[i] = getDisabledThemeAttrColor(R.attr.colorSwitchThumbNormal);
+            states[i] = ThemeUtils.DISABLED_STATE_SET;
+            colors[i] = getDisabledThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
             i++;
 
-            states[i] = new int[] { android.R.attr.state_checked };
-            colors[i] = getThemeAttrColor(R.attr.colorControlActivated);
+            states[i] = ThemeUtils.CHECKED_STATE_SET;
+            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
             i++;
 
             // Default enabled state
-            states[i] = new int[0];
-            colors[i] = getThemeAttrColor(R.attr.colorSwitchThumbNormal);
+            states[i] = ThemeUtils.EMPTY_STATE_SET;
+            colors[i] = getThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
             i++;
-
-            mSwitchThumbStateList = new ColorStateList(states, colors);
         }
-        return mSwitchThumbStateList;
+
+        return new ColorStateList(states, colors);
     }
 
-    private ColorStateList getButtonColorStateList() {
-        if (mButtonStateList == null) {
-            final int[][] states = new int[4][];
-            final int[] colors = new int[4];
-            int i = 0;
+    private ColorStateList createEditTextColorStateList(Context context) {
+        final int[][] states = new int[3][];
+        final int[] colors = new int[3];
+        int i = 0;
 
-            // Disabled state
-            states[i] = new int[] { -android.R.attr.state_enabled };
-            colors[i] = getDisabledThemeAttrColor(R.attr.colorButtonNormal);
-            i++;
+        // Disabled state
+        states[i] = ThemeUtils.DISABLED_STATE_SET;
+        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
+        i++;
 
-            states[i] = new int[] { android.R.attr.state_pressed };
-            colors[i] = getThemeAttrColor(R.attr.colorControlHighlight);
-            i++;
+        states[i] = ThemeUtils.NOT_PRESSED_OR_FOCUSED_STATE_SET;
+        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
+        i++;
 
-            states[i] = new int[] { android.R.attr.state_focused };
-            colors[i] = getThemeAttrColor(R.attr.colorControlHighlight);
-            i++;
+        // Default enabled state
+        states[i] = ThemeUtils.EMPTY_STATE_SET;
+        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
+        i++;
 
-            // Default enabled state
-            states[i] = new int[0];
-            colors[i] = getThemeAttrColor(R.attr.colorButtonNormal);
-            i++;
-
-            mButtonStateList = new ColorStateList(states, colors);
-        }
-        return mButtonStateList;
+        return new ColorStateList(states, colors);
     }
 
-    int getThemeAttrColor(int attr) {
-        if (mContext.getTheme().resolveAttribute(attr, mTypedValue, true)) {
-            if (mTypedValue.type >= TypedValue.TYPE_FIRST_INT
-                    && mTypedValue.type <= TypedValue.TYPE_LAST_INT) {
-                return mTypedValue.data;
-            } else if (mTypedValue.type == TypedValue.TYPE_STRING) {
-                return mResources.getColor(mTypedValue.resourceId);
-            }
-        }
-        return 0;
+    private ColorStateList createButtonColorStateList(Context context) {
+        final int[][] states = new int[4][];
+        final int[] colors = new int[4];
+        int i = 0;
+
+        final int colorButtonNormal = getThemeAttrColor(context, R.attr.colorButtonNormal);
+        final int colorControlHighlight = getThemeAttrColor(context, R.attr.colorControlHighlight);
+
+        // Disabled state
+        states[i] = ThemeUtils.DISABLED_STATE_SET;
+        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorButtonNormal);
+        i++;
+
+        states[i] = ThemeUtils.PRESSED_STATE_SET;
+        colors[i] = ColorUtils.compositeColors(colorControlHighlight, colorButtonNormal);
+        i++;
+
+        states[i] = ThemeUtils.FOCUSED_STATE_SET;
+        colors[i] = ColorUtils.compositeColors(colorControlHighlight, colorButtonNormal);
+        i++;
+
+        // Default enabled state
+        states[i] = ThemeUtils.EMPTY_STATE_SET;
+        colors[i] = colorButtonNormal;
+        i++;
+
+        return new ColorStateList(states, colors);
     }
 
-    int getThemeAttrColor(int attr, float alpha) {
-        final int color = getThemeAttrColor(attr);
-        final int originalAlpha = Color.alpha(color);
+    private ColorStateList createSpinnerColorStateList(Context context) {
+        final int[][] states = new int[3][];
+        final int[] colors = new int[3];
+        int i = 0;
 
-        // Return the color, multiplying the original alpha by the disabled value
-        return (color & 0x00ffffff) | (Math.round(originalAlpha * alpha) << 24);
-    }
+        // Disabled state
+        states[i] = ThemeUtils.DISABLED_STATE_SET;
+        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
+        i++;
 
-    int getDisabledThemeAttrColor(int attr) {
-        // Now retrieve the disabledAlpha value from the theme
-        mContext.getTheme().resolveAttribute(android.R.attr.disabledAlpha, mTypedValue, true);
-        final float disabledAlpha = mTypedValue.getFloat();
+        states[i] = ThemeUtils.NOT_PRESSED_OR_FOCUSED_STATE_SET;
+        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
+        i++;
 
-        return getThemeAttrColor(attr, disabledAlpha);
+        states[i] = ThemeUtils.EMPTY_STATE_SET;
+        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
+        i++;
+
+        return new ColorStateList(states, colors);
     }
 
     private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> {
@@ -402,4 +507,41 @@
             return hashCode;
         }
     }
+
+    public static void tintViewBackground(View view, TintInfo tint) {
+        final Drawable background = view.getBackground();
+        if (tint.mHasTintList) {
+            setPorterDuffColorFilter(
+                    background,
+                    tint.mTintList.getColorForState(view.getDrawableState(),
+                            tint.mTintList.getDefaultColor()),
+                    tint.mHasTintMode ? tint.mTintMode : null);
+        } else {
+            background.clearColorFilter();
+        }
+
+        if (Build.VERSION.SDK_INT <= 10) {
+            // On Gingerbread, GradientDrawable does not invalidate itself when it's ColorFilter
+            // has changed, so we need to force an invalidation
+            view.invalidate();
+        }
+    }
+
+    private static void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) {
+        if (mode == null) {
+            // If we don't have a blending mode specified, use our default
+            mode = DEFAULT_MODE;
+        }
+
+        // First, lets see if the cache already contains the color filter
+        PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode);
+
+        if (filter == null) {
+            // Cache miss, so create a color filter and add it to the cache
+            filter = new PorterDuffColorFilter(color, mode);
+            COLOR_FILTER_CACHE.put(color, mode, filter);
+        }
+
+        d.setColorFilter(filter);
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintMultiAutoCompleteTextView.java b/v7/appcompat/src/android/support/v7/internal/widget/TintMultiAutoCompleteTextView.java
deleted file mode 100644
index 9746335..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintMultiAutoCompleteTextView.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.AutoCompleteTextView;
-import android.widget.MultiAutoCompleteTextView;
-
-/**
- * An tint aware {@link android.widget.MultiAutoCompleteTextView}.
- *
- * @hide
- */
-public class TintMultiAutoCompleteTextView extends MultiAutoCompleteTextView {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.background,
-            android.R.attr.popupBackground
-    };
-
-    private final TintManager mTintManager;
-
-    public TintMultiAutoCompleteTextView(Context context) {
-        this(context, null);
-    }
-
-    public TintMultiAutoCompleteTextView(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.autoCompleteTextViewStyle);
-    }
-
-    public TintMultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
-                defStyleAttr, 0);
-        setBackgroundDrawable(a.getDrawable(0));
-        if (a.hasValue(1)) {
-            setDropDownBackgroundDrawable(a.getDrawable(1));
-        }
-        a.recycle();
-
-        mTintManager = a.getTintManager();
-    }
-
-    @Override
-    public void setDropDownBackgroundResource(int id) {
-        setDropDownBackgroundDrawable(mTintManager.getDrawable(id));
-    }
-
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintRadioButton.java b/v7/appcompat/src/android/support/v7/internal/widget/TintRadioButton.java
deleted file mode 100644
index 6216063..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintRadioButton.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.CheckBox;
-import android.widget.RadioButton;
-
-/**
- * An tint aware {@link android.widget.RadioButton}.
- *
- * @hide
- */
-public class TintRadioButton extends RadioButton {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.button
-    };
-
-    private final TintManager mTintManager;
-
-    public TintRadioButton(Context context) {
-        this(context, null);
-    }
-
-    public TintRadioButton(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.radioButtonStyle);
-    }
-
-    public TintRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
-                defStyleAttr, 0);
-        setButtonDrawable(a.getDrawable(0));
-        a.recycle();
-
-        mTintManager = a.getTintManager();
-    }
-
-    @Override
-    public void setButtonDrawable(int resid) {
-        setButtonDrawable(mTintManager.getDrawable(resid));
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintResources.java b/v7/appcompat/src/android/support/v7/internal/widget/TintResources.java
deleted file mode 100644
index 3dfbbc1..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintResources.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.widget;
-
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.support.v7.appcompat.R;
-
-/**
- * This class allows us to intercept calls so that we can tint resources (if applicable).
- *
- * @hide
- */
-class TintResources extends ResourcesWrapper {
-
-    private final TintManager mTintManager;
-
-    public TintResources(Resources resources, TintManager tintManager) {
-        super(resources);
-        mTintManager = tintManager;
-    }
-
-    /**
-     * We intercept this call so that we tint the result (if applicable). This is needed for things
-     * like {@link DrawableContainer}s which retrieve their children via this method.
-     */
-    @Override
-    public Drawable getDrawable(int id) throws NotFoundException {
-        Drawable d = super.getDrawable(id);
-        if (d != null) {
-            mTintManager.tintDrawable(id, d);
-        }
-        return d;
-    }
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintSpinner.java b/v7/appcompat/src/android/support/v7/internal/widget/TintSpinner.java
deleted file mode 100644
index 3f81d35..0000000
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintSpinner.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.internal.widget;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.widget.ListPopupWindow;
-import android.widget.Spinner;
-
-import java.lang.reflect.Field;
-
-/**
- * An tint aware {@link android.widget.Spinner}.
- *
- * @hide
- */
-public class TintSpinner extends Spinner {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.background,
-            android.R.attr.popupBackground
-    };
-
-    public TintSpinner(Context context) {
-        this(context, null);
-    }
-
-    public TintSpinner(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.spinnerStyle);
-    }
-
-    public TintSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
-                defStyleAttr, 0);
-        setBackgroundDrawable(a.getDrawable(0));
-
-        if (a.hasValue(1)) {
-            final Drawable background = a.getDrawable(1);
-            if (Build.VERSION.SDK_INT >= 16) {
-                setPopupBackgroundDrawable(background);
-            } else if (Build.VERSION.SDK_INT >= 11) {
-                setPopupBackgroundDrawableV11(this, background);
-            }
-        }
-
-        a.recycle();
-    }
-
-    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
-    private static void setPopupBackgroundDrawableV11(Spinner view, Drawable background) {
-        try {
-            Field popupField = Spinner.class.getDeclaredField("mPopup");
-            popupField.setAccessible(true);
-
-            Object popup = popupField.get(view);
-
-            if (popup instanceof ListPopupWindow) {
-                ((ListPopupWindow) popup).setBackgroundDrawable(background);
-            }
-        } catch (NoSuchFieldException e) {
-            e.printStackTrace();
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        }
-    }
-
-}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintTypedArray.java b/v7/appcompat/src/android/support/v7/internal/widget/TintTypedArray.java
index aff509c..c19692d 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintTypedArray.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/TintTypedArray.java
@@ -179,7 +179,7 @@
 
     public TintManager getTintManager() {
         if (mTintManager == null) {
-            mTintManager = new TintManager(mContext);
+            mTintManager = TintManager.get(mContext);
         }
         return mTintManager;
     }
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/ToolbarWidgetWrapper.java b/v7/appcompat/src/android/support/v7/internal/widget/ToolbarWidgetWrapper.java
index ff22b16..225f8e7 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/ToolbarWidgetWrapper.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ToolbarWidgetWrapper.java
@@ -24,7 +24,6 @@
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
 import android.support.v7.appcompat.R;
-import android.support.v7.internal.app.WindowCallback;
 import android.support.v7.internal.view.menu.ActionMenuItem;
 import android.support.v7.internal.view.menu.MenuBuilder;
 import android.support.v7.internal.view.menu.MenuPresenter;
@@ -74,7 +73,7 @@
     private CharSequence mSubtitle;
     private CharSequence mHomeDescription;
 
-    private WindowCallback mWindowCallback;
+    private Window.Callback mWindowCallback;
     private boolean mMenuPrepared;
     private ActionMenuPresenter mActionMenuPresenter;
 
@@ -95,7 +94,7 @@
         mTitle = toolbar.getTitle();
         mSubtitle = toolbar.getSubtitle();
         mTitleSet = mTitle != null;
-        mNavIcon = mToolbar.getNavigationIcon();
+        mNavIcon = toolbar.getNavigationIcon();
 
         if (style) {
             final TintTypedArray a = TintTypedArray.obtainStyledAttributes(toolbar.getContext(),
@@ -117,7 +116,7 @@
             }
 
             final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon);
-            if (icon != null) {
+            if (mNavIcon == null && icon != null) {
                 setIcon(icon);
             }
 
@@ -174,7 +173,7 @@
         } else {
             mDisplayOpts = detectDisplayOptions();
             // Create a TintManager in case we need it later
-            mTintManager = new TintManager(toolbar.getContext());
+            mTintManager = TintManager.get(toolbar.getContext());
         }
 
         setDefaultNavigationContentDescription(defaultNavigationContentDescription);
@@ -257,7 +256,7 @@
     }
 
     @Override
-    public void setWindowCallback(WindowCallback cb) {
+    public void setWindowCallback(Window.Callback cb) {
         mWindowCallback = cb;
     }
 
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/ViewUtils.java b/v7/appcompat/src/android/support/v7/internal/widget/ViewUtils.java
index a56cf3f..98e55d5 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/ViewUtils.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ViewUtils.java
@@ -16,9 +16,14 @@
 
 package android.support.v7.internal.widget;
 
+import android.content.Context;
+import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.os.Build;
 import android.support.v4.view.ViewCompat;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.view.ContextThemeWrapper;
+import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
 
@@ -102,4 +107,34 @@
             }
         }
     }
+
+    /**
+     * Allows us to emulate the {@code android:theme} attribute for devices before L.
+     */
+    public static Context themifyContext(Context context, AttributeSet attrs,
+            boolean useAndroidTheme, boolean useAppTheme) {
+        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.View, 0, 0);
+        int themeId = 0;
+        if (useAndroidTheme) {
+            // First try reading android:theme if enabled
+            themeId = a.getResourceId(R.styleable.View_android_theme, 0);
+        }
+        if (useAppTheme && themeId == 0) {
+            // ...if that didn't work, try reading app:theme (for legacy reasons) if enabled
+            themeId = a.getResourceId(R.styleable.View_theme, 0);
+
+            if (themeId != 0) {
+                Log.i(TAG, "app:theme is now deprecated. Please move to using android:theme instead.");
+            }
+        }
+        a.recycle();
+
+        if (themeId != 0 && (!(context instanceof ContextThemeWrapper)
+                || ((ContextThemeWrapper) context).getThemeResId() != themeId)) {
+            // If the context isn't a ContextThemeWrapperCompat, or it is but does not have
+            // the same theme as we need, wrap it in a new wrapper
+            context = new ContextThemeWrapper(context, themeId);
+        }
+        return context;
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/view/ActionMode.java b/v7/appcompat/src/android/support/v7/view/ActionMode.java
index 579305f..ff9cd49 100644
--- a/v7/appcompat/src/android/support/v7/view/ActionMode.java
+++ b/v7/appcompat/src/android/support/v7/view/ActionMode.java
@@ -222,7 +222,7 @@
 
     /**
      * Callback interface for action modes. Supplied to
-     * {@link android.support.v7.app.ActionBarActivity#startSupportActionMode(Callback)} (Callback)},
+     * {@link android.support.v7.app.AppCompatDelegate#startSupportActionMode(Callback)} (Callback)},
      * a Callback configures and handles events raised by a user's interaction with an action mode.
      *
      * <p>An action mode's lifecycle is as follows:
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java b/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
index 801be5b..6956311 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
@@ -17,8 +17,10 @@
 package android.support.v7.widget;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -35,7 +37,8 @@
 import android.support.v7.internal.view.menu.MenuPopupHelper;
 import android.support.v7.internal.view.menu.MenuView;
 import android.support.v7.internal.view.menu.SubMenuBuilder;
-import android.support.v7.internal.widget.TintImageView;
+import android.support.v7.internal.widget.TintImageButton;
+import android.support.v7.internal.widget.TintInfo;
 import android.util.SparseBooleanArray;
 import android.view.MenuItem;
 import android.view.SoundEffectConstants;
@@ -55,7 +58,7 @@
 
     private static final String TAG = "ActionMenuPresenter";
 
-    private View mOverflowButton;
+    private OverflowMenuButton mOverflowButton;
     private boolean mReserveOverflow;
     private boolean mReserveOverflowSet;
     private int mWidthLimit;
@@ -79,6 +82,8 @@
     private OpenOverflowRunnable mPostedOpenRunnable;
     private ActionMenuPopupCallback mPopupCallback;
 
+    private TintInfo mOverflowTintInfo;
+
     final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
     int mOpenSubMenuId;
 
@@ -112,6 +117,7 @@
                 mOverflowButton = new OverflowMenuButton(mSystemContext);
                 final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
                 mOverflowButton.measure(spec, spec);
+                applyOverflowTint();
             }
             width -= mOverflowButton.getMeasuredWidth();
         } else {
@@ -235,6 +241,7 @@
         if (hasOverflow) {
             if (mOverflowButton == null) {
                 mOverflowButton = new OverflowMenuButton(mSystemContext);
+                applyOverflowTint();
             }
             ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
             if (parent != mMenuView) {
@@ -549,6 +556,40 @@
         menuView.initialize(mMenu);
     }
 
+    public void setOverflowTintList(ColorStateList tint) {
+        if (mOverflowTintInfo == null) {
+            mOverflowTintInfo = new TintInfo();
+        }
+        mOverflowTintInfo.mTintList = tint;
+        mOverflowTintInfo.mHasTintList = true;
+
+        applyOverflowTint();
+    }
+
+    public void setOverflowTintMode(PorterDuff.Mode tintMode) {
+        if (mOverflowTintInfo == null) {
+            mOverflowTintInfo = new TintInfo();
+        }
+        mOverflowTintInfo.mTintMode = tintMode;
+        mOverflowTintInfo.mHasTintMode = true;
+
+        applyOverflowTint();
+    }
+
+    private void applyOverflowTint() {
+        final TintInfo tintInfo = mOverflowTintInfo;
+        if (tintInfo != null && (tintInfo.mHasTintList || tintInfo.mHasTintMode)) {
+            if (mOverflowButton != null) {
+                if (tintInfo.mHasTintList) {
+                    mOverflowButton.setImageTintList(tintInfo.mTintList);
+                }
+                if (tintInfo.mHasTintMode) {
+                    mOverflowButton.setImageTintMode(tintInfo.mTintMode);
+                }
+            }
+        }
+    }
+
     private static class SavedState implements Parcelable {
         public int openSubMenuId;
 
@@ -581,7 +622,8 @@
         };
     }
 
-    private class OverflowMenuButton extends TintImageView implements ActionMenuView.ActionMenuChildView {
+    private class OverflowMenuButton extends TintImageButton
+            implements ActionMenuView.ActionMenuChildView {
         private final float[] mTempPts = new float[2];
 
         public OverflowMenuButton(Context context) {
@@ -652,11 +694,15 @@
             final Drawable d = getDrawable();
             final Drawable bg = getBackground();
             if (d != null && bg != null) {
-                final float[] pts = mTempPts;
-                pts[0] = d.getBounds().centerX();
-                getImageMatrix().mapPoints(pts);
-                final int offset =  (int) pts[0] - getWidth() / 2;
-                DrawableCompat.setHotspotBounds(bg, offset, 0, getWidth() + offset, getHeight());
+                final int width = getWidth();
+                final int height = getHeight();
+                final int halfEdge = Math.max(width, height) / 2;
+                final int offsetX = getPaddingLeft() - getPaddingRight();
+                final int offsetY = getPaddingTop() - getPaddingBottom();
+                final int centerX = (width + offsetX) / 2;
+                final int centerY = (height + offsetY) / 2;
+                DrawableCompat.setHotspotBounds(bg, centerX - halfEdge, centerY - halfEdge,
+                        centerX + halfEdge, centerY + halfEdge);
             }
 
             return changed;
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionMenuView.java b/v7/appcompat/src/android/support/v7/widget/ActionMenuView.java
index 563961a..eb9b9f2 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionMenuView.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionMenuView.java
@@ -16,13 +16,16 @@
 package android.support.v7.widget;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
+import android.graphics.PorterDuff;
 import android.os.Build;
 import android.support.v7.internal.view.menu.ActionMenuItemView;
 import android.support.v7.internal.view.menu.MenuBuilder;
 import android.support.v7.internal.view.menu.MenuItemImpl;
 import android.support.v7.internal.view.menu.MenuPresenter;
 import android.support.v7.internal.view.menu.MenuView;
+import android.support.v7.internal.widget.TintInfo;
 import android.support.v7.internal.widget.ViewUtils;
 import android.util.AttributeSet;
 import android.view.ContextThemeWrapper;
@@ -126,11 +129,13 @@
             super.onConfigurationChanged(newConfig);
         }
 
-        mPresenter.updateMenuView(false);
+        if (mPresenter != null) {
+            mPresenter.updateMenuView(false);
 
-        if (mPresenter != null && mPresenter.isOverflowMenuShowing()) {
-            mPresenter.hideOverflowMenu();
-            mPresenter.showOverflowMenu();
+            if (mPresenter.isOverflowMenuShowing()) {
+                mPresenter.hideOverflowMenu();
+                mPresenter.showOverflowMenu();
+            }
         }
     }
 
@@ -552,6 +557,31 @@
         mReserveOverflow = reserveOverflow;
     }
 
+    /**
+     * Applies a tint to the overflow drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     */
+    public void setOverflowTintList(ColorStateList tint) {
+        if (mPresenter != null) {
+            mPresenter.setOverflowTintList(tint);
+        }
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by {@link
+     * #setOverflowTintList(ColorStateList)} to the overflow drawable.
+     * The default mode is {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be {@code null} to clear tint
+     */
+    public void setOverflowTintMode(PorterDuff.Mode tintMode) {
+        if (mPresenter != null) {
+            mPresenter.setOverflowTintMode(tintMode);
+        }
+    }
+
     @Override
     protected LayoutParams generateDefaultLayoutParams() {
         LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatAutoCompleteTextView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatAutoCompleteTextView.java
new file mode 100644
index 0000000..915410b
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatAutoCompleteTextView.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 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.v7.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.support.annotation.Nullable;
+import android.support.v4.view.TintableBackgroundView;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.widget.TintContextWrapper;
+import android.support.v7.internal.widget.TintInfo;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.util.AttributeSet;
+import android.widget.AutoCompleteTextView;
+
+/**
+ * A tint aware {@link android.widget.AutoCompleteTextView}.
+ * <p>
+ * This will automatically be used when you use {@link AutoCompleteTextView} in your layouts. You
+ * should only need to manually use this class writing custom views.
+ */
+public class AppCompatAutoCompleteTextView extends AutoCompleteTextView implements
+        TintableBackgroundView {
+
+    private static final int[] TINT_ATTRS = {
+            android.R.attr.background,
+            android.R.attr.popupBackground
+    };
+
+    private TintManager mTintManager;
+    private TintInfo mBackgroundTint;
+
+    public AppCompatAutoCompleteTextView(Context context) {
+        this(context, null);
+    }
+
+    public AppCompatAutoCompleteTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.autoCompleteTextViewStyle);
+    }
+
+    public AppCompatAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
+
+        if (TintManager.SHOULD_BE_USED) {
+            TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
+                    TINT_ATTRS, defStyleAttr, 0);
+            mTintManager = a.getTintManager();
+
+            if (a.hasValue(0)) {
+                ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1));
+                if (tint != null) {
+                    setSupportBackgroundTintList(tint);
+                }
+            }
+            if (a.hasValue(1)) {
+                setDropDownBackgroundDrawable(a.getDrawable(1));
+            }
+            a.recycle();
+        }
+    }
+
+    @Override
+    public void setDropDownBackgroundResource(int id) {
+        setDropDownBackgroundDrawable(mTintManager.getDrawable(id));
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
+     * android.content.res.ColorStateList)}
+     *
+     * @hide
+     */
+    @Override
+    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintList = tint;
+        mBackgroundTint.mHasTintList = true;
+
+        applySupportBackgroundTint();
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
+     *
+     * @hide
+     */
+    @Override
+    @Nullable
+    public ColorStateList getSupportBackgroundTintList() {
+        return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode)}
+     *
+     * @hide
+     */
+    @Override
+    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintMode = tintMode;
+        mBackgroundTint.mHasTintMode = true;
+
+        applySupportBackgroundTint();
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
+     *
+     * @hide
+     */
+    @Override
+    @Nullable
+    public PorterDuff.Mode getSupportBackgroundTintMode() {
+        return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        applySupportBackgroundTint();
+    }
+
+    private void applySupportBackgroundTint() {
+        if (getBackground() != null && mBackgroundTint != null) {
+            TintManager.tintViewBackground(this, mBackgroundTint);
+        }
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
new file mode 100644
index 0000000..389aef5
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2014 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.v7.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.support.v4.view.TintableBackgroundView;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.text.AllCapsTransformationMethod;
+import android.support.v7.internal.widget.ThemeUtils;
+import android.support.v7.internal.widget.TintInfo;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
+
+/**
+ * A tint aware {@link android.widget.Button}.
+ * <p>
+ * This will automatically be used when you use {@link android.widget.Button} in your layouts. You
+ * should only need to manually use this class when writing custom views.
+ */
+public class AppCompatButton extends Button implements TintableBackgroundView {
+
+    private static final int[] TINT_ATTRS = {
+            android.R.attr.background
+    };
+
+    private TintInfo mBackgroundTint;
+
+    public AppCompatButton(Context context) {
+        this(context, null);
+    }
+
+    public AppCompatButton(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.buttonStyle);
+    }
+
+    public AppCompatButton(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        if (TintManager.SHOULD_BE_USED) {
+            TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
+                    TINT_ATTRS, defStyleAttr, 0);
+            if (a.hasValue(0)) {
+                ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1));
+                if (tint != null) {
+                    setSupportBackgroundTintList(tint);
+                }
+            }
+            a.recycle();
+        }
+
+        // First read the TextAppearance style id
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppCompatTextView,
+                defStyleAttr, 0);
+        final int ap = a.getResourceId(R.styleable.AppCompatTextView_android_textAppearance, -1);
+        a.recycle();
+
+        // Now check TextAppearance's textAllCaps value
+        if (ap != -1) {
+            TypedArray appearance = context.obtainStyledAttributes(ap, R.styleable.TextAppearance);
+            if (appearance.hasValue(R.styleable.TextAppearance_textAllCaps)) {
+                setAllCaps(appearance.getBoolean(R.styleable.TextAppearance_textAllCaps, false));
+            }
+            appearance.recycle();
+        }
+
+        // Now read the style's value
+        a = context.obtainStyledAttributes(attrs, R.styleable.AppCompatTextView, defStyleAttr, 0);
+        if (a.hasValue(R.styleable.AppCompatTextView_textAllCaps)) {
+            setAllCaps(a.getBoolean(R.styleable.AppCompatTextView_textAllCaps, false));
+        }
+        a.recycle();
+
+        final ColorStateList textColors = getTextColors();
+        if (textColors != null && !textColors.isStateful()) {
+            // If we have a ColorStateList which isn't stateful, create one which includes
+            // a disabled state
+
+            final int disabledTextColor;
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+                // Pre-Lollipop, we will use textColorSecondary with android:disabledAlpha
+                // applied
+                disabledTextColor = ThemeUtils.getDisabledThemeAttrColor(context,
+                        android.R.attr.textColorSecondary);
+            } else {
+                // With certain styles on Lollipop, there is a StateListAnimator which sets
+                // an alpha on the whole view, so we don't need to apply disabledAlpha to
+                // textColorSecondary
+                disabledTextColor = ThemeUtils.getThemeAttrColor(context,
+                        android.R.attr.textColorSecondary);
+            }
+
+            setTextColor(ThemeUtils.createDisabledStateList(
+                    textColors.getDefaultColor(), disabledTextColor));
+        }
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
+     * android.content.res.ColorStateList)}
+     *
+     * @hide
+     */
+    @Override
+    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintList = tint;
+        mBackgroundTint.mHasTintList = true;
+
+        applySupportBackgroundTint();
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
+     *
+     * @hide
+     */
+    @Override
+    @Nullable
+    public ColorStateList getSupportBackgroundTintList() {
+        return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode)}
+     *
+     * @hide
+     */
+    @Override
+    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintMode = tintMode;
+        mBackgroundTint.mHasTintMode = true;
+
+        applySupportBackgroundTint();
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
+     *
+     * @hide
+     */
+    @Override
+    @Nullable
+    public PorterDuff.Mode getSupportBackgroundTintMode() {
+        return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        applySupportBackgroundTint();
+    }
+
+    private void applySupportBackgroundTint() {
+        if (getBackground() != null && mBackgroundTint != null) {
+            TintManager.tintViewBackground(this, mBackgroundTint);
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setClassName(Button.class.getName());
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(Button.class.getName());
+    }
+
+    public void setAllCaps(boolean allCaps) {
+        setTransformationMethod(allCaps ? new AllCapsTransformationMethod(getContext()) : null);
+    }
+
+    @Override
+    public void setTextAppearance(Context context, int resId) {
+        super.setTextAppearance(context, resId);
+
+        TypedArray appearance = context.obtainStyledAttributes(resId, R.styleable.TextAppearance);
+        if (appearance.hasValue(R.styleable.TextAppearance_textAllCaps)) {
+            setAllCaps(appearance.getBoolean(R.styleable.TextAppearance_textAllCaps, false));
+        }
+        appearance.recycle();
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java
new file mode 100644
index 0000000..6cbcdd1
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 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.v7.widget;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.DrawableRes;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.util.AttributeSet;
+import android.widget.CheckBox;
+
+/**
+ * A tint aware {@link android.widget.CheckBox}.
+ * <p>
+ * This will automatically be used when you use {@link android.widget.CheckBox} in your layouts.
+ * You should only need to manually use this class when writing custom views.
+ */
+public class AppCompatCheckBox extends CheckBox {
+
+    private static final int[] TINT_ATTRS = {
+            android.R.attr.button
+    };
+
+    private TintManager mTintManager;
+    private Drawable mButtonDrawable;
+
+    public AppCompatCheckBox(Context context) {
+        this(context, null);
+    }
+
+    public AppCompatCheckBox(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.checkboxStyle);
+    }
+
+    public AppCompatCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        if (TintManager.SHOULD_BE_USED) {
+            TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
+                    TINT_ATTRS, defStyleAttr, 0);
+            setButtonDrawable(a.getDrawable(0));
+            a.recycle();
+
+            mTintManager = a.getTintManager();
+        }
+    }
+
+    @Override
+    public void setButtonDrawable(Drawable buttonDrawable) {
+        super.setButtonDrawable(buttonDrawable);
+        mButtonDrawable = buttonDrawable;
+    }
+
+    @Override
+    public void setButtonDrawable(@DrawableRes int resId) {
+        if (mTintManager != null) {
+            setButtonDrawable(mTintManager.getDrawable(resId));
+        } else {
+            super.setButtonDrawable(resId);
+        }
+    }
+
+    @Override
+    public int getCompoundPaddingLeft() {
+        int padding = super.getCompoundPaddingLeft();
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            // Before JB-MR1 the button drawable wasn't taken into account for padding. We'll
+            // workaround that here
+            if (mButtonDrawable != null) {
+                padding += mButtonDrawable.getIntrinsicWidth();
+            }
+        }
+        return padding;
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java
new file mode 100644
index 0000000..04836c6
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 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.v7.widget;
+
+import android.content.Context;
+import android.support.annotation.DrawableRes;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.util.AttributeSet;
+import android.widget.CheckedTextView;
+
+/**
+ * A tint aware {@link android.widget.CheckedTextView}.
+ * <p>
+ * This will automatically be used when you use {@link android.widget.CheckedTextView} in your
+ * layouts. You should only need to manually use this class when writing custom views.
+ */
+public class AppCompatCheckedTextView extends CheckedTextView {
+
+    private static final int[] TINT_ATTRS = {
+            android.R.attr.checkMark
+    };
+
+    private TintManager mTintManager;
+
+    public AppCompatCheckedTextView(Context context) {
+        this(context, null);
+    }
+
+    public AppCompatCheckedTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, android.R.attr.checkedTextViewStyle);
+    }
+
+    public AppCompatCheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        if (TintManager.SHOULD_BE_USED) {
+            TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
+                    TINT_ATTRS, defStyleAttr, 0);
+            setCheckMarkDrawable(a.getDrawable(0));
+            a.recycle();
+
+            mTintManager = a.getTintManager();
+        }
+    }
+
+    @Override
+    public void setCheckMarkDrawable(@DrawableRes int resId) {
+        if (mTintManager != null) {
+            setCheckMarkDrawable(mTintManager.getDrawable(resId));
+        } else {
+            super.setCheckMarkDrawable(resId);
+        }
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatEditText.java b/v7/appcompat/src/android/support/v7/widget/AppCompatEditText.java
new file mode 100644
index 0000000..8d3e8d1
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatEditText.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 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.v7.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.support.annotation.Nullable;
+import android.support.v4.view.TintableBackgroundView;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.widget.TintContextWrapper;
+import android.support.v7.internal.widget.TintInfo;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.util.AttributeSet;
+import android.widget.EditText;
+
+/**
+ * A tint aware {@link android.widget.EditText}.
+ * <p>
+ * This will automatically be used when you use {@link android.widget.EditText} in your
+ * layouts. You should only need to manually use this class when writing custom views.
+ */
+public class AppCompatEditText extends EditText implements TintableBackgroundView {
+
+    private static final int[] TINT_ATTRS = {
+            android.R.attr.background
+    };
+
+    private TintInfo mBackgroundTint;
+
+    public AppCompatEditText(Context context) {
+        this(context, null);
+    }
+
+    public AppCompatEditText(Context context, AttributeSet attrs) {
+        this(TintContextWrapper.wrap(context), attrs, R.attr.editTextStyle);
+    }
+
+    public AppCompatEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        if (TintManager.SHOULD_BE_USED) {
+            TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
+                    TINT_ATTRS, defStyleAttr, 0);
+            if (a.hasValue(0)) {
+                ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1));
+                if (tint != null) {
+                    setSupportBackgroundTintList(tint);
+                }
+            }
+            a.recycle();
+        }
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
+     * android.content.res.ColorStateList)}
+     *
+     * @hide
+     */
+    @Override
+    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintList = tint;
+        mBackgroundTint.mHasTintList = true;
+
+        applySupportBackgroundTint();
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
+     *
+     * @hide
+     */
+    @Override
+    @Nullable
+    public ColorStateList getSupportBackgroundTintList() {
+        return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode)}
+     *
+     * @hide
+     */
+    @Override
+    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintMode = tintMode;
+        mBackgroundTint.mHasTintMode = true;
+
+        applySupportBackgroundTint();
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
+     *
+     * @hide
+     */
+    @Override
+    @Nullable
+    public PorterDuff.Mode getSupportBackgroundTintMode() {
+        return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        applySupportBackgroundTint();
+    }
+
+    private void applySupportBackgroundTint() {
+        if (getBackground() != null && mBackgroundTint != null) {
+            TintManager.tintViewBackground(this, mBackgroundTint);
+        }
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatMultiAutoCompleteTextView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatMultiAutoCompleteTextView.java
new file mode 100644
index 0000000..fdba0d3
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatMultiAutoCompleteTextView.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 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.v7.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.Nullable;
+import android.support.v4.view.TintableBackgroundView;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.widget.TintContextWrapper;
+import android.support.v7.internal.widget.TintInfo;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.util.AttributeSet;
+import android.widget.MultiAutoCompleteTextView;
+
+/**
+ * A tint aware {@link android.widget.MultiAutoCompleteTextView}.
+ * <p>
+ * This will automatically be used when you use {@link android.widget.MultiAutoCompleteTextView}
+ * in your layouts. You should only need to manually use this class when writing custom views.
+ */
+public class AppCompatMultiAutoCompleteTextView extends MultiAutoCompleteTextView
+        implements TintableBackgroundView {
+
+    private static final int[] TINT_ATTRS = {
+            android.R.attr.background,
+            android.R.attr.popupBackground
+    };
+
+    private TintManager mTintManager;
+    private TintInfo mBackgroundTint;
+
+    public AppCompatMultiAutoCompleteTextView(Context context) {
+        this(context, null);
+    }
+
+    public AppCompatMultiAutoCompleteTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.autoCompleteTextViewStyle);
+    }
+
+    public AppCompatMultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
+
+        if (TintManager.SHOULD_BE_USED) {
+            TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
+                    TINT_ATTRS, defStyleAttr, 0);
+            mTintManager = a.getTintManager();
+
+            if (a.hasValue(0)) {
+                ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1));
+                if (tint != null) {
+                    setSupportBackgroundTintList(tint);
+                }
+            }
+            if (a.hasValue(1)) {
+                setDropDownBackgroundDrawable(a.getDrawable(1));
+            }
+            a.recycle();
+        }
+    }
+
+    @Override
+    public void setDropDownBackgroundResource(@DrawableRes int id) {
+        if (mTintManager != null) {
+            setDropDownBackgroundDrawable(mTintManager.getDrawable(id));
+        } else {
+            super.setDropDownBackgroundResource(id);
+        }
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
+     * android.content.res.ColorStateList)}
+     *
+     * @hide
+     */
+    @Override
+    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintList = tint;
+        mBackgroundTint.mHasTintList = true;
+
+        applySupportBackgroundTint();
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
+     *
+     * @hide
+     */
+    @Override
+    @Nullable
+    public ColorStateList getSupportBackgroundTintList() {
+        return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode)}
+     *
+     * @hide
+     */
+    @Override
+    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintMode = tintMode;
+        mBackgroundTint.mHasTintMode = true;
+
+        applySupportBackgroundTint();
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
+     *
+     * @hide
+     */
+    @Override
+    @Nullable
+    public PorterDuff.Mode getSupportBackgroundTintMode() {
+        return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        applySupportBackgroundTint();
+    }
+
+    private void applySupportBackgroundTint() {
+        if (getBackground() != null && mBackgroundTint != null) {
+            TintManager.tintViewBackground(this, mBackgroundTint);
+        }
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java
new file mode 100644
index 0000000..a9df4dc
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 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.v7.widget;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.DrawableRes;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.util.AttributeSet;
+import android.widget.RadioButton;
+
+/**
+ * A tint aware {@link android.widget.RadioButton}.
+ * <p>
+ * This will automatically be used when you use {@link android.widget.RadioButton} in your
+ * layouts. You should only need to manually use this class when writing custom views.
+ */
+public class AppCompatRadioButton extends RadioButton {
+
+    private static final int[] TINT_ATTRS = {
+            android.R.attr.button
+    };
+
+    private TintManager mTintManager;
+    private Drawable mButtonDrawable;
+
+    public AppCompatRadioButton(Context context) {
+        this(context, null);
+    }
+
+    public AppCompatRadioButton(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.radioButtonStyle);
+    }
+
+    public AppCompatRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        if (TintManager.SHOULD_BE_USED) {
+            TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
+                    TINT_ATTRS, defStyleAttr, 0);
+            setButtonDrawable(a.getDrawable(0));
+            a.recycle();
+
+            mTintManager = a.getTintManager();
+        }
+    }
+
+    @Override
+    public void setButtonDrawable(Drawable buttonDrawable) {
+        super.setButtonDrawable(buttonDrawable);
+        mButtonDrawable = buttonDrawable;
+    }
+
+    @Override
+    public void setButtonDrawable(@DrawableRes int resid) {
+        if (mTintManager != null) {
+            setButtonDrawable(mTintManager.getDrawable(resid));
+        } else {
+            super.setButtonDrawable(resid);
+        }
+    }
+
+    @Override
+    public int getCompoundPaddingLeft() {
+        int padding = super.getCompoundPaddingLeft();
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            // Before JB-MR1 the button drawable wasn't taken into account for padding. We'll
+            // workaround that here
+            if (mButtonDrawable != null) {
+                padding += mButtonDrawable.getIntrinsicWidth();
+            }
+        }
+        return padding;
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintRatingBar.java b/v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java
similarity index 78%
rename from v7/appcompat/src/android/support/v7/internal/widget/TintRatingBar.java
rename to v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java
index 592c6ff..e8b8cf9 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintRatingBar.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.v7.internal.widget;
+package android.support.v7.widget;
 
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -28,17 +28,22 @@
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.RoundRectShape;
 import android.graphics.drawable.shapes.Shape;
+import android.support.v4.graphics.drawable.DrawableWrapper;
 import android.support.v4.view.ViewCompat;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.internal.widget.TintTypedArray;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.widget.RatingBar;
 
 /**
- * An tint aware {@link android.widget.RatingBar}.
- *
- * @hide
+ * A tint aware {@link android.widget.RatingBar}.
+ * <p>
+ * This will automatically be used when you use {@link android.widget.RatingBar} in your
+ * layouts. You should only need to manually use this class when writing custom views.
  */
-public class TintRatingBar extends RatingBar {
+public class AppCompatRatingBar extends RatingBar {
 
     private static final int[] TINT_ATTRS = {
             android.R.attr.indeterminateDrawable,
@@ -47,31 +52,33 @@
 
     private Bitmap mSampleTile;
 
-    public TintRatingBar(Context context) {
+    public AppCompatRatingBar(Context context) {
         this(context, null);
     }
 
-    public TintRatingBar(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.ratingBarStyle);
+    public AppCompatRatingBar(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.ratingBarStyle);
     }
 
-    public TintRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
-                defStyleAttr, 0);
+        if (TintManager.SHOULD_BE_USED) {
+            TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
+                    TINT_ATTRS, defStyleAttr, 0);
 
-        Drawable drawable = a.getDrawable(0);
-        if (drawable != null) {
-            setIndeterminateDrawable(tileifyIndeterminate(drawable));
+            Drawable drawable = a.getDrawable(0);
+            if (drawable != null) {
+                setIndeterminateDrawable(tileifyIndeterminate(drawable));
+            }
+
+            drawable = a.getDrawable(1);
+            if (drawable != null) {
+                setProgressDrawable(tileify(drawable, false));
+            }
+
+            a.recycle();
         }
-
-        drawable = a.getDrawable(1);
-        if (drawable != null) {
-            setProgressDrawable(tileify(drawable, false));
-        }
-
-        a.recycle();
     }
 
     /**
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
new file mode 100644
index 0000000..be65bd3
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014 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.v7.widget;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.support.v4.view.TintableBackgroundView;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.widget.TintInfo;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.util.AttributeSet;
+import android.widget.ListPopupWindow;
+import android.widget.Spinner;
+
+import java.lang.reflect.Field;
+
+/**
+ * A tint aware {@link android.widget.Spinner}.
+ * <p>
+ * This will automatically be used when you use {@link android.widget.Spinner} in your
+ * layouts. You should only need to manually use this class when writing custom views.
+ */
+public class AppCompatSpinner extends Spinner implements TintableBackgroundView {
+
+    private static final int[] TINT_ATTRS = {
+            android.R.attr.background,
+            android.R.attr.popupBackground
+    };
+
+    private TintInfo mBackgroundTint;
+
+    public AppCompatSpinner(Context context) {
+        this(context, null);
+    }
+
+    public AppCompatSpinner(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.spinnerStyle);
+    }
+
+    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        if (TintManager.SHOULD_BE_USED) {
+            TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
+                    TINT_ATTRS, defStyleAttr, 0);
+            if (a.hasValue(0)) {
+                ColorStateList tint = a.getTintManager().getTintList(a.getResourceId(0, -1));
+                if (tint != null) {
+                    setSupportBackgroundTintList(tint);
+                }
+            }
+            if (a.hasValue(1)) {
+                final Drawable popupBackground = a.getDrawable(1);
+                if (Build.VERSION.SDK_INT >= 16) {
+                    setPopupBackgroundDrawable(popupBackground);
+                } else if (Build.VERSION.SDK_INT >= 11) {
+                    setPopupBackgroundDrawableV11(this, popupBackground);
+                }
+            }
+            a.recycle();
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    private static void setPopupBackgroundDrawableV11(Spinner view, Drawable background) {
+        try {
+            Field popupField = Spinner.class.getDeclaredField("mPopup");
+            popupField.setAccessible(true);
+
+            Object popup = popupField.get(view);
+
+            if (popup instanceof ListPopupWindow) {
+                ((ListPopupWindow) popup).setBackgroundDrawable(background);
+            }
+        } catch (NoSuchFieldException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
+     * android.content.res.ColorStateList)}
+     *
+     * @hide
+     */
+    @Override
+    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintList = tint;
+        mBackgroundTint.mHasTintList = true;
+
+        applySupportBackgroundTint();
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
+     *
+     * @hide
+     */
+    @Override
+    @Nullable
+    public ColorStateList getSupportBackgroundTintList() {
+        return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode)}
+     *
+     * @hide
+     */
+    @Override
+    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintMode = tintMode;
+        mBackgroundTint.mHasTintMode = true;
+
+        applySupportBackgroundTint();
+    }
+
+    /**
+     * This should be accessed via
+     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
+     *
+     * @hide
+     */
+    @Override
+    @Nullable
+    public PorterDuff.Mode getSupportBackgroundTintMode() {
+        return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        applySupportBackgroundTint();
+    }
+
+    private void applySupportBackgroundTint() {
+        if (getBackground() != null && mBackgroundTint != null) {
+            TintManager.tintViewBackground(this, mBackgroundTint);
+        }
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
new file mode 100644
index 0000000..402d63f
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
@@ -0,0 +1,83 @@
+/*
+ * 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.v7.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.v7.appcompat.R;
+import android.support.v7.internal.text.AllCapsTransformationMethod;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * A {@link android.widget.TextView} which supports compatible features on older version of the
+ * platform.
+ * <p>
+ * This will automatically be used when you use {@link android.widget.TextView} in your
+ * layouts. You should only need to manually use this class when writing custom views.
+ */
+public class AppCompatTextView extends TextView {
+
+    public AppCompatTextView(Context context) {
+        this(context, null);
+    }
+
+    public AppCompatTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AppCompatTextView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        // First read the TextAppearance style id
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppCompatTextView,
+                defStyle, 0);
+        final int ap = a.getResourceId(R.styleable.AppCompatTextView_android_textAppearance, -1);
+        a.recycle();
+
+        // Now check TextAppearance's textAllCaps value
+        if (ap != -1) {
+            TypedArray appearance = context.obtainStyledAttributes(ap, R.styleable.TextAppearance);
+            if (appearance.hasValue(R.styleable.TextAppearance_textAllCaps)) {
+                setAllCaps(appearance.getBoolean(R.styleable.TextAppearance_textAllCaps, false));
+            }
+            appearance.recycle();
+        }
+
+        // Now read the style's value
+        a = context.obtainStyledAttributes(attrs, R.styleable.AppCompatTextView, defStyle, 0);
+        if (a.hasValue(R.styleable.AppCompatTextView_textAllCaps)) {
+            setAllCaps(a.getBoolean(R.styleable.AppCompatTextView_textAllCaps, false));
+        }
+        a.recycle();
+    }
+
+    public void setAllCaps(boolean allCaps) {
+        setTransformationMethod(allCaps ? new AllCapsTransformationMethod(getContext()) : null);
+    }
+
+    @Override
+    public void setTextAppearance(Context context, int resId) {
+        super.setTextAppearance(context, resId);
+
+        TypedArray appearance = context.obtainStyledAttributes(resId, R.styleable.TextAppearance);
+        if (appearance.hasValue(R.styleable.TextAppearance_textAllCaps)) {
+            setAllCaps(appearance.getBoolean(R.styleable.TextAppearance_textAllCaps, false));
+        }
+        appearance.recycle();
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java b/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
index 01d5e1c..0d2bcb8 100644
--- a/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
+++ b/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
@@ -1387,7 +1387,9 @@
             clearCallbacks();
 
             final View src = mSrc;
-            if (!src.isEnabled()) {
+            if (!src.isEnabled() || src.isLongClickable()) {
+                // Ignore long-press if the view is disabled or has its own
+                // handler.
                 return;
             }
 
@@ -1396,12 +1398,12 @@
             }
 
             // Don't let the parent intercept our events.
-            mSrc.getParent().requestDisallowInterceptTouchEvent(true);
+            src.getParent().requestDisallowInterceptTouchEvent(true);
 
             // Make sure we cancel any ongoing source event stream.
             final long now = SystemClock.uptimeMillis();
             final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
-            mSrc.onTouchEvent(e);
+            src.onTouchEvent(e);
             e.recycle();
 
             mForwarding = true;
diff --git a/v7/appcompat/src/android/support/v7/widget/PopupMenu.java b/v7/appcompat/src/android/support/v7/widget/PopupMenu.java
index 5b7d333..5c4dde8 100644
--- a/v7/appcompat/src/android/support/v7/widget/PopupMenu.java
+++ b/v7/appcompat/src/android/support/v7/widget/PopupMenu.java
@@ -114,6 +114,29 @@
     }
 
     /**
+     * Sets the gravity used to align the popup window to its anchor view.
+     * <p>
+     * If the popup is showing, calling this method will take effect only
+     * the next time the popup is shown.
+     *
+     * @param gravity the gravity used to align the popup window
+     *
+     * @see #getGravity()
+     */
+    public void setGravity(int gravity) {
+        mPopup.setGravity(gravity);
+    }
+
+    /**
+     * @return the gravity used to align the popup window to its anchor view
+     *
+     * @see #setGravity(int)
+     */
+    public int getGravity() {
+        return mPopup.getGravity();
+    }
+
+    /**
      * Returns an {@link android.view.View.OnTouchListener} that can be added to the anchor view
      * to implement drag-to-open behavior.
      * <p>
diff --git a/v7/appcompat/src/android/support/v7/widget/SearchView.java b/v7/appcompat/src/android/support/v7/widget/SearchView.java
index 46ad229..5bc8ac0 100644
--- a/v7/appcompat/src/android/support/v7/widget/SearchView.java
+++ b/v7/appcompat/src/android/support/v7/widget/SearchView.java
@@ -39,7 +39,6 @@
 import android.support.v4.view.KeyEventCompat;
 import android.support.v4.widget.CursorAdapter;
 import android.support.v7.appcompat.R;
-import android.support.v7.internal.widget.TintAutoCompleteTextView;
 import android.support.v7.internal.widget.TintManager;
 import android.support.v7.internal.widget.TintTypedArray;
 import android.support.v7.internal.widget.ViewUtils;
@@ -114,17 +113,21 @@
      */
     private static final String IME_OPTION_NO_MICROPHONE = "nm";
 
-    private final SearchAutoComplete mQueryTextView;
+    private final SearchAutoComplete mSearchSrcTextView;
     private final View mSearchEditFrame;
     private final View mSearchPlate;
     private final View mSubmitArea;
     private final ImageView mSearchButton;
-    private final ImageView mSubmitButton;
+    private final ImageView mGoButton;
     private final ImageView mCloseButton;
     private final ImageView mVoiceButton;
-    private final ImageView mSearchHintIcon;
     private final View mDropDownAnchor;
-    private final int mSearchIconResId;
+
+    /** Icon optionally displayed when the SearchView is collapsed. */
+    private final ImageView mCollapsedIcon;
+
+    /** Drawable used as an EditText hint. */
+    private final Drawable mSearchHintIcon;
 
     // Resources used by SuggestionsAdapter to display suggestions.
     private final int mSuggestionRowLayout;
@@ -133,6 +136,7 @@
     // Intents used for voice searching.
     private final Intent mVoiceWebSearchIntent;
     private final Intent mVoiceAppSearchIntent;
+
     private OnQueryTextListener mOnQueryChangeListener;
     private OnCloseListener mOnCloseListener;
     private OnFocusChangeListener mOnQueryTextFocusChangeListener;
@@ -277,48 +281,53 @@
         // Keep the TintManager in case we need it later
         mTintManager = a.getTintManager();
 
-        final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-        final int layoutResId = a.getResourceId(R.styleable.SearchView_layout, 0);
+        final LayoutInflater inflater = LayoutInflater.from(context);
+        final int layoutResId = a.getResourceId(
+                R.styleable.SearchView_layout, R.layout.abc_search_view);
         inflater.inflate(layoutResId, this, true);
-        mQueryTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
-        mQueryTextView.setSearchView(this);
+
+        mSearchSrcTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
+        mSearchSrcTextView.setSearchView(this);
 
         mSearchEditFrame = findViewById(R.id.search_edit_frame);
         mSearchPlate = findViewById(R.id.search_plate);
         mSubmitArea = findViewById(R.id.submit_area);
         mSearchButton = (ImageView) findViewById(R.id.search_button);
-        mSubmitButton = (ImageView) findViewById(R.id.search_go_btn);
+        mGoButton = (ImageView) findViewById(R.id.search_go_btn);
         mCloseButton = (ImageView) findViewById(R.id.search_close_btn);
         mVoiceButton = (ImageView) findViewById(R.id.search_voice_btn);
-        mSearchHintIcon = (ImageView) findViewById(R.id.search_mag_icon);
+        mCollapsedIcon = (ImageView) findViewById(R.id.search_mag_icon);
+
         // Set up icons and backgrounds.
         mSearchPlate.setBackgroundDrawable(a.getDrawable(R.styleable.SearchView_queryBackground));
         mSubmitArea.setBackgroundDrawable(a.getDrawable(R.styleable.SearchView_submitBackground));
-        mSearchIconResId = a.getResourceId(R.styleable.SearchView_searchIcon, 0);
-        mSearchButton.setImageResource(mSearchIconResId);
-        mSubmitButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_goIcon));
+        mSearchButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+        mGoButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_goIcon));
         mCloseButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_closeIcon));
         mVoiceButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_voiceIcon));
-        mSearchHintIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+        mCollapsedIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+
+        mSearchHintIcon = a.getDrawable(R.styleable.SearchView_searchHintIcon);
 
         // Extract dropdown layout resource IDs for later use.
-        mSuggestionRowLayout = a.getResourceId(R.styleable.SearchView_suggestionRowLayout, 0);
+        mSuggestionRowLayout = a.getResourceId(R.styleable.SearchView_suggestionRowLayout,
+                R.layout.abc_search_dropdown_item_icons_2line);
         mSuggestionCommitIconResId = a.getResourceId(R.styleable.SearchView_commitIcon, 0);
 
         mSearchButton.setOnClickListener(mOnClickListener);
         mCloseButton.setOnClickListener(mOnClickListener);
-        mSubmitButton.setOnClickListener(mOnClickListener);
+        mGoButton.setOnClickListener(mOnClickListener);
         mVoiceButton.setOnClickListener(mOnClickListener);
-        mQueryTextView.setOnClickListener(mOnClickListener);
+        mSearchSrcTextView.setOnClickListener(mOnClickListener);
 
-        mQueryTextView.addTextChangedListener(mTextWatcher);
-        mQueryTextView.setOnEditorActionListener(mOnEditorActionListener);
-        mQueryTextView.setOnItemClickListener(mOnItemClickListener);
-        mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
-        mQueryTextView.setOnKeyListener(mTextKeyListener);
+        mSearchSrcTextView.addTextChangedListener(mTextWatcher);
+        mSearchSrcTextView.setOnEditorActionListener(mOnEditorActionListener);
+        mSearchSrcTextView.setOnItemClickListener(mOnItemClickListener);
+        mSearchSrcTextView.setOnItemSelectedListener(mOnItemSelectedListener);
+        mSearchSrcTextView.setOnKeyListener(mTextKeyListener);
+
         // Inform any listener of focus changes
-        mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
+        mSearchSrcTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
 
             public void onFocusChange(View v, boolean hasFocus) {
                 if (mOnQueryTextFocusChangeListener != null) {
@@ -332,14 +341,17 @@
         if (maxWidth != -1) {
             setMaxWidth(maxWidth);
         }
+
         final CharSequence queryHint = a.getText(R.styleable.SearchView_queryHint);
         if (!TextUtils.isEmpty(queryHint)) {
             setQueryHint(queryHint);
         }
+
         final int imeOptions = a.getInt(R.styleable.SearchView_android_imeOptions, -1);
         if (imeOptions != -1) {
             setImeOptions(imeOptions);
         }
+
         final int inputType = a.getInt(R.styleable.SearchView_android_inputType, -1);
         if (inputType != -1) {
             setInputType(inputType);
@@ -360,7 +372,7 @@
         mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
         mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-        mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor());
+        mDropDownAnchor = findViewById(mSearchSrcTextView.getDropDownAnchor());
         if (mDropDownAnchor != null) {
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                 addOnLayoutChangeListenerToDropDownAnchorSDK11();
@@ -424,7 +436,7 @@
         if (mVoiceButtonEnabled) {
             // Disable the microphone on the keyboard, as a mic is displayed near the text box
             // TODO: use imeOptions to disable voice input when the new API will be available
-            mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
+            mSearchSrcTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
         }
         updateViewsVisibility(isIconified());
     }
@@ -445,7 +457,7 @@
      * @param imeOptions the options to set on the query text field
      */
     public void setImeOptions(int imeOptions) {
-        mQueryTextView.setImeOptions(imeOptions);
+        mSearchSrcTextView.setImeOptions(imeOptions);
     }
 
     /**
@@ -454,7 +466,7 @@
      * @see TextView#setImeOptions(int)
      */
     public int getImeOptions() {
-        return mQueryTextView.getImeOptions();
+        return mSearchSrcTextView.getImeOptions();
     }
 
     /**
@@ -464,7 +476,7 @@
      * @param inputType the input type to set on the query text field
      */
     public void setInputType(int inputType) {
-        mQueryTextView.setInputType(inputType);
+        mSearchSrcTextView.setInputType(inputType);
     }
 
     /**
@@ -472,7 +484,7 @@
      * @return the input type
      */
     public int getInputType() {
-        return mQueryTextView.getInputType();
+        return mSearchSrcTextView.getInputType();
     }
 
     /** @hide */
@@ -484,7 +496,7 @@
         if (!isFocusable()) return false;
         // If it is not iconified, then give the focus to the text field
         if (!isIconified()) {
-            boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect);
+            boolean result = mSearchSrcTextView.requestFocus(direction, previouslyFocusedRect);
             if (result) {
                 updateViewsVisibility(false);
             }
@@ -500,7 +512,7 @@
         mClearingFocus = true;
         setImeVisibility(false);
         super.clearFocus();
-        mQueryTextView.clearFocus();
+        mSearchSrcTextView.clearFocus();
         mClearingFocus = false;
     }
 
@@ -559,7 +571,7 @@
      * @return the query string
      */
     public CharSequence getQuery() {
-        return mQueryTextView.getText();
+        return mSearchSrcTextView.getText();
     }
 
     /**
@@ -571,9 +583,9 @@
      * text field.
      */
     public void setQuery(CharSequence query, boolean submit) {
-        mQueryTextView.setText(query);
+        mSearchSrcTextView.setText(query);
         if (query != null) {
-            mQueryTextView.setSelection(mQueryTextView.length());
+            mSearchSrcTextView.setSelection(mSearchSrcTextView.length());
             mUserQuery = query;
         }
 
@@ -726,7 +738,7 @@
     public void setSuggestionsAdapter(CursorAdapter adapter) {
         mSuggestionsAdapter = adapter;
 
-        mQueryTextView.setAdapter(mSuggestionsAdapter);
+        mSearchSrcTextView.setAdapter(mSuggestionsAdapter);
     }
 
     /**
@@ -800,12 +812,12 @@
         // Visibility of views that are visible when collapsed
         final int visCollapsed = collapsed ? VISIBLE : GONE;
         // Is there text in the query
-        final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+        final boolean hasText = !TextUtils.isEmpty(mSearchSrcTextView.getText());
 
         mSearchButton.setVisibility(visCollapsed);
         updateSubmitButton(hasText);
         mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE);
-        mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
+        mCollapsedIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
         updateCloseButton();
         updateVoiceButton(!hasText);
         updateSubmitArea();
@@ -813,8 +825,7 @@
 
     @TargetApi(Build.VERSION_CODES.FROYO)
     private boolean hasVoiceSearch() {
-        if (mSearchable != null &&
-                mSearchable.getVoiceSearchEnabled()) {
+        if (mSearchable != null && mSearchable.getVoiceSearchEnabled()) {
             Intent testIntent = null;
             if (mSearchable.getVoiceSearchLaunchWebSearch()) {
                 testIntent = mVoiceWebSearchIntent;
@@ -840,26 +851,29 @@
                 && (hasText || !mVoiceButtonEnabled)) {
             visibility = VISIBLE;
         }
-        mSubmitButton.setVisibility(visibility);
+        mGoButton.setVisibility(visibility);
     }
 
     private void updateSubmitArea() {
         int visibility = GONE;
         if (isSubmitAreaEnabled()
-                && (mSubmitButton.getVisibility() == VISIBLE
-                || mVoiceButton.getVisibility() == VISIBLE)) {
+                && (mGoButton.getVisibility() == VISIBLE
+                        || mVoiceButton.getVisibility() == VISIBLE)) {
             visibility = VISIBLE;
         }
         mSubmitArea.setVisibility(visibility);
     }
 
     private void updateCloseButton() {
-        final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+        final boolean hasText = !TextUtils.isEmpty(mSearchSrcTextView.getText());
         // Should we show the close button? It is not shown if there's no focus,
         // field is not iconified by default and there is no text in it.
         final boolean showClose = hasText || (mIconifiedByDefault && !mExpandedInActionView);
         mCloseButton.setVisibility(showClose ? VISIBLE : GONE);
-        mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
+        final Drawable closeButtonImg = mCloseButton.getDrawable();
+        if (closeButtonImg != null){
+            closeButtonImg.setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
+        }
     }
 
     private void postUpdateFocusedState() {
@@ -867,9 +881,16 @@
     }
 
     private void updateFocusedState() {
-        boolean focused = mQueryTextView.hasFocus();
-        mSearchPlate.getBackground().setState(focused ? ENABLED_FOCUSED_STATE_SET : EMPTY_STATE_SET);
-        mSubmitArea.getBackground().setState(focused ? ENABLED_FOCUSED_STATE_SET : EMPTY_STATE_SET);
+        final boolean focused = mSearchSrcTextView.hasFocus();
+        final int[] stateSet = focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET;
+        final Drawable searchPlateBg = mSearchPlate.getBackground();
+        if (searchPlateBg != null) {
+            searchPlateBg.setState(stateSet);
+        }
+        final Drawable submitAreaBg = mSubmitArea.getBackground();
+        if (submitAreaBg != null) {
+            submitAreaBg.setState(stateSet);
+        }
         invalidate();
     }
 
@@ -909,13 +930,11 @@
                 onSearchClicked();
             } else if (v == mCloseButton) {
                 onCloseClicked();
-            } else if (v == mSubmitButton) {
+            } else if (v == mGoButton) {
                 onSubmitQuery();
             } else if (v == mVoiceButton) {
-                if (IS_AT_LEAST_FROYO) {
-                    onVoiceClicked();
-                }
-            } else if (v == mQueryTextView) {
+                onVoiceClicked();
+            } else if (v == mSearchSrcTextView) {
                 forceSuggestionQuery();
             }
         }
@@ -935,25 +954,25 @@
 
             if (DBG) {
                 Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: "
-                        + mQueryTextView.getListSelection());
+                        + mSearchSrcTextView.getListSelection());
             }
 
             // If a suggestion is selected, handle enter, search key, and action keys
             // as presses on the selected suggestion
-            if (mQueryTextView.isPopupShowing()
-                    && mQueryTextView.getListSelection() != ListView.INVALID_POSITION) {
+            if (mSearchSrcTextView.isPopupShowing()
+                    && mSearchSrcTextView.getListSelection() != ListView.INVALID_POSITION) {
                 return onSuggestionsKey(v, keyCode, event);
             }
 
             // If there is text in the query box, handle enter, and action keys
             // The search key is handled by the dialog's onKeyDown().
-            if (!mQueryTextView.isEmpty() && KeyEventCompat.hasNoModifiers(event)) {
+            if (!mSearchSrcTextView.isEmpty() && KeyEventCompat.hasNoModifiers(event)) {
                 if (event.getAction() == KeyEvent.ACTION_UP) {
                     if (keyCode == KeyEvent.KEYCODE_ENTER) {
                         v.cancelLongPress();
 
                         // Launch as a regular search.
-                        launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mQueryTextView.getText()
+                        launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mSearchSrcTextView.getText()
                                 .toString());
                         return true;
                     }
@@ -981,7 +1000,7 @@
             // "click")
             if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH
                     || keyCode == KeyEvent.KEYCODE_TAB) {
-                int position = mQueryTextView.getListSelection();
+                int position = mSearchSrcTextView.getListSelection();
                 return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
             }
 
@@ -992,18 +1011,18 @@
                 // left key, at end if right key
                 // TODO: Reverse left/right for right-to-left languages, e.g.
                 // Arabic
-                int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView
+                int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mSearchSrcTextView
                         .length();
-                mQueryTextView.setSelection(selPoint);
-                mQueryTextView.setListSelection(0);
-                mQueryTextView.clearListSelection();
-                HIDDEN_METHOD_INVOKER.ensureImeVisible(mQueryTextView, true);
+                mSearchSrcTextView.setSelection(selPoint);
+                mSearchSrcTextView.setListSelection(0);
+                mSearchSrcTextView.clearListSelection();
+                HIDDEN_METHOD_INVOKER.ensureImeVisible(mSearchSrcTextView, true);
 
                 return true;
             }
 
             // Next, check for an "up and out" move
-            if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) {
+            if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mSearchSrcTextView.getListSelection()) {
                 // TODO: restoreUserQuery();
                 // let ACTV complete the move
                 return false;
@@ -1013,24 +1032,24 @@
     }
 
     private CharSequence getDecoratedHint(CharSequence hintText) {
-        // If the field is always expanded, then don't add the search icon to the hint
-        if (!mIconifiedByDefault) {
+        // If the field is always expanded or we don't have a search hint icon,
+        // then don't add the search icon to the hint.
+        if (!mIconifiedByDefault || mSearchHintIcon == null) {
             return hintText;
         }
 
-        final Drawable searchIcon = mTintManager.getDrawable(mSearchIconResId);
-        final int textSize = (int) (mQueryTextView.getTextSize() * 1.25);
-        searchIcon.setBounds(0, 0, textSize, textSize);
+        final int textSize = (int) (mSearchSrcTextView.getTextSize() * 1.25);
+        mSearchHintIcon.setBounds(0, 0, textSize, textSize);
 
-        final SpannableStringBuilder ssb = new SpannableStringBuilder("   "); // for the icon
+        final SpannableStringBuilder ssb = new SpannableStringBuilder("   ");
+        ssb.setSpan(new ImageSpan(mSearchHintIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
         ssb.append(hintText);
-        ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
         return ssb;
     }
 
     private void updateQueryHint() {
         if (mQueryHint != null) {
-            mQueryTextView.setHint(getDecoratedHint(mQueryHint));
+            mSearchSrcTextView.setHint(getDecoratedHint(mQueryHint));
         } else if (IS_AT_LEAST_FROYO && mSearchable != null) {
             CharSequence hint = null;
             int hintId = mSearchable.getHintId();
@@ -1038,10 +1057,10 @@
                 hint = getContext().getString(hintId);
             }
             if (hint != null) {
-                mQueryTextView.setHint(getDecoratedHint(hint));
+                mSearchSrcTextView.setHint(getDecoratedHint(hint));
             }
         } else {
-            mQueryTextView.setHint(getDecoratedHint(""));
+            mSearchSrcTextView.setHint(getDecoratedHint(""));
         }
     }
 
@@ -1050,8 +1069,8 @@
      */
     @TargetApi(Build.VERSION_CODES.FROYO)
     private void updateSearchAutoComplete() {
-        mQueryTextView.setThreshold(mSearchable.getSuggestThreshold());
-        mQueryTextView.setImeOptions(mSearchable.getImeOptions());
+        mSearchSrcTextView.setThreshold(mSearchable.getSuggestThreshold());
+        mSearchSrcTextView.setImeOptions(mSearchable.getImeOptions());
         int inputType = mSearchable.getInputType();
         // We only touch this if the input type is set up for text (which it almost certainly
         // should be, in the case of search!)
@@ -1070,7 +1089,7 @@
                 inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
             }
         }
-        mQueryTextView.setInputType(inputType);
+        mSearchSrcTextView.setInputType(inputType);
         if (mSuggestionsAdapter != null) {
             mSuggestionsAdapter.changeCursor(null);
         }
@@ -1079,10 +1098,10 @@
         if (mSearchable.getSuggestAuthority() != null) {
             mSuggestionsAdapter = new SuggestionsAdapter(getContext(),
                     this, mSearchable, mOutsideDrawablesCache);
-            mQueryTextView.setAdapter(mSuggestionsAdapter);
+            mSearchSrcTextView.setAdapter(mSuggestionsAdapter);
             ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
                     mQueryRefinement ? SuggestionsAdapter.REFINE_ALL
-                            : SuggestionsAdapter.REFINE_BY_ENTRY);
+                    : SuggestionsAdapter.REFINE_BY_ENTRY);
         }
     }
 
@@ -1096,7 +1115,7 @@
         int visibility = GONE;
         if (mVoiceButtonEnabled && !isIconified() && empty) {
             visibility = VISIBLE;
-            mSubmitButton.setVisibility(GONE);
+            mGoButton.setVisibility(GONE);
         }
         mVoiceButton.setVisibility(visibility);
     }
@@ -1113,7 +1132,7 @@
     };
 
     private void onTextChanged(CharSequence newText) {
-        CharSequence text = mQueryTextView.getText();
+        CharSequence text = mSearchSrcTextView.getText();
         mUserQuery = text;
         boolean hasText = !TextUtils.isEmpty(text);
         updateSubmitButton(hasText);
@@ -1127,7 +1146,7 @@
     }
 
     private void onSubmitQuery() {
-        CharSequence query = mQueryTextView.getText();
+        CharSequence query = mSearchSrcTextView.getText();
         if (query != null && TextUtils.getTrimmedLength(query) > 0) {
             if (mOnQueryChangeListener == null
                     || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
@@ -1141,11 +1160,11 @@
     }
 
     private void dismissSuggestions() {
-        mQueryTextView.dismissDropDown();
+        mSearchSrcTextView.dismissDropDown();
     }
 
     private void onCloseClicked() {
-        CharSequence text = mQueryTextView.getText();
+        CharSequence text = mSearchSrcTextView.getText();
         if (TextUtils.isEmpty(text)) {
             if (mIconifiedByDefault) {
                 // If the app doesn't override the close behavior
@@ -1157,8 +1176,8 @@
                 }
             }
         } else {
-            mQueryTextView.setText("");
-            mQueryTextView.requestFocus();
+            mSearchSrcTextView.setText("");
+            mSearchSrcTextView.requestFocus();
             setImeVisibility(true);
         }
 
@@ -1166,7 +1185,7 @@
 
     private void onSearchClicked() {
         updateViewsVisibility(false);
-        mQueryTextView.requestFocus();
+        mSearchSrcTextView.requestFocus();
         setImeVisibility(true);
         if (mOnSearchClickListener != null) {
             mOnSearchClickListener.onClick(this);
@@ -1202,7 +1221,7 @@
         // Delayed update to make sure that the focus has settled down and window focus changes
         // don't affect it. A synchronous update was not working.
         postUpdateFocusedState();
-        if (mQueryTextView.hasFocus()) {
+        if (mSearchSrcTextView.hasFocus()) {
             forceSuggestionQuery();
         }
     }
@@ -1222,7 +1241,7 @@
         setQuery("", false);
         clearFocus();
         updateViewsVisibility(true);
-        mQueryTextView.setImeOptions(mCollapsedImeOptions);
+        mSearchSrcTextView.setImeOptions(mCollapsedImeOptions);
         mExpandedInActionView = false;
     }
 
@@ -1234,9 +1253,9 @@
         if (mExpandedInActionView) return;
 
         mExpandedInActionView = true;
-        mCollapsedImeOptions = mQueryTextView.getImeOptions();
-        mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
-        mQueryTextView.setText("");
+        mCollapsedImeOptions = mSearchSrcTextView.getImeOptions();
+        mSearchSrcTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
+        mSearchSrcTextView.setText("");
         setIconified(false);
     }
 
@@ -1251,17 +1270,17 @@
                     ? res.getDimensionPixelSize(R.dimen.abc_dropdownitem_icon_width)
                     + res.getDimensionPixelSize(R.dimen.abc_dropdownitem_text_padding_left)
                     : 0;
-            mQueryTextView.getDropDownBackground().getPadding(dropDownPadding);
+            mSearchSrcTextView.getDropDownBackground().getPadding(dropDownPadding);
             int offset;
             if (isLayoutRtl) {
                 offset = - dropDownPadding.left;
             } else {
                 offset = anchorPadding - (dropDownPadding.left + iconOffset);
             }
-            mQueryTextView.setDropDownHorizontalOffset(offset);
+            mSearchSrcTextView.setDropDownHorizontalOffset(offset);
             final int width = mDropDownAnchor.getWidth() + dropDownPadding.left
                     + dropDownPadding.right + iconOffset - anchorPadding;
-            mQueryTextView.setDropDownWidth(width);
+            mSearchSrcTextView.setDropDownWidth(width);
         }
     }
 
@@ -1319,7 +1338,7 @@
      * Query rewriting.
      */
     private void rewriteQueryFromSuggestion(int position) {
-        CharSequence oldQuery = mQueryTextView.getText();
+        CharSequence oldQuery = mSearchSrcTextView.getText();
         Cursor c = mSuggestionsAdapter.getCursor();
         if (c == null) {
             return;
@@ -1385,9 +1404,9 @@
      * Sets the text in the query box, without updating the suggestions.
      */
     private void setQuery(CharSequence query) {
-        mQueryTextView.setText(query);
+        mSearchSrcTextView.setText(query);
         // Move the cursor to the end
-        mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
+        mSearchSrcTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
     }
 
     private void launchQuerySearch(int actionKey, String actionMsg, String query) {
@@ -1571,14 +1590,14 @@
                 rowNum = -1;
             }
             Log.w(LOG_TAG, "Search suggestions cursor at row " + rowNum +
-                    " returned exception.", e);
+                            " returned exception.", e);
             return null;
         }
     }
 
     private void forceSuggestionQuery() {
-        HIDDEN_METHOD_INVOKER.doBeforeTextChanged(mQueryTextView);
-        HIDDEN_METHOD_INVOKER.doAfterTextChanged(mQueryTextView);
+        HIDDEN_METHOD_INVOKER.doBeforeTextChanged(mSearchSrcTextView);
+        HIDDEN_METHOD_INVOKER.doAfterTextChanged(mSearchSrcTextView);
     }
 
     static boolean isLandscapeMode(Context context) {
@@ -1606,7 +1625,7 @@
      * Local subclass for AutoCompleteTextView.
      * @hide
      */
-    public static class SearchAutoComplete extends TintAutoCompleteTextView {
+    public static class SearchAutoComplete extends AppCompatAutoCompleteTextView {
 
         private int mThreshold;
         private SearchView mSearchView;
diff --git a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
index 74d6e4a..9c53920 100644
--- a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
+++ b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.widget;
 
-import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -25,6 +24,7 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -33,6 +33,7 @@
 import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
 import android.support.v7.internal.text.AllCapsTransformationMethod;
+import android.support.v7.internal.widget.DrawableUtils;
 import android.support.v7.internal.widget.TintManager;
 import android.support.v7.internal.widget.TintTypedArray;
 import android.support.v7.internal.widget.ViewUtils;
@@ -76,6 +77,10 @@
     private static final int TOUCH_MODE_DOWN = 1;
     private static final int TOUCH_MODE_DRAGGING = 2;
 
+    // We force the accessibility events to have a class name of Switch, since screen readers
+    // already know how to handle their events
+    private static final String ACCESSIBILITY_EVENT_CLASS_NAME = "android.widget.Switch";
+
     // Enum for the "typeface" XML parameter.
     private static final int SANS = 1;
     private static final int SERIF = 2;
@@ -187,7 +192,13 @@
         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
                 attrs, R.styleable.SwitchCompat, defStyleAttr, 0);
         mThumbDrawable = a.getDrawable(R.styleable.SwitchCompat_android_thumb);
+        if (mThumbDrawable != null) {
+            mThumbDrawable.setCallback(this);
+        }
         mTrackDrawable = a.getDrawable(R.styleable.SwitchCompat_track);
+        if (mTrackDrawable != null) {
+            mTrackDrawable.setCallback(this);
+        }
         mTextOn = a.getText(R.styleable.SwitchCompat_android_textOn);
         mTextOff = a.getText(R.styleable.SwitchCompat_android_textOff);
         mShowText = a.getBoolean(R.styleable.SwitchCompat_showText, true);
@@ -223,14 +234,12 @@
      * from the specified TextAppearance resource.
      */
     public void setSwitchTextAppearance(Context context, int resid) {
-        TypedArray appearance = context.obtainStyledAttributes(resid,
-                R.styleable.SwitchCompatTextAppearance);
+        TypedArray appearance = context.obtainStyledAttributes(resid, R.styleable.TextAppearance);
 
         ColorStateList colors;
         int ts;
 
-        colors = appearance.getColorStateList(
-                R.styleable.SwitchCompatTextAppearance_android_textColor);
+        colors = appearance.getColorStateList(R.styleable.TextAppearance_android_textColor);
         if (colors != null) {
             mTextColors = colors;
         } else {
@@ -238,8 +247,7 @@
             mTextColors = getTextColors();
         }
 
-        ts = appearance.getDimensionPixelSize(
-                R.styleable.SwitchCompatTextAppearance_android_textSize, 0);
+        ts = appearance.getDimensionPixelSize(R.styleable.TextAppearance_android_textSize, 0);
         if (ts != 0) {
             if (ts != mTextPaint.getTextSize()) {
                 mTextPaint.setTextSize(ts);
@@ -247,8 +255,13 @@
             }
         }
 
-        boolean allCaps = appearance.getBoolean(
-                R.styleable.SwitchCompatTextAppearance_textAllCaps, false);
+        int typefaceIndex, styleIndex;
+        typefaceIndex = appearance.getInt(R.styleable.TextAppearance_android_typeface, -1);
+        styleIndex = appearance.getInt(R.styleable.TextAppearance_android_textStyle, -1);
+
+        setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
+
+        boolean allCaps = appearance.getBoolean(R.styleable.TextAppearance_textAllCaps, false);
         if (allCaps) {
             mSwitchTransformationMethod = new AllCapsTransformationMethod(getContext());
         } else {
@@ -258,6 +271,25 @@
         appearance.recycle();
     }
 
+    private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {
+        Typeface tf = null;
+        switch (typefaceIndex) {
+            case SANS:
+                tf = Typeface.SANS_SERIF;
+                break;
+
+            case SERIF:
+                tf = Typeface.SERIF;
+                break;
+
+            case MONOSPACE:
+                tf = Typeface.MONOSPACE;
+                break;
+        }
+
+        setSwitchTypeface(tf, styleIndex);
+    }
+
     /**
      * Sets the typeface and style in which the text should be displayed on the
      * switch, and turns on the fake bold and italic bits in the Paint if the
@@ -535,6 +567,11 @@
         // thumb's padding (when present).
         int paddingLeft = padding.left;
         int paddingRight = padding.right;
+        if (mThumbDrawable != null) {
+            final Rect inset = DrawableUtils.getOpticalBounds(mThumbDrawable);
+            paddingLeft = Math.max(paddingLeft, inset.left);
+            paddingRight = Math.max(paddingRight, inset.right);
+        }
 
         final int switchWidth = Math.max(mSwitchMinWidth,
                 2 * mThumbWidth + paddingLeft + paddingRight);
@@ -567,7 +604,8 @@
                 : text;
 
         return new StaticLayout(transformed, mTextPaint,
-                (int) Math.ceil(Layout.getDesiredWidth(transformed, mTextPaint)),
+                transformed != null ?
+                        (int) Math.ceil(Layout.getDesiredWidth(transformed, mTextPaint)) : 0,
                 Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
     }
 
@@ -575,6 +613,10 @@
      * @return true if (x, y) is within the target area of the switch thumb
      */
     private boolean hitThumb(float x, float y) {
+        if (mThumbDrawable == null) {
+            return false;
+        }
+
         // Relies on mTempRect, MUST be called first!
         final int thumbOffset = getThumbOffset();
 
@@ -754,7 +796,7 @@
         // recursively with a different value, so load the REAL value...
         checked = isChecked();
 
-        if (getWindowToken() != null) {
+        if (getWindowToken() != null && ViewCompat.isLaidOut(this)) {
             animateThumbToCheckedState(checked);
         } else {
             // Immediately move the thumb to the new position.
@@ -777,8 +819,9 @@
                 trackPadding.setEmpty();
             }
 
-            opticalInsetLeft = 0;
-            opticalInsetRight = 0;
+            final Rect insets = DrawableUtils.getOpticalBounds(mThumbDrawable);
+            opticalInsetLeft = Math.max(0, insets.left - trackPadding.left);
+            opticalInsetRight = Math.max(0, insets.right - trackPadding.right);
         }
 
         final int switchRight;
@@ -828,6 +871,13 @@
 
         int thumbInitialLeft = switchLeft + getThumbOffset();
 
+        final Rect thumbInsets;
+        if (mThumbDrawable != null) {
+            thumbInsets = DrawableUtils.getOpticalBounds(mThumbDrawable);
+        } else {
+            thumbInsets = DrawableUtils.INSETS_NONE;
+        }
+
         // Layout the track.
         if (mTrackDrawable != null) {
             mTrackDrawable.getPadding(padding);
@@ -840,6 +890,20 @@
             int trackTop = switchTop;
             int trackRight = switchRight;
             int trackBottom = switchBottom;
+            if (thumbInsets != null && !thumbInsets.isEmpty()) {
+                if (thumbInsets.left > padding.left) {
+                    trackLeft += thumbInsets.left - padding.left;
+                }
+                if (thumbInsets.top > padding.top) {
+                    trackTop += thumbInsets.top - padding.top;
+                }
+                if (thumbInsets.right > padding.right) {
+                    trackRight -= thumbInsets.right - padding.right;
+                }
+                if (thumbInsets.bottom > padding.bottom) {
+                    trackBottom -= thumbInsets.bottom - padding.bottom;
+                }
+            }
             mTrackDrawable.setBounds(trackLeft, trackTop, trackRight, trackBottom);
         }
 
@@ -881,7 +945,19 @@
 
         final Drawable thumbDrawable = mThumbDrawable;
         if (trackDrawable != null) {
-            trackDrawable.draw(canvas);
+            if (mSplitTrack && thumbDrawable != null) {
+                final Rect insets = DrawableUtils.getOpticalBounds(thumbDrawable);
+                thumbDrawable.copyBounds(padding);
+                padding.left += insets.left;
+                padding.right -= insets.right;
+
+                final int saveCount = canvas.save();
+                canvas.clipRect(padding, Region.Op.DIFFERENCE);
+                trackDrawable.draw(canvas);
+                canvas.restoreToCount(saveCount);
+            } else {
+                trackDrawable.draw(canvas);
+            }
         }
 
         final int saveCount = canvas.save();
@@ -959,7 +1035,16 @@
         if (mTrackDrawable != null) {
             final Rect padding = mTempRect;
             mTrackDrawable.getPadding(padding);
-            return mSwitchWidth - mThumbWidth - padding.left - padding.right;
+
+            final Rect insets;
+            if (mThumbDrawable != null) {
+                insets = DrawableUtils.getOpticalBounds(mThumbDrawable);
+            } else {
+                insets = DrawableUtils.INSETS_NONE;
+            }
+
+            return mSwitchWidth - mThumbWidth - padding.left - padding.right
+                    - insets.left - insets.right;
         } else {
             return 0;
         }
@@ -1024,8 +1109,7 @@
                 mTrackDrawable.jumpToCurrentState();
             }
 
-            if (mPositionAnimator != null && mPositionAnimator.hasStarted() &&
-                    !mPositionAnimator.hasEnded()) {
+            if (mPositionAnimator != null && !mPositionAnimator.hasEnded()) {
                 clearAnimation();
                 mPositionAnimator = null;
             }
@@ -1036,14 +1120,14 @@
     @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
-        event.setClassName(SwitchCompat.class.getName());
+        event.setClassName(ACCESSIBILITY_EVENT_CLASS_NAME);
     }
 
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         if (Build.VERSION.SDK_INT >= 14) {
             super.onInitializeAccessibilityNodeInfo(info);
-            info.setClassName(SwitchCompat.class.getName());
+            info.setClassName(ACCESSIBILITY_EVENT_CLASS_NAME);
             CharSequence switchText = isChecked() ? mTextOn : mTextOff;
             if (!TextUtils.isEmpty(switchText)) {
                 CharSequence oldText = info.getText();
diff --git a/v7/appcompat/src/android/support/v7/widget/Toolbar.java b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
index d98aec7..dab1cd5 100644
--- a/v7/appcompat/src/android/support/v7/widget/Toolbar.java
+++ b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
@@ -17,11 +17,14 @@
 package android.support.v7.widget;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.ColorInt;
 import android.support.annotation.Nullable;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.MarginLayoutParamsCompat;
@@ -30,6 +33,7 @@
 import android.support.v4.view.ViewCompat;
 import android.support.v7.app.ActionBar;
 import android.support.v7.appcompat.R;
+import android.support.v7.graphics.drawable.DrawableUtils;
 import android.support.v7.internal.view.SupportMenuInflater;
 import android.support.v7.internal.view.menu.MenuBuilder;
 import android.support.v7.internal.view.menu.MenuItemImpl;
@@ -38,6 +42,8 @@
 import android.support.v7.internal.view.menu.SubMenuBuilder;
 import android.support.v7.internal.widget.DecorToolbar;
 import android.support.v7.internal.widget.RtlSpacingHelper;
+import android.support.v7.internal.widget.TintImageButton;
+import android.support.v7.internal.widget.TintInfo;
 import android.support.v7.internal.widget.TintManager;
 import android.support.v7.internal.widget.TintTypedArray;
 import android.support.v7.internal.widget.ToolbarWidgetWrapper;
@@ -69,7 +75,7 @@
  * {@link android.app.Activity Activity's} opaque window decor controlled by the framework,
  * a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy.
  * An application may choose to designate a Toolbar as the action bar for an Activity
- * using the {@link android.support.v7.app.ActionBarActivity#setSupportActionBar(Toolbar)
+ * using the {@link android.support.v7.app.AppCompatActivity#setSupportActionBar(Toolbar)
  * setSupportActionBar()} method.</p>
  *
  * <p>Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar
@@ -110,12 +116,15 @@
     private ActionMenuView mMenuView;
     private TextView mTitleTextView;
     private TextView mSubtitleTextView;
-    private ImageButton mNavButtonView;
+    private TintImageButton mNavButtonView;
     private ImageView mLogoView;
 
+    private TintInfo mOverflowTintInfo;
+    private TintInfo mNavTintInfo;
+
     private Drawable mCollapseIcon;
     private CharSequence mCollapseDescription;
-    private ImageButton mCollapseButtonView;
+    private TintImageButton mCollapseButtonView;
     View mExpandedActionView;
 
     /** Context against which to inflate popup menus. */
@@ -188,12 +197,14 @@
         this(context, null);
     }
 
-    public Toolbar(Context context, AttributeSet attrs) {
+    public Toolbar(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.toolbarStyle);
     }
 
-    public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(themifyContext(context, attrs, defStyleAttr), attrs, defStyleAttr);
+    public Toolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        // We manually themify the context here so that we don't break apps which only
+        // use app:theme when running on >= Lollipop
+        super(ViewUtils.themifyContext(context, attrs, false, true), attrs, defStyleAttr);
 
         // Need to use getContext() here so that we use the themed context
         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
@@ -272,6 +283,21 @@
             setNavigationContentDescription(navDesc);
         }
 
+        if (a.hasValue(R.styleable.Toolbar_overflowTint)) {
+            setOverflowTintList(a.getColorStateList(R.styleable.Toolbar_overflowTint));
+        }
+        if (a.hasValue(R.styleable.Toolbar_overflowTintMode)) {
+            setOverflowTintMode(DrawableUtils.parseTintMode(
+                    a.getInt(R.styleable.Toolbar_overflowTintMode, -1), null));
+        }
+        if (a.hasValue(R.styleable.Toolbar_navigationTint)) {
+            setNavigationTintList(a.getColorStateList(R.styleable.Toolbar_navigationTint));
+        }
+        if (a.hasValue(R.styleable.Toolbar_navigationTintMode)) {
+            setNavigationTintMode(DrawableUtils.parseTintMode(
+                    a.getInt(R.styleable.Toolbar_navigationTintMode, -1), null));
+        }
+
         // This is read for devices running pre-v16
         mMinHeight = a.getDimensionPixelSize(R.styleable.Toolbar_android_minHeight, 0);
 
@@ -679,7 +705,7 @@
      *
      * @param color The new text color in 0xAARRGGBB format
      */
-    public void setTitleTextColor(int color) {
+    public void setTitleTextColor(@ColorInt int color) {
         mTitleTextColor = color;
         if (mTitleTextView != null) {
             mTitleTextView.setTextColor(color);
@@ -691,7 +717,7 @@
      *
      * @param color The new text color in 0xAARRGGBB format
      */
-    public void setSubtitleTextColor(int color) {
+    public void setSubtitleTextColor(@ColorInt int color) {
         mSubtitleTextColor = color;
         if (mSubtitleTextView != null) {
             mSubtitleTextView.setTextColor(color);
@@ -807,6 +833,83 @@
     }
 
     /**
+     * Applies a tint to the icon drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to {@link #setNavigationIcon(Drawable)} will automatically mutate
+     * the drawable and apply the specified tint and tint mode.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     */
+    public void setNavigationTintList(ColorStateList tint) {
+        if (mNavTintInfo == null) {
+            mNavTintInfo = new TintInfo();
+        }
+        mNavTintInfo.mTintList = tint;
+        mNavTintInfo.mHasTintList = true;
+
+        applyNavigationTint();
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by {@link
+     * #setNavigationTintList(ColorStateList)} to the navigation drawable.
+     * The default mode is {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be {@code null} to clear tint
+     */
+    public void setNavigationTintMode(PorterDuff.Mode tintMode) {
+        if (mNavTintInfo == null) {
+            mNavTintInfo = new TintInfo();
+        }
+        mNavTintInfo.mTintMode = tintMode;
+        mNavTintInfo.mHasTintMode = true;
+
+        applyNavigationTint();
+    }
+
+    /**
+     * Applies a tint to the overflow drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     */
+    public void setOverflowTintList(ColorStateList tint) {
+        if (mMenuView != null) {
+            // If the menu view is available, directly set the tint
+            mMenuView.setOverflowTintList(tint);
+        } else {
+            // Otherwise we will record the value
+            if (mOverflowTintInfo == null) {
+                mOverflowTintInfo = new TintInfo();
+            }
+            mOverflowTintInfo.mTintList = tint;
+            mOverflowTintInfo.mHasTintList = true;
+        }
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by {@link
+     * #setOverflowTintList(ColorStateList)} to the overflow drawable.
+     * The default mode is {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be {@code null} to clear tint
+     */
+    public void setOverflowTintMode(PorterDuff.Mode tintMode) {
+        if (mMenuView != null) {
+            // If the menu view is available, directly set the tint mode
+            mMenuView.setOverflowTintMode(tintMode);
+        } else {
+            // Otherwise we will record the value
+            if (mOverflowTintInfo == null) {
+                mOverflowTintInfo = new TintInfo();
+            }
+            mOverflowTintInfo.mTintMode = tintMode;
+            mOverflowTintInfo.mHasTintMode = true;
+        }
+    }
+
+    /**
      * Return the Menu shown in the toolbar.
      *
      * <p>Applications that wish to populate the toolbar's menu can do so from here. To use
@@ -842,6 +945,17 @@
             lp.gravity = GravityCompat.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
             mMenuView.setLayoutParams(lp);
             addSystemView(mMenuView);
+
+            if (mOverflowTintInfo != null) {
+                // If we have tint info for the overflow, set it on the menu view now
+                if (mOverflowTintInfo.mHasTintList) {
+                    mMenuView.setOverflowTintList(mOverflowTintInfo.mTintList);
+                }
+                if (mOverflowTintInfo.mHasTintMode) {
+                    mMenuView.setOverflowTintMode(mOverflowTintInfo.mTintMode);
+                }
+                mOverflowTintInfo = null;
+            }
         }
     }
 
@@ -991,17 +1105,18 @@
 
     private void ensureNavButtonView() {
         if (mNavButtonView == null) {
-            mNavButtonView = new ImageButton(getContext(), null,
+            mNavButtonView = new TintImageButton(getContext(), null,
                     R.attr.toolbarNavigationButtonStyle);
             final LayoutParams lp = generateDefaultLayoutParams();
             lp.gravity = GravityCompat.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
             mNavButtonView.setLayoutParams(lp);
+            applyNavigationTint();
         }
     }
 
     private void ensureCollapseButtonView() {
         if (mCollapseButtonView == null) {
-            mCollapseButtonView = new ImageButton(getContext(), null,
+            mCollapseButtonView = new TintImageButton(getContext(), null,
                     R.attr.toolbarNavigationButtonStyle);
             mCollapseButtonView.setImageDrawable(mCollapseIcon);
             mCollapseButtonView.setContentDescription(mCollapseDescription);
@@ -1015,6 +1130,7 @@
                     collapseActionView();
                 }
             });
+            applyNavigationTint();
         }
     }
 
@@ -1788,6 +1904,30 @@
         }
     }
 
+    private void applyNavigationTint() {
+        final TintInfo tintInfo = mNavTintInfo;
+        if (tintInfo != null && (tintInfo.mHasTintList || tintInfo.mHasTintMode)) {
+            if (mNavButtonView != null) {
+                if (tintInfo.mHasTintList) {
+                    mNavButtonView.setImageTintList(tintInfo.mTintList);
+                }
+                if (tintInfo.mHasTintMode) {
+                    mNavButtonView.setImageTintMode(tintInfo.mTintMode);
+                }
+            }
+
+            if (mCollapseButtonView != null) {
+                // We will use the same tint for the collapse button
+                if (tintInfo.mHasTintList) {
+                    mCollapseButtonView.setImageTintList(tintInfo.mTintList);
+                }
+                if (tintInfo.mHasTintMode) {
+                    mCollapseButtonView.setImageTintMode(tintInfo.mTintMode);
+                }
+            }
+        }
+    }
+
     /**
      * Interface responsible for receiving menu item click events if the items themselves
      * do not have individual item click listeners.
@@ -1808,7 +1948,7 @@
      *
      * <p>Toolbar.LayoutParams extends ActionBar.LayoutParams for compatibility with existing
      * ActionBar API. See
-     * {@link android.support.v7.app.ActionBarActivity#setSupportActionBar(Toolbar)
+     * {@link android.support.v7.app.AppCompatActivity#setSupportActionBar(Toolbar)
      * ActionBarActivity.setActionBar}
      * for more info on how to use a Toolbar as your Activity's ActionBar.</p>
      */
@@ -2022,18 +2162,4 @@
         public void onRestoreInstanceState(Parcelable state) {
         }
     }
-
-    /**
-     * Allows us to emulate the {@code android:theme} attribute for devices before L.
-     */
-    private static Context themifyContext(Context context, AttributeSet attrs, int defStyleAttr) {
-        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Toolbar,
-                defStyleAttr, 0);
-        final int themeId = a.getResourceId(R.styleable.Toolbar_theme, 0);
-        if (themeId != 0) {
-            context = new ContextThemeWrapper(context, themeId);
-        }
-        a.recycle();
-        return context;
-    }
 }
diff --git a/v7/appcompat/src/android/support/v7/widget/WindowCallbackWrapper.java b/v7/appcompat/src/android/support/v7/widget/WindowCallbackWrapper.java
deleted file mode 100644
index 867a9ef..0000000
--- a/v7/appcompat/src/android/support/v7/widget/WindowCallbackWrapper.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2014 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.v7.widget;
-
-import android.support.v7.internal.app.WindowCallback;
-import android.support.v7.view.ActionMode;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-
-/**
- * A simple decorator stub for WindowCallback that passes through any calls
- * to the wrapped instance as a base implementation. Call super.foo() to call into
- * the wrapped callback for any subclasses.
- *
- * @hide for internal use
- */
-public class WindowCallbackWrapper implements WindowCallback {
-    private WindowCallback mWrapped;
-
-    public WindowCallbackWrapper(WindowCallback wrapped) {
-        if (wrapped == null) {
-            throw new IllegalArgumentException("Window callback may not be null");
-        }
-        mWrapped = wrapped;
-    }
-
-    @Override
-    public boolean onMenuItemSelected(int featureId, MenuItem menuItem) {
-        return mWrapped.onMenuItemSelected(featureId, menuItem);
-    }
-
-    @Override
-    public boolean onCreatePanelMenu(int featureId, Menu menu) {
-        return mWrapped.onCreatePanelMenu(featureId, menu);
-    }
-
-    @Override
-    public boolean onPreparePanel(int featureId, View menuView, Menu menu) {
-        return mWrapped.onPreparePanel(featureId, menuView, menu);
-    }
-
-    @Override
-    public void onPanelClosed(int featureId, Menu menu) {
-        mWrapped.onPanelClosed(featureId, menu);
-    }
-
-    @Override
-    public boolean onMenuOpened(int featureId, Menu menu) {
-        return mWrapped.onMenuOpened(featureId, menu);
-    }
-
-    @Override
-    public ActionMode startActionMode(ActionMode.Callback callback) {
-        return mWrapped.startActionMode(callback);
-    }
-
-    @Override
-    public View onCreatePanelView(int featureId) {
-        return mWrapped.onCreatePanelView(featureId);
-    }
-}
diff --git a/v7/gridlayout/build.gradle b/v7/gridlayout/build.gradle
index bb586d1..916e7b9 100644
--- a/v7/gridlayout/build.gradle
+++ b/v7/gridlayout/build.gradle
@@ -21,6 +21,8 @@
         // This is a *reset* so it replaces the default paths
         androidTest.setRoot('tests')
         androidTest.java.srcDir 'tests/src'
+        androidTest.res.srcDir 'tests/res'
+        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
     }
 
     lintOptions {
diff --git a/v7/gridlayout/res/values/dimens.xml b/v7/gridlayout/res/values/dimens.xml
index 963ecb5..189f409 100644
--- a/v7/gridlayout/res/values/dimens.xml
+++ b/v7/gridlayout/res/values/dimens.xml
@@ -2,6 +2,6 @@
 <resources>
 
     <!-- The default gap between components in a layout. -->
-    <dimen name="default_gap">16dip</dimen>
+    <dimen name="default_gap">8dip</dimen>
 
 </resources>
\ No newline at end of file
diff --git a/v7/gridlayout/src/android/support/v7/widget/GridLayout.java b/v7/gridlayout/src/android/support/v7/widget/GridLayout.java
index 5369f86..cee59eb 100644
--- a/v7/gridlayout/src/android/support/v7/widget/GridLayout.java
+++ b/v7/gridlayout/src/android/support/v7/widget/GridLayout.java
@@ -139,9 +139,12 @@
  * view was alone in a column, that column would itself collapse to zero width if and only if
  * no gravity was defined on the view. If gravity was defined, then the gone-marked
  * view has no effect on the layout and the container should be laid out as if the view
- * had never been added to it.
+ * had never been added to it. GONE views are taken to have zero weight during excess space
+ * distribution.
+ * <p>
  * These statements apply equally to rows as well as columns, and to groups of rows or columns.
  *
+ *
  * <p>
  * See {@link GridLayout.LayoutParams} for a full description of the
  * layout parameters used by GridLayout.
@@ -901,12 +904,10 @@
             LayoutParams lp = getLayoutParams(c);
             if (firstPass) {
                 measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
-                mHorizontalAxis.recordOriginalMeasurement(i);
-                mVerticalAxis.recordOriginalMeasurement(i);
             } else {
                 boolean horizontal = (mOrientation == HORIZONTAL);
                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                if (spec.alignment == FILL) {
+                if (spec.getAbsoluteAlignment(horizontal) == FILL) {
                     Interval span = spec.span;
                     Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
                     int[] locations = axis.getLocations();
@@ -982,11 +983,6 @@
         invalidateStructure();
     }
 
-    final Alignment getAlignment(Alignment alignment, boolean horizontal) {
-        return (alignment != UNDEFINED_ALIGNMENT) ? alignment :
-                (horizontal ? START : BASELINE);
-    }
-
     // Layout container
 
     /**
@@ -1041,8 +1037,8 @@
             int pWidth = getMeasurement(c, true);
             int pHeight = getMeasurement(c, false);
 
-            Alignment hAlign = getAlignment(columnSpec.alignment, true);
-            Alignment vAlign = getAlignment(rowSpec.alignment, false);
+            Alignment hAlign = columnSpec.getAbsoluteAlignment(true);
+            Alignment vAlign = rowSpec.getAbsoluteAlignment(false);
 
             Bounds boundsX = mHorizontalAxis.getGroupBounds().getValue(i);
             Bounds boundsY = mVerticalAxis.getGroupBounds().getValue(i);
@@ -1120,7 +1116,6 @@
 
         public boolean hasWeights;
         public boolean hasWeightsValid = false;
-        public int[] originalMeasurements;
         public int[] deltas;
 
         boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
@@ -1183,7 +1178,7 @@
                 // we must include views that are GONE here, see introductory javadoc
                 LayoutParams lp = getLayoutParams(c);
                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                Bounds bounds = getAlignment(spec.alignment, horizontal).getBounds();
+                Bounds bounds = spec.getAbsoluteAlignment(horizontal).getBounds();
                 assoc.put(spec, bounds);
             }
             return assoc.pack();
@@ -1199,9 +1194,8 @@
                 // we must include views that are GONE here, see introductory javadoc
                 LayoutParams lp = getLayoutParams(c);
                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                int size = (spec.weight == 0) ?
-                        getMeasurementIncludingMargin(c, horizontal) :
-                        getOriginalMeasurements()[i] + getDeltas()[i];
+                int size = getMeasurementIncludingMargin(c, horizontal) +
+                                ((spec.weight == 0) ? 0 : getDeltas()[i]);
                 groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size);
             }
         }
@@ -1589,7 +1583,11 @@
 
         private boolean computeHasWeights() {
             for (int i = 0, N = getChildCount(); i < N; i++) {
-                LayoutParams lp = getLayoutParams(getChildAt(i));
+                final View child = getChildAt(i);
+                if (child.getVisibility() == View.GONE) {
+                    continue;
+                }
+                LayoutParams lp = getLayoutParams(child);
                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
                 if (spec.weight != 0) {
                     return true;
@@ -1606,19 +1604,6 @@
             return hasWeights;
         }
 
-        public int[] getOriginalMeasurements() {
-            if (originalMeasurements == null) {
-                originalMeasurements = new int[getChildCount()];
-            }
-            return originalMeasurements;
-        }
-
-        private void recordOriginalMeasurement(int i) {
-            if (hasWeights()) {
-                getOriginalMeasurements()[i] = getMeasurementIncludingMargin(getChildAt(i), horizontal);
-            }
-        }
-
         public int[] getDeltas() {
             if (deltas == null) {
                 deltas = new int[getChildCount()];
@@ -1629,7 +1614,10 @@
         private void shareOutDelta(int totalDelta, float totalWeight) {
             Arrays.fill(deltas, 0);
             for (int i = 0, N = getChildCount(); i < N; i++) {
-                View c = getChildAt(i);
+                final View c = getChildAt(i);
+                if (c.getVisibility() == View.GONE) {
+                    continue;
+                }
                 LayoutParams lp = getLayoutParams(c);
                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
                 float weight = spec.weight;
@@ -1682,6 +1670,9 @@
             float totalWeight = 0f;
             for (int i = 0, N = getChildCount(); i < N; i++) {
                 View c = getChildAt(i);
+                if (c.getVisibility() == View.GONE) {
+                    continue;
+                }
                 LayoutParams lp = getLayoutParams(c);
                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
                 totalWeight += spec.weight;
@@ -1777,7 +1768,6 @@
 
             locations = null;
 
-            originalMeasurements = null;
             deltas = null;
             hasWeightsValid = false;
 
@@ -2289,7 +2279,7 @@
         protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) {
             this.flexibility &= spec.getFlexibility();
             boolean horizontal = axis.horizontal;
-            Alignment alignment = gl.getAlignment(spec.alignment, horizontal);
+            Alignment alignment = spec.getAbsoluteAlignment(horizontal);
             // todo test this works correctly when the returned value is UNDEFINED
             int before = alignment.getAlignmentValue(c, size, ViewGroupCompat.getLayoutMode(gl));
             include(before, size - before);
@@ -2440,6 +2430,16 @@
             this(startDefined, new Interval(start, start + size), alignment, weight);
         }
 
+        public Alignment getAbsoluteAlignment(boolean horizontal) {
+            if (alignment != UNDEFINED_ALIGNMENT) {
+                return alignment;
+            }
+            if (weight == 0f) {
+                return horizontal ? START : BASELINE;
+            }
+            return FILL;
+        }
+
         final Spec copyWriteSpan(Interval span) {
             return new Spec(startDefined, span, alignment, weight);
         }
@@ -2666,6 +2666,13 @@
         Bounds getBounds() {
             return new Bounds();
         }
+
+        abstract String getDebugString();
+
+        @Override
+        public String toString() {
+            return "Alignment:" + getDebugString();
+        }
     }
 
     static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
@@ -2678,6 +2685,11 @@
         public int getAlignmentValue(View view, int viewSize, int mode) {
             return UNDEFINED;
         }
+
+        @Override
+        String getDebugString() {
+            return "UNDEFINED";
+        }
     };
 
     /**
@@ -2694,6 +2706,11 @@
         public int getAlignmentValue(View view, int viewSize, int mode) {
             return 0;
         }
+
+        @Override
+        String getDebugString() {
+            return "LEADING";
+        }
     };
 
     /**
@@ -2710,6 +2727,11 @@
         public int getAlignmentValue(View view, int viewSize, int mode) {
             return viewSize;
         }
+
+        @Override
+        String getDebugString() {
+            return "TRAILING";
+        }
     };
 
     /**
@@ -2751,6 +2773,11 @@
                         ViewCompat.LAYOUT_DIRECTION_RTL;
                 return (!isLayoutRtl ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
             }
+
+            @Override
+            String getDebugString() {
+                return "SWITCHING[L:" + ltr.getDebugString() + ", R:" + rtl.getDebugString() + "]";
+            }
         };
     }
 
@@ -2781,6 +2808,11 @@
         public int getAlignmentValue(View view, int viewSize, int mode) {
             return viewSize >> 1;
         }
+
+        @Override
+        String getDebugString() {
+            return "CENTER";
+        }
     };
 
     /**
@@ -2839,6 +2871,11 @@
                 }
             };
         }
+
+        @Override
+        String getDebugString() {
+            return "BASELINE";
+        }
     };
 
     /**
@@ -2861,6 +2898,11 @@
         public int getSizeInCell(View view, int viewSize, int cellSize) {
             return cellSize;
         }
+
+        @Override
+        String getDebugString() {
+            return "FILL";
+        }
     };
 
     static boolean canStretch(int flexibility) {
diff --git a/v7/gridlayout/src/android/support/v7/widget/Space.java b/v7/gridlayout/src/android/support/v7/widget/Space.java
index 47071b9..e84bdfb 100644
--- a/v7/gridlayout/src/android/support/v7/widget/Space.java
+++ b/v7/gridlayout/src/android/support/v7/widget/Space.java
@@ -17,78 +17,24 @@
 package android.support.v7.widget;
 
 import android.content.Context;
-import android.graphics.Canvas;
 import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
 
 /**
- * Space is a lightweight View subclass that may be used to create gaps between components
- * in general purpose layouts.
+ * @deprecated Use {@link android.support.v4.widget.Space} instead.
  */
-public final class Space extends View {
-    /**
-     * {@inheritDoc}
-     */
+@Deprecated
+public final class Space extends android.support.v4.widget.Space {
+
+    public Space(Context context) {
+        super(context);
+    }
+
+    public Space(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
     public Space(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        if (getVisibility() == VISIBLE) {
-            setVisibility(INVISIBLE);
-        }
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public Space(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Space(Context context) {
-        //noinspection NullableProblems
-        this(context, null);
-    }
-
-    /**
-     * Draw nothing.
-     *
-     * @param canvas an unused parameter.
-     */
-    @Override
-    public void draw(Canvas canvas) {
-    }
-
-    /**
-     * Compare to: {@link View#getDefaultSize(int, int)}
-     * If mode is AT_MOST, return the child size instead of the parent size
-     * (unless it is too big).
-     */
-    private static int getDefaultSize2(int size, int measureSpec) {
-        int result = size;
-        int specMode = MeasureSpec.getMode(measureSpec);
-        int specSize = MeasureSpec.getSize(measureSpec);
-
-        switch (specMode) {
-            case MeasureSpec.UNSPECIFIED:
-                result = size;
-                break;
-            case MeasureSpec.AT_MOST:
-                result = Math.min(size, specSize);
-                break;
-            case MeasureSpec.EXACTLY:
-                result = specSize;
-                break;
-        }
-        return result;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(
-                getDefaultSize2(getSuggestedMinimumWidth(), widthMeasureSpec),
-                getDefaultSize2(getSuggestedMinimumHeight(), heightMeasureSpec));
-    }
 }
diff --git a/v7/gridlayout/tests/Android.mk b/v7/gridlayout/tests/Android.mk
new file mode 100644
index 0000000..70017f0
--- /dev/null
+++ b/v7/gridlayout/tests/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := \
+    $(LOCAL_PATH)/res \
+    $(LOCAL_PATH)/../res
+
+LOCAL_STATIC_JAVA_LIBRARIES  := \
+        android-support-v7-gridlayout \
+        android-support-v4
+
+LOCAL_PACKAGE_NAME := GridLayoutTests
+LOCAL_AAPT_FLAGS := \
+        --auto-add-overlay \
+        --extra-packages android.support.v7.gridlayout
+
+include $(BUILD_PACKAGE)
+
diff --git a/v7/gridlayout/tests/AndroidManifest.xml b/v7/gridlayout/tests/AndroidManifest.xml
new file mode 100644
index 0000000..c55a49a
--- /dev/null
+++ b/v7/gridlayout/tests/AndroidManifest.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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.v7.gridlayout.test">
+    <uses-sdk android:minSdkVersion="7"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.support.v7.widget.test.GridLayoutTestActivity"/>
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="android.support.v7.gridlayout.test"
+                     android:label="GridLayout Tests" />
+
+</manifest>
diff --git a/v7/gridlayout/tests/res/layout/fill_horizontal_test.xml b/v7/gridlayout/tests/res/layout/fill_horizontal_test.xml
new file mode 100644
index 0000000..301b1a1
--- /dev/null
+++ b/v7/gridlayout/tests/res/layout/fill_horizontal_test.xml
@@ -0,0 +1,44 @@
+<!--
+  ~ 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.v7.widget.GridLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/gridView"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="10dp"
+        android:background="@android:color/white"
+        app:columnCount="2"
+        app:rowCount="1"
+        app:orientation="horizontal">
+
+    <TextView
+            android:id="@+id/leftView"
+            android:layout_width="0dp"
+            android:background="@color/red"
+            app:layout_columnWeight="2"
+            android:maxLines="3"
+            android:text="column 1, with weight of 2"/>
+
+    <TextView
+            android:id="@+id/rightView"
+            android:layout_width="0dp"
+            app:layout_columnWeight="1"
+            android:minLines="3"
+            android:background="@color/blue"
+            android:text="column 2, with weight of 1"/>
+</android.support.v7.widget.GridLayout>
\ No newline at end of file
diff --git a/v7/gridlayout/tests/res/layout/height_wrap_content_test.xml b/v7/gridlayout/tests/res/layout/height_wrap_content_test.xml
new file mode 100644
index 0000000..308539a
--- /dev/null
+++ b/v7/gridlayout/tests/res/layout/height_wrap_content_test.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ 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.v7.widget.GridLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/gridView"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="10dp"
+        android:background="@android:color/white"
+        app:columnCount="2"
+        app:rowCount="1"
+        app:orientation="horizontal">
+    <TextView
+            android:id="@+id/leftView"
+            android:background="@color/red"
+            android:layout_width="0dp"
+            app:layout_gravity="fill_horizontal"
+            app:layout_columnWeight="2"
+            android:text="column 1, with weight of 2"/>
+    <TextView
+            android:id="@+id/rightView"
+            android:background="@color/red"
+            android:layout_width="0dp"
+            app:layout_columnWeight="1"
+            app:layout_gravity="fill_horizontal"
+            android:text="column 2, with weight of 1"/>
+</android.support.v7.widget.GridLayout>
diff --git a/v7/gridlayout/tests/res/layout/make_view_gone_test.xml b/v7/gridlayout/tests/res/layout/make_view_gone_test.xml
new file mode 100644
index 0000000..75d55ef
--- /dev/null
+++ b/v7/gridlayout/tests/res/layout/make_view_gone_test.xml
@@ -0,0 +1,39 @@
+<!--
+  ~ 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.v7.widget.GridLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/gridView"
+        android:background="@android:color/white"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+    <TextView
+            android:id="@+id/leftView"
+            android:background="@color/red"
+            android:layout_width="0dp"
+            app:layout_columnWeight="2"
+            app:layout_gravity="fill"
+            android:text="column 1, weight 2" />
+    <TextView
+            android:id="@+id/rightView"
+            android:background="@color/blue"
+            android:layout_width="0dp"
+            app:layout_gravity="fill"
+            app:layout_columnWeight="1"
+            android:text="column 2, weight 1" />
+</android.support.v7.widget.GridLayout>
diff --git a/v7/gridlayout/tests/res/layout/use_default_margin_test.xml b/v7/gridlayout/tests/res/layout/use_default_margin_test.xml
new file mode 100644
index 0000000..fa3940f
--- /dev/null
+++ b/v7/gridlayout/tests/res/layout/use_default_margin_test.xml
@@ -0,0 +1,48 @@
+<?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.v7.widget.GridLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/gridView"
+        android:background="@android:color/white"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="10dp"
+        app:useDefaultMargins="true"
+        app:columnCount="2"
+        app:rowCount="1"
+        app:orientation="horizontal">
+
+    <TextView
+            android:id="@+id/leftView"
+            android:layout_width="0dp"
+            app:layout_gravity="fill_horizontal"
+            android:background="@color/red"
+            app:layout_columnWeight="2"
+            android:maxLines="3"
+            android:text="column 1, with weight of 2"/>
+
+    <TextView
+            android:id="@+id/rightView"
+            android:layout_width="0dp"
+            app:layout_gravity="fill_horizontal"
+            android:background="@color/blue"
+            app:layout_columnWeight="1"
+            android:minLines="3"
+            android:text="column 2, with weight of 1"/>
+</android.support.v7.widget.GridLayout>
diff --git a/v7/gridlayout/tests/res/values/colors.xml b/v7/gridlayout/tests/res/values/colors.xml
new file mode 100644
index 0000000..9bf059c
--- /dev/null
+++ b/v7/gridlayout/tests/res/values/colors.xml
@@ -0,0 +1,22 @@
+<?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>
+    <color name="blue">#0000FF</color>
+    <color name="red">#FF0000</color>
+    <color name="green">#00FF00</color>
+</resources>
\ No newline at end of file
diff --git a/v7/gridlayout/tests/src/android/support/v7/widget/test/GridLayoutTest.java b/v7/gridlayout/tests/src/android/support/v7/widget/test/GridLayoutTest.java
new file mode 100644
index 0000000..701a674
--- /dev/null
+++ b/v7/gridlayout/tests/src/android/support/v7/widget/test/GridLayoutTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.v7.widget.test;
+
+import android.app.Activity;
+import android.os.Debug;
+import android.support.v7.widget.GridLayout;
+import android.test.ActivityInstrumentationTestCase2;
+import android.support.v7.gridlayout.R;
+import android.test.UiThreadTest;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+public class GridLayoutTest extends ActivityInstrumentationTestCase2 {
+
+    public GridLayoutTest() {
+        super("android.support.v7.widget.test", GridLayoutTestActivity.class);
+    }
+
+    private void setContentView(final int layoutId) throws Throwable {
+        final Activity activity = getActivity();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.setContentView(layoutId);
+            }
+        });
+    }
+
+    public void testUseDefaultMargin() throws Throwable {
+        setContentView(R.layout.use_default_margin_test);
+        getInstrumentation().waitForIdleSync();
+        int left = getActivity().findViewById(R.id.leftView).getWidth();
+        int right = getActivity().findViewById(R.id.rightView).getWidth();
+        int total = getActivity().findViewById(R.id.gridView).getWidth();
+        assertTrue("left item should get some width", left > 0);
+        assertTrue("right item should get some width", right > 0);
+        assertTrue("test sanity", total > 0);
+        assertTrue("left view should be almost two times right view " + left + " vs " + right,
+                Math.abs(right * 2 - left) < 2);
+    }
+
+    public void testImplicitFillHorizontal() throws Throwable {
+        setContentView(R.layout.fill_horizontal_test);
+        getInstrumentation().waitForIdleSync();
+        int left = getActivity().findViewById(R.id.leftView).getWidth();
+        int right = getActivity().findViewById(R.id.rightView).getWidth();
+        int total = getActivity().findViewById(R.id.gridView).getWidth();
+        assertTrue("left item should get some width", left > 0);
+        assertTrue("right item should get some width", right > 0);
+        assertTrue("test sanity", total > 0);
+        assertTrue("left view should be almost two times right view " + left + " vs " + right,
+                Math.abs(right * 2 - left) < 2);
+    }
+
+    public void testMakeViewGone() throws Throwable {
+        setContentView(R.layout.make_view_gone_test);
+        getInstrumentation().waitForIdleSync();
+        int left = getActivity().findViewById(R.id.leftView).getWidth();
+        final int right = getActivity().findViewById(R.id.rightView).getWidth();
+        int total = getActivity().findViewById(R.id.gridView).getWidth();
+        assertTrue("left item should get some width", left > 0);
+        assertTrue("right item should get some width", right > 0);
+        assertTrue("test sanity", total > 0);
+        // set second view to gone
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final View rightView = getActivity().findViewById(R.id.rightView);
+                GridLayout.LayoutParams lp = (GridLayout.LayoutParams) rightView.getLayoutParams();
+                lp.setGravity(Gravity.NO_GRAVITY);
+                rightView.setVisibility(View.GONE);
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+        left = getActivity().findViewById(R.id.leftView).getWidth();
+        assertEquals(total, left);
+    }
+    public void testWrapContentInOtherDirection() throws Throwable {
+        setContentView(R.layout.height_wrap_content_test);
+        getInstrumentation().waitForIdleSync();
+        int left = getActivity().findViewById(R.id.leftView).getHeight();
+        final int right = getActivity().findViewById(R.id.rightView).getHeight();
+        final View gridView = getActivity().findViewById(R.id.gridView);
+        int total = gridView.getHeight();
+        assertTrue("test sanity", left > 0);
+        assertTrue("test sanity", right > 0);
+        assertTrue("test sanity", total > 0);
+        assertTrue("right should be taller than left", right > left);
+        assertTrue("total height should be smaller than what it could be",
+                total < ((ViewGroup)gridView.getParent()).getHeight());
+
+    }
+}
diff --git a/v7/gridlayout/tests/src/android/support/v7/widget/test/GridLayoutTestActivity.java b/v7/gridlayout/tests/src/android/support/v7/widget/test/GridLayoutTestActivity.java
new file mode 100644
index 0000000..b83152f
--- /dev/null
+++ b/v7/gridlayout/tests/src/android/support/v7/widget/test/GridLayoutTestActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.v7.widget.test;
+
+import android.app.Activity;
+
+public class GridLayoutTestActivity extends Activity {
+}
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_cast_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_cast_dark.png
index 4a3396a..da91591 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_cast_dark.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_cast_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_cast_disabled_light.png b/v7/mediarouter/res/drawable-hdpi/ic_cast_disabled_light.png
index 440751e..74b2d16 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_cast_disabled_light.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_cast_disabled_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_cast_light.png b/v7/mediarouter/res/drawable-hdpi/ic_cast_light.png
index 20740bc..a74163a 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_cast_light.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_cast_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_cast_off_light.png b/v7/mediarouter/res/drawable-hdpi/ic_cast_off_light.png
index c6f9002..6e02527 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_cast_off_light.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_cast_off_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_cast_on_0_light.png b/v7/mediarouter/res/drawable-hdpi/ic_cast_on_0_light.png
index 9d7a4e4..26c6847 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_cast_on_0_light.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_cast_on_0_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_cast_on_1_light.png b/v7/mediarouter/res/drawable-hdpi/ic_cast_on_1_light.png
index 679bb98..2fea78f 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_cast_on_1_light.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_cast_on_1_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_cast_on_2_light.png b/v7/mediarouter/res/drawable-hdpi/ic_cast_on_2_light.png
index 2910903..2071e8f 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_cast_on_2_light.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_cast_on_2_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_cast_on_light.png b/v7/mediarouter/res/drawable-hdpi/ic_cast_on_light.png
index b7e6db8..54a277e 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_cast_on_light.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_cast_on_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_media_pause.png b/v7/mediarouter/res/drawable-hdpi/ic_media_pause.png
index 1d465a4..fe26360 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_media_pause.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_media_pause.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_media_play.png b/v7/mediarouter/res/drawable-hdpi/ic_media_play.png
index 2746d17..8afcd7d 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_media_play.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_media_play.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_media_route_disabled_mono_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_media_route_disabled_mono_dark.png
index 07603b5..5b103e9 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_media_route_disabled_mono_dark.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_media_route_disabled_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_media_route_off_mono_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_media_route_off_mono_dark.png
index 27e2d4a..0090701 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_media_route_off_mono_dark.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_media_route_off_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_0_mono_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_0_mono_dark.png
index 3afde73..41eb0b7 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_0_mono_dark.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_0_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_1_mono_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_1_mono_dark.png
index f4ce1d5..49f14cb 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_1_mono_dark.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_1_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_2_mono_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_2_mono_dark.png
index 8dcb6e9..5dd6aea 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_2_mono_dark.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_2_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_mono_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_mono_dark.png
index 5584a13..0d5ac5c 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_mono_dark.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_media_route_on_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_pause_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_pause_dark.png
index 337cf0f..81c32fe 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_pause_dark.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_pause_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_pause_light.png b/v7/mediarouter/res/drawable-hdpi/ic_pause_light.png
index ec70ee1..864d8d2 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_pause_light.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_pause_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_play_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_play_dark.png
index b36cf49..568ae86 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_play_dark.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_play_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_play_light.png b/v7/mediarouter/res/drawable-hdpi/ic_play_light.png
index 2046149..e278033 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_play_light.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_play_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_setting_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_setting_dark.png
index 66efe42..3248ad1 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_setting_dark.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_setting_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_setting_light.png b/v7/mediarouter/res/drawable-hdpi/ic_setting_light.png
index a1f4101..c39fc1f 100644
--- a/v7/mediarouter/res/drawable-hdpi/ic_setting_light.png
+++ b/v7/mediarouter/res/drawable-hdpi/ic_setting_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/mr_ic_audio_vol.png b/v7/mediarouter/res/drawable-hdpi/mr_ic_audio_vol.png
index 2277170..17565c5 100644
--- a/v7/mediarouter/res/drawable-hdpi/mr_ic_audio_vol.png
+++ b/v7/mediarouter/res/drawable-hdpi/mr_ic_audio_vol.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_cast_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_cast_dark.png
index 9bee201..1121562 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_cast_dark.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_cast_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_cast_disabled_light.png b/v7/mediarouter/res/drawable-mdpi/ic_cast_disabled_light.png
index b60a666..ebb616c 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_cast_disabled_light.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_cast_disabled_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_cast_light.png b/v7/mediarouter/res/drawable-mdpi/ic_cast_light.png
index 6b7885c..7e5a3b6 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_cast_light.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_cast_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_cast_off_light.png b/v7/mediarouter/res/drawable-mdpi/ic_cast_off_light.png
index 0de509a..d6114dc 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_cast_off_light.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_cast_off_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_cast_on_0_light.png b/v7/mediarouter/res/drawable-mdpi/ic_cast_on_0_light.png
index 6d94fee..76da308 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_cast_on_0_light.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_cast_on_0_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_cast_on_1_light.png b/v7/mediarouter/res/drawable-mdpi/ic_cast_on_1_light.png
index af23356..25d3809 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_cast_on_1_light.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_cast_on_1_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_cast_on_2_light.png b/v7/mediarouter/res/drawable-mdpi/ic_cast_on_2_light.png
index 8b238d4..3d63b0d 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_cast_on_2_light.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_cast_on_2_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_cast_on_light.png b/v7/mediarouter/res/drawable-mdpi/ic_cast_on_light.png
index 13c53e7..20e7619 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_cast_on_light.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_cast_on_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_media_pause.png b/v7/mediarouter/res/drawable-mdpi/ic_media_pause.png
index 3e6b2a1..7149f56 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_media_pause.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_media_pause.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_media_play.png b/v7/mediarouter/res/drawable-mdpi/ic_media_play.png
index 7966bbc..a1cb5df 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_media_play.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_media_play.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_media_route_disabled_mono_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_media_route_disabled_mono_dark.png
index a2c68c5..01a0c50 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_media_route_disabled_mono_dark.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_media_route_disabled_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_media_route_off_mono_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_media_route_off_mono_dark.png
index 69287b8..8ec0cb1 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_media_route_off_mono_dark.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_media_route_off_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_0_mono_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_0_mono_dark.png
index cf035c0..7cb6dcc 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_0_mono_dark.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_0_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_1_mono_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_1_mono_dark.png
index 247b72f..38ee9f6 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_1_mono_dark.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_1_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_2_mono_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_2_mono_dark.png
index eaed2f7..91f1b4b 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_2_mono_dark.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_2_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_mono_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_mono_dark.png
index 77bd8f5..6838f80 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_mono_dark.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_media_route_on_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_pause_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_pause_dark.png
index 0842337..c4218a4 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_pause_dark.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_pause_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_pause_light.png b/v7/mediarouter/res/drawable-mdpi/ic_pause_light.png
index affca8b..ab26a30 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_pause_light.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_pause_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_play_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_play_dark.png
index 33f08ae..4446faa 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_play_dark.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_play_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_play_light.png b/v7/mediarouter/res/drawable-mdpi/ic_play_light.png
index 4360c45..55b8c5e 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_play_light.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_play_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_setting_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_setting_dark.png
index 28fbc90..1f0ba42 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_setting_dark.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_setting_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_setting_light.png b/v7/mediarouter/res/drawable-mdpi/ic_setting_light.png
index 5625eed..3744fe4 100644
--- a/v7/mediarouter/res/drawable-mdpi/ic_setting_light.png
+++ b/v7/mediarouter/res/drawable-mdpi/ic_setting_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/mr_ic_audio_vol.png b/v7/mediarouter/res/drawable-mdpi/mr_ic_audio_vol.png
index 4f8df78..36f079d 100644
--- a/v7/mediarouter/res/drawable-mdpi/mr_ic_audio_vol.png
+++ b/v7/mediarouter/res/drawable-mdpi/mr_ic_audio_vol.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_cast_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_cast_dark.png
index c3649df..e9a2a6f 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_cast_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_cast_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_cast_disabled_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_cast_disabled_light.png
index fcb2139..88978ff 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_cast_disabled_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_cast_disabled_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_cast_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_cast_light.png
index ed30868..30f413d 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_cast_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_cast_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_cast_off_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_cast_off_light.png
index 29f3779..4479d12 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_cast_off_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_cast_off_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_0_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_0_light.png
index e6c15a8..9f9b6d2 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_0_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_0_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_1_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_1_light.png
index b46e60e..441b943 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_1_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_1_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_2_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_2_light.png
index 820f3a8..d8e59e6 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_2_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_2_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_light.png
index aff2845..e20a895 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_cast_on_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_media_pause.png b/v7/mediarouter/res/drawable-xhdpi/ic_media_pause.png
index 6bd3d48..90b6543 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_media_pause.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_media_pause.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_media_play.png b/v7/mediarouter/res/drawable-xhdpi/ic_media_play.png
index ccfef18..f615361 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_media_play.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_media_play.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_disabled_mono_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_disabled_mono_dark.png
index 14652ef..8882965 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_disabled_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_disabled_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_off_mono_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_off_mono_dark.png
index 842dfd3..3f8b212 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_off_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_off_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_0_mono_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_0_mono_dark.png
index 036cb13..ab1b6ea 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_0_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_0_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_1_mono_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_1_mono_dark.png
index dcf61af..daa1310 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_1_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_1_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_2_mono_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_2_mono_dark.png
index 16ce334..7143236 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_2_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_2_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_mono_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_mono_dark.png
index 0b83548..6cfe6fb 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_media_route_on_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_pause_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_pause_dark.png
index 67abd61..bd7ec0f 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_pause_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_pause_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_pause_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_pause_light.png
index 007b3ce..e79d7d7 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_pause_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_pause_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_play_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_play_dark.png
index 6584e28..9a5d45f 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_play_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_play_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_play_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_play_light.png
index 0975acf..acaeca6 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_play_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_play_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_setting_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_setting_dark.png
index 8622345..8db5dbb 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_setting_dark.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_setting_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_setting_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_setting_light.png
index b142237..bfc30ef 100644
--- a/v7/mediarouter/res/drawable-xhdpi/ic_setting_light.png
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_setting_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/mr_ic_audio_vol.png b/v7/mediarouter/res/drawable-xhdpi/mr_ic_audio_vol.png
index ea6bd43..e0dff4c 100644
--- a/v7/mediarouter/res/drawable-xhdpi/mr_ic_audio_vol.png
+++ b/v7/mediarouter/res/drawable-xhdpi/mr_ic_audio_vol.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_dark.png
index 4f2e24c..616bd2e 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_disabled_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_disabled_light.png
index 6b74a0f..ad30027 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_disabled_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_disabled_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_light.png
index 444078d..d30d4cf 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_off_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_off_light.png
index e6099b5..9fd15ba 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_off_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_off_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_0_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_0_light.png
index bd7bf92..95ace86 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_0_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_0_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_1_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_1_light.png
index 63e108f..ba85bea 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_1_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_1_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_2_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_2_light.png
index f578d88..8c448fa 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_2_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_2_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_light.png
index 904c2b6..7e93e4d 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_cast_on_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_media_pause.png b/v7/mediarouter/res/drawable-xxhdpi/ic_media_pause.png
index 9a36b17..6391830 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_media_pause.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_media_pause.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_media_play.png b/v7/mediarouter/res/drawable-xxhdpi/ic_media_play.png
index 41f76bb..3b2d712 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_media_play.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_media_play.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_disabled_mono_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_disabled_mono_dark.png
index 95f639a..185ba25 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_disabled_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_disabled_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_off_mono_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_off_mono_dark.png
index 8164bde..3fa0ee6 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_off_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_off_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_0_mono_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_0_mono_dark.png
index 0663290..d21b07b 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_0_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_0_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_1_mono_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_1_mono_dark.png
index c9efc4b..063709e 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_1_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_1_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_2_mono_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_2_mono_dark.png
index 398eb39..9f928ed 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_2_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_2_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_mono_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_mono_dark.png
index 18d9150..4355e79 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_mono_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_media_route_on_mono_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_pause_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_pause_dark.png
index 7077089..2a96557 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_pause_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_pause_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_pause_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_pause_light.png
index 24305f1..bf2560f 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_pause_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_pause_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_play_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_play_dark.png
index f65aa13..1ec1995 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_play_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_play_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_play_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_play_light.png
index 00af328..a5bd8df 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_play_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_play_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_setting_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_setting_dark.png
index 7fef5f5..1d58233 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_setting_dark.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_setting_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_setting_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_setting_light.png
index b5085b1..43c9b99 100644
--- a/v7/mediarouter/res/drawable-xxhdpi/ic_setting_light.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_setting_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/mr_ic_audio_vol.png b/v7/mediarouter/res/drawable-xxhdpi/mr_ic_audio_vol.png
index 15b6311..3dd7a68 100755
--- a/v7/mediarouter/res/drawable-xxhdpi/mr_ic_audio_vol.png
+++ b/v7/mediarouter/res/drawable-xxhdpi/mr_ic_audio_vol.png
Binary files differ
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java
index 3b14e2b..afbdc72 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java
@@ -47,7 +47,7 @@
  * <h3>Prerequisites</h3>
  * <p>
  * To use the media route action provider, the activity must be a subclass of
- * {@link ActionBarActivity} from the <code>android.support.v7.appcompat</code>
+ * {@link AppCompatActivity} from the <code>android.support.v7.appcompat</code>
  * support library.  Refer to support library documentation for details.
  * </p>
  *
diff --git a/v7/palette/Android.mk b/v7/palette/Android.mk
index 877e518..f7694cb 100644
--- a/v7/palette/Android.mk
+++ b/v7/palette/Android.mk
@@ -18,6 +18,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v7-palette
 LOCAL_SDK_VERSION := 7
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src/main)
+LOCAL_MANIFEST_FILE := $(LOCAL_PATH)/src/main/AndroidManifest.xml
 LOCAL_JAVA_LIBRARIES += android-support-v4
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v7/palette/build.gradle b/v7/palette/build.gradle
index 863028a..51a64f8 100644
--- a/v7/palette/build.gradle
+++ b/v7/palette/build.gradle
@@ -14,15 +14,15 @@
         minSdkVersion 7
     }
 
-    sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDir 'src'
-    }
-
     lintOptions {
         // TODO: fix errors and reenable.
         abortOnError false
     }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
 }
 
 android.libraryVariants.all { variant ->
diff --git a/v7/palette/src/android/support/v7/graphics/ColorCutQuantizer.java b/v7/palette/src/android/support/v7/graphics/ColorCutQuantizer.java
deleted file mode 100644
index 8fbd87c..0000000
--- a/v7/palette/src/android/support/v7/graphics/ColorCutQuantizer.java
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright 2014 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.v7.graphics;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.support.v7.graphics.Palette.Swatch;
-import android.util.SparseIntArray;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-import java.util.PriorityQueue;
-
-/**
- * An color quantizer based on the Median-cut algorithm, but optimized for picking out distinct
- * colors rather than representation colors.
- *
- * The color space is represented as a 3-dimensional cube with each dimension being an RGB
- * component. The cube is then repeatedly divided until we have reduced the color space to the
- * requested number of colors. An average color is then generated from each cube.
- *
- * What makes this different to median-cut is that median-cut divided cubes so that all of the cubes
- * have roughly the same population, where this quantizer divides boxes based on their color volume.
- * This means that the color space is divided into distinct colors, rather than representative
- * colors.
- */
-final class ColorCutQuantizer {
-
-    private static final String LOG_TAG = ColorCutQuantizer.class.getSimpleName();
-
-    private final float[] mTempHsl = new float[3];
-
-    private static final float BLACK_MAX_LIGHTNESS = 0.05f;
-    private static final float WHITE_MIN_LIGHTNESS = 0.95f;
-
-    private static final int COMPONENT_RED = -3;
-    private static final int COMPONENT_GREEN = -2;
-    private static final int COMPONENT_BLUE = -1;
-
-    private final int[] mColors;
-    private final SparseIntArray mColorPopulations;
-
-    private final List<Swatch> mQuantizedColors;
-
-    /**
-     * Factory-method to generate a {@link ColorCutQuantizer} from a {@link Bitmap} object.
-     *
-     * @param bitmap Bitmap to extract the pixel data from
-     * @param maxColors The maximum number of colors that should be in the result palette.
-     */
-    static ColorCutQuantizer fromBitmap(Bitmap bitmap, int maxColors) {
-        final int width = bitmap.getWidth();
-        final int height = bitmap.getHeight();
-
-        final int[] pixels = new int[width * height];
-        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
-
-        return new ColorCutQuantizer(new ColorHistogram(pixels), maxColors);
-    }
-
-    /**
-     * Private constructor.
-     *
-     * @param colorHistogram histogram representing an image's pixel data
-     * @param maxColors The maximum number of colors that should be in the result palette.
-     */
-    private ColorCutQuantizer(ColorHistogram colorHistogram, int maxColors) {
-        final int rawColorCount = colorHistogram.getNumberOfColors();
-        final int[] rawColors = colorHistogram.getColors();
-        final int[] rawColorCounts = colorHistogram.getColorCounts();
-
-        // First, lets pack the populations into a SparseIntArray so that they can be easily
-        // retrieved without knowing a color's index
-        mColorPopulations = new SparseIntArray(rawColorCount);
-        for (int i = 0; i < rawColors.length; i++) {
-            mColorPopulations.append(rawColors[i], rawColorCounts[i]);
-        }
-
-        // Now go through all of the colors and keep those which we do not want to ignore
-        mColors = new int[rawColorCount];
-        int validColorCount = 0;
-        for (int color : rawColors) {
-            if (!shouldIgnoreColor(color)) {
-                mColors[validColorCount++] = color;
-            }
-        }
-
-        if (validColorCount <= maxColors) {
-            // The image has fewer colors than the maximum requested, so just return the colors
-            mQuantizedColors = new ArrayList<Swatch>();
-            for (final int color : mColors) {
-                mQuantizedColors.add(new Swatch(color, mColorPopulations.get(color)));
-            }
-        } else {
-            // We need use quantization to reduce the number of colors
-            mQuantizedColors = quantizePixels(validColorCount - 1, maxColors);
-        }
-    }
-
-    /**
-     * @return the list of quantized colors
-     */
-    List<Swatch> getQuantizedColors() {
-        return mQuantizedColors;
-    }
-
-    private List<Swatch> quantizePixels(int maxColorIndex, int maxColors) {
-        // Create the priority queue which is sorted by volume descending. This means we always
-        // split the largest box in the queue
-        final PriorityQueue<Vbox> pq = new PriorityQueue<Vbox>(maxColors, VBOX_COMPARATOR_VOLUME);
-
-        // To start, offer a box which contains all of the colors
-        pq.offer(new Vbox(0, maxColorIndex));
-
-        // Now go through the boxes, splitting them until we have reached maxColors or there are no
-        // more boxes to split
-        splitBoxes(pq, maxColors);
-
-        // Finally, return the average colors of the color boxes
-        return generateAverageColors(pq);
-    }
-
-    /**
-     * Iterate through the {@link java.util.Queue}, popping
-     * {@link ColorCutQuantizer.Vbox} objects from the queue
-     * and splitting them. Once split, the new box and the remaining box are offered back to the
-     * queue.
-     *
-     * @param queue {@link java.util.PriorityQueue} to poll for boxes
-     * @param maxSize Maximum amount of boxes to split
-     */
-    private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) {
-        while (queue.size() < maxSize) {
-            final Vbox vbox = queue.poll();
-
-            if (vbox != null && vbox.canSplit()) {
-                // First split the box, and offer the result
-                queue.offer(vbox.splitBox());
-                // Then offer the box back
-                queue.offer(vbox);
-            } else {
-                // If we get here then there are no more boxes to split, so return
-                return;
-            }
-        }
-    }
-
-    private List<Swatch> generateAverageColors(Collection<Vbox> vboxes) {
-        ArrayList<Swatch> colors = new ArrayList<Swatch>(vboxes.size());
-        for (Vbox vbox : vboxes) {
-            Swatch color = vbox.getAverageColor();
-            if (!shouldIgnoreColor(color)) {
-                // As we're averaging a color box, we can still get colors which we do not want, so
-                // we check again here
-                colors.add(color);
-            }
-        }
-        return colors;
-    }
-
-    /**
-     * Represents a tightly fitting box around a color space.
-     */
-    private class Vbox {
-        // lower and upper index are inclusive
-        private int mLowerIndex;
-        private int mUpperIndex;
-
-        private int mMinRed, mMaxRed;
-        private int mMinGreen, mMaxGreen;
-        private int mMinBlue, mMaxBlue;
-
-        Vbox(int lowerIndex, int upperIndex) {
-            mLowerIndex = lowerIndex;
-            mUpperIndex = upperIndex;
-            fitBox();
-        }
-
-        int getVolume() {
-            return (mMaxRed - mMinRed + 1) * (mMaxGreen - mMinGreen + 1) *
-                    (mMaxBlue - mMinBlue + 1);
-        }
-
-        boolean canSplit() {
-            return getColorCount() > 1;
-        }
-
-        int getColorCount() {
-            return mUpperIndex - mLowerIndex + 1;
-        }
-
-        /**
-         * Recomputes the boundaries of this box to tightly fit the colors within the box.
-         */
-        void fitBox() {
-            // Reset the min and max to opposite values
-            mMinRed = mMinGreen = mMinBlue = 0xFF;
-            mMaxRed = mMaxGreen = mMaxBlue = 0x0;
-
-            for (int i = mLowerIndex; i <= mUpperIndex; i++) {
-                final int color = mColors[i];
-                final int r = Color.red(color);
-                final int g = Color.green(color);
-                final int b = Color.blue(color);
-                if (r > mMaxRed) {
-                    mMaxRed = r;
-                }
-                if (r < mMinRed) {
-                    mMinRed = r;
-                }
-                if (g > mMaxGreen) {
-                    mMaxGreen = g;
-                }
-                if (g < mMinGreen) {
-                    mMinGreen = g;
-                }
-                if (b > mMaxBlue) {
-                    mMaxBlue = b;
-                }
-                if (b < mMinBlue) {
-                    mMinBlue = b;
-                }
-            }
-        }
-
-        /**
-         * Split this color box at the mid-point along it's longest dimension
-         *
-         * @return the new ColorBox
-         */
-        Vbox splitBox() {
-            if (!canSplit()) {
-                throw new IllegalStateException("Can not split a box with only 1 color");
-            }
-
-            // find median along the longest dimension
-            final int splitPoint = findSplitPoint();
-
-            Vbox newBox = new Vbox(splitPoint + 1, mUpperIndex);
-
-            // Now change this box's upperIndex and recompute the color boundaries
-            mUpperIndex = splitPoint;
-            fitBox();
-
-            return newBox;
-        }
-
-        /**
-         * @return the dimension which this box is largest in
-         */
-        int getLongestColorDimension() {
-            final int redLength = mMaxRed - mMinRed;
-            final int greenLength = mMaxGreen - mMinGreen;
-            final int blueLength = mMaxBlue - mMinBlue;
-
-            if (redLength >= greenLength && redLength >= blueLength) {
-                return COMPONENT_RED;
-            } else if (greenLength >= redLength && greenLength >= blueLength) {
-                return COMPONENT_GREEN;
-            } else {
-                return COMPONENT_BLUE;
-            }
-        }
-
-        /**
-         * Finds the point within this box's lowerIndex and upperIndex index of where to split.
-         *
-         * This is calculated by finding the longest color dimension, and then sorting the
-         * sub-array based on that dimension value in each color. The colors are then iterated over
-         * until a color is found with at least the midpoint of the whole box's dimension midpoint.
-         *
-         * @return the index of the colors array to split from
-         */
-        int findSplitPoint() {
-            final int longestDimension = getLongestColorDimension();
-
-            // We need to sort the colors in this box based on the longest color dimension.
-            // As we can't use a Comparator to define the sort logic, we modify each color so that
-            // it's most significant is the desired dimension
-            modifySignificantOctet(longestDimension, mLowerIndex, mUpperIndex);
-
-            // Now sort... Arrays.sort uses a exclusive toIndex so we need to add 1
-            Arrays.sort(mColors, mLowerIndex, mUpperIndex + 1);
-
-            // Now revert all of the colors so that they are packed as RGB again
-            modifySignificantOctet(longestDimension, mLowerIndex, mUpperIndex);
-
-            final int dimensionMidPoint = midPoint(longestDimension);
-
-            for (int i = mLowerIndex; i <= mUpperIndex; i++)  {
-                final int color = mColors[i];
-
-                switch (longestDimension) {
-                    case COMPONENT_RED:
-                        if (Color.red(color) >= dimensionMidPoint) {
-                            return i;
-                        }
-                        break;
-                    case COMPONENT_GREEN:
-                        if (Color.green(color) >= dimensionMidPoint) {
-                            return i;
-                        }
-                        break;
-                    case COMPONENT_BLUE:
-                        if (Color.blue(color) > dimensionMidPoint) {
-                            return i;
-                        }
-                        break;
-                }
-            }
-
-            return mLowerIndex;
-        }
-
-        /**
-         * @return the average color of this box.
-         */
-        Swatch getAverageColor() {
-            int redSum = 0;
-            int greenSum = 0;
-            int blueSum = 0;
-            int totalPopulation = 0;
-
-            for (int i = mLowerIndex; i <= mUpperIndex; i++) {
-                final int color = mColors[i];
-                final int colorPopulation = mColorPopulations.get(color);
-
-                totalPopulation += colorPopulation;
-                redSum += colorPopulation * Color.red(color);
-                greenSum += colorPopulation * Color.green(color);
-                blueSum += colorPopulation * Color.blue(color);
-            }
-
-            final int redAverage = Math.round(redSum / (float) totalPopulation);
-            final int greenAverage = Math.round(greenSum / (float) totalPopulation);
-            final int blueAverage = Math.round(blueSum / (float) totalPopulation);
-
-            return new Swatch(redAverage, greenAverage, blueAverage, totalPopulation);
-        }
-
-        /**
-         * @return the midpoint of this box in the given {@code dimension}
-         */
-        int midPoint(int dimension) {
-            switch (dimension) {
-                case COMPONENT_RED:
-                default:
-                    return (mMinRed + mMaxRed) / 2;
-                case COMPONENT_GREEN:
-                    return (mMinGreen + mMaxGreen) / 2;
-                case COMPONENT_BLUE:
-                    return (mMinBlue + mMaxBlue) / 2;
-            }
-        }
-    }
-
-    /**
-     * Modify the significant octet in a packed color int. Allows sorting based on the value of a
-     * single color component.
-     *
-     * @see Vbox#findSplitPoint()
-     */
-    private void modifySignificantOctet(final int dimension, int lowerIndex, int upperIndex) {
-        switch (dimension) {
-            case COMPONENT_RED:
-                // Already in RGB, no need to do anything
-                break;
-            case COMPONENT_GREEN:
-                // We need to do a RGB to GRB swap, or vice-versa
-                for (int i = lowerIndex; i <= upperIndex; i++) {
-                    final int color = mColors[i];
-                    mColors[i] = Color.rgb((color >> 8) & 0xFF, (color >> 16) & 0xFF, color & 0xFF);
-                }
-                break;
-            case COMPONENT_BLUE:
-                // We need to do a RGB to BGR swap, or vice-versa
-                for (int i = lowerIndex; i <= upperIndex; i++) {
-                    final int color = mColors[i];
-                    mColors[i] = Color.rgb(color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF);
-                }
-                break;
-        }
-    }
-
-    private boolean shouldIgnoreColor(int color) {
-        ColorUtils.RGBtoHSL(Color.red(color), Color.green(color), Color.blue(color), mTempHsl);
-        return shouldIgnoreColor(mTempHsl);
-    }
-
-    private static boolean shouldIgnoreColor(Swatch color) {
-        return shouldIgnoreColor(color.getHsl());
-    }
-
-    private static boolean shouldIgnoreColor(float[] hslColor) {
-        return isWhite(hslColor) || isBlack(hslColor) || isNearRedILine(hslColor);
-    }
-
-    /**
-     * @return true if the color represents a color which is close to black.
-     */
-    private static boolean isBlack(float[] hslColor) {
-        return hslColor[2] <= BLACK_MAX_LIGHTNESS;
-    }
-
-    /**
-     * @return true if the color represents a color which is close to white.
-     */
-    private static boolean isWhite(float[] hslColor) {
-        return hslColor[2] >= WHITE_MIN_LIGHTNESS;
-    }
-
-    /**
-     * @return true if the color lies close to the red side of the I line.
-     */
-    private static boolean isNearRedILine(float[] hslColor) {
-        return hslColor[0] >= 10f && hslColor[0] <= 37f && hslColor[1] <= 0.82f;
-    }
-
-    /**
-     * Comparator which sorts {@link Vbox} instances based on their volume, in descending order
-     */
-    private static final Comparator<Vbox> VBOX_COMPARATOR_VOLUME = new Comparator<Vbox>() {
-        @Override
-        public int compare(Vbox lhs, Vbox rhs) {
-            return rhs.getVolume() - lhs.getVolume();
-        }
-    };
-
-}
diff --git a/v7/palette/src/android/support/v7/graphics/ColorHistogram.java b/v7/palette/src/android/support/v7/graphics/ColorHistogram.java
deleted file mode 100644
index bcc43fe..0000000
--- a/v7/palette/src/android/support/v7/graphics/ColorHistogram.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2014 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.v7.graphics;
-
-import java.util.Arrays;
-
-/**
- * Class which provides a histogram for RGB values.
- */
-final class ColorHistogram {
-
-    private final int[] mColors;
-    private final int[] mColorCounts;
-    private final int mNumberColors;
-
-    /**
-     * A new {@link ColorHistogram} instance.
-     *
-     * @param pixels array of image contents
-     */
-    ColorHistogram(final int[] pixels) {
-        // Sort the pixels to enable counting below
-        Arrays.sort(pixels);
-
-        // Count number of distinct colors
-        mNumberColors = countDistinctColors(pixels);
-
-        // Create arrays
-        mColors = new int[mNumberColors];
-        mColorCounts = new int[mNumberColors];
-
-        // Finally count the frequency of each color
-        countFrequencies(pixels);
-    }
-
-    /**
-     * @return number of distinct colors in the image.
-     */
-    int getNumberOfColors() {
-        return mNumberColors;
-    }
-
-    /**
-     * @return an array containing all of the distinct colors in the image.
-     */
-    int[] getColors() {
-        return mColors;
-    }
-
-    /**
-     * @return an array containing the frequency of a distinct colors within the image.
-     */
-    int[] getColorCounts() {
-        return mColorCounts;
-    }
-
-    private static int countDistinctColors(final int[] pixels) {
-        if (pixels.length < 2) {
-            // If we have less than 2 pixels we can stop here
-            return pixels.length;
-        }
-
-        // If we have at least 2 pixels, we have a minimum of 1 color...
-        int colorCount = 1;
-        int currentColor = pixels[0];
-
-        // Now iterate from the second pixel to the end, counting distinct colors
-        for (int i = 1; i < pixels.length; i++) {
-            // If we encounter a new color, increase the population
-            if (pixels[i] != currentColor) {
-                currentColor = pixels[i];
-                colorCount++;
-            }
-        }
-
-        return colorCount;
-    }
-
-    private void countFrequencies(final int[] pixels) {
-        if (pixels.length == 0) {
-            return;
-        }
-
-        int currentColorIndex = 0;
-        int currentColor = pixels[0];
-
-        mColors[currentColorIndex] = currentColor;
-        mColorCounts[currentColorIndex] = 1;
-
-        if (pixels.length == 1) {
-            // If we only have one pixel, we can stop here
-            return;
-        }
-
-        // Now iterate from the second pixel to the end, population distinct colors
-        for (int i = 1; i < pixels.length; i++) {
-            if (pixels[i] == currentColor) {
-                // We've hit the same color as before, increase population
-                mColorCounts[currentColorIndex]++;
-            } else {
-                // We've hit a new color, increase index
-                currentColor = pixels[i];
-
-                currentColorIndex++;
-                mColors[currentColorIndex] = currentColor;
-                mColorCounts[currentColorIndex] = 1;
-            }
-        }
-    }
-
-}
diff --git a/v7/palette/src/android/support/v7/graphics/ColorUtils.java b/v7/palette/src/android/support/v7/graphics/ColorUtils.java
deleted file mode 100644
index 597fb6e..0000000
--- a/v7/palette/src/android/support/v7/graphics/ColorUtils.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright 2014 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.v7.graphics;
-
-import android.graphics.Color;
-
-final class ColorUtils {
-
-    private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10;
-    private static final int MIN_ALPHA_SEARCH_PRECISION = 10;
-
-    private ColorUtils() {}
-
-    /**
-     * Composite two potentially translucent colors over each other and returns the result.
-     */
-    private static int compositeColors(int fg, int bg) {
-        final float alpha1 = Color.alpha(fg) / 255f;
-        final float alpha2 = Color.alpha(bg) / 255f;
-
-        float a = (alpha1 + alpha2) * (1f - alpha1);
-        float r = (Color.red(fg) * alpha1) + (Color.red(bg) * alpha2 * (1f - alpha1));
-        float g = (Color.green(fg) * alpha1) + (Color.green(bg) * alpha2 * (1f - alpha1));
-        float b = (Color.blue(fg) * alpha1) + (Color.blue(bg) * alpha2 * (1f - alpha1));
-
-        return Color.argb((int) a, (int) r, (int) g, (int) b);
-    }
-
-    /**
-     * Returns the luminance of a color.
-     *
-     * Formula defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
-     */
-    private static double calculateLuminance(int color) {
-        double red = Color.red(color) / 255d;
-        red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4);
-
-        double green = Color.green(color) / 255d;
-        green = green < 0.03928 ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4);
-
-        double blue = Color.blue(color) / 255d;
-        blue = blue < 0.03928 ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4);
-
-        return (0.2126 * red) + (0.7152 * green) + (0.0722 * blue);
-    }
-
-    /**
-     * Returns the contrast ratio between two colors.
-     *
-     * Formula defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
-     */
-    private static double calculateContrast(int foreground, int background) {
-        if (Color.alpha(background) != 255) {
-            throw new IllegalArgumentException("background can not be translucent");
-        }
-        if (Color.alpha(foreground) < 255) {
-            // If the foreground is translucent, composite the foreground over the background
-            foreground = compositeColors(foreground, background);
-        }
-
-        final double luminance1 = calculateLuminance(foreground) + 0.05;
-        final double luminance2 = calculateLuminance(background) + 0.05;
-
-        // Now return the lighter luminance divided by the darker luminance
-        return Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2);
-    }
-
-    /**
-     * Finds the minimum alpha value which can be applied to {@code foreground} so that is has a
-     * contrast value of at least {@code minContrastRatio} when compared to background.
-     *
-     * @return the alpha value in the range 0-255.
-     */
-    private static int findMinimumAlpha(int foreground, int background, double minContrastRatio) {
-        if (Color.alpha(background) != 255) {
-            throw new IllegalArgumentException("background can not be translucent");
-        }
-
-        // First lets check that a fully opaque foreground has sufficient contrast
-        int testForeground = modifyAlpha(foreground, 255);
-        double testRatio = calculateContrast(testForeground, background);
-        if (testRatio < minContrastRatio) {
-            // Fully opaque foreground does not have sufficient contrast, return error
-            return -1;
-        }
-
-        // Binary search to find a value with the minimum value which provides sufficient contrast
-        int numIterations = 0;
-        int minAlpha = 0;
-        int maxAlpha = 255;
-
-        while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS &&
-                (maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) {
-            final int testAlpha = (minAlpha + maxAlpha) / 2;
-
-            testForeground = modifyAlpha(foreground, testAlpha);
-            testRatio = calculateContrast(testForeground, background);
-
-            if (testRatio < minContrastRatio) {
-                minAlpha = testAlpha;
-            } else {
-                maxAlpha = testAlpha;
-            }
-
-            numIterations++;
-        }
-
-        // Conservatively return the max of the range of possible alphas, which is known to pass.
-        return maxAlpha;
-    }
-
-    static int getTextColorForBackground(int backgroundColor, int textColor, float minContrastRatio) {
-        final int minAlpha = ColorUtils
-                .findMinimumAlpha(textColor, backgroundColor, minContrastRatio);
-
-        if (minAlpha >= 0) {
-            return ColorUtils.modifyAlpha(textColor, minAlpha);
-        }
-
-        // Didn't find an opacity which provided enough contrast
-        return -1;
-    }
-
-    static void RGBtoHSL(int r, int g, int b, float[] hsl) {
-        final float rf = r / 255f;
-        final float gf = g / 255f;
-        final float bf = b / 255f;
-
-        final float max = Math.max(rf, Math.max(gf, bf));
-        final float min = Math.min(rf, Math.min(gf, bf));
-        final float deltaMaxMin = max - min;
-
-        float h, s;
-        float l = (max + min) / 2f;
-
-        if (max == min) {
-            // Monochromatic
-            h = s = 0f;
-        } else {
-            if (max == rf) {
-                h = ((gf - bf) / deltaMaxMin) % 6f;
-            } else if (max == gf) {
-                h = ((bf - rf) / deltaMaxMin) + 2f;
-            } else {
-                h = ((rf - gf) / deltaMaxMin) + 4f;
-            }
-
-            s =  deltaMaxMin / (1f - Math.abs(2f * l - 1f));
-        }
-
-        hsl[0] = (h * 60f) % 360f;
-        hsl[1] = s;
-        hsl[2] = l;
-    }
-
-    static int HSLtoRGB (float[] hsl) {
-        final float h = hsl[0];
-        final float s = hsl[1];
-        final float l = hsl[2];
-
-        final float c = (1f - Math.abs(2 * l - 1f)) * s;
-        final float m = l - 0.5f * c;
-        final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f));
-
-        final int hueSegment = (int) h / 60;
-
-        int r = 0, g = 0, b = 0;
-
-        switch (hueSegment) {
-            case 0:
-                r = Math.round(255 * (c + m));
-                g = Math.round(255 * (x + m));
-                b = Math.round(255 * m);
-                break;
-            case 1:
-                r = Math.round(255 * (x + m));
-                g = Math.round(255 * (c + m));
-                b = Math.round(255 * m);
-                break;
-            case 2:
-                r = Math.round(255 * m);
-                g = Math.round(255 * (c + m));
-                b = Math.round(255 * (x + m));
-                break;
-            case 3:
-                r = Math.round(255 * m);
-                g = Math.round(255 * (x + m));
-                b = Math.round(255 * (c + m));
-                break;
-            case 4:
-                r = Math.round(255 * (x + m));
-                g = Math.round(255 * m);
-                b = Math.round(255 * (c + m));
-                break;
-            case 5:
-            case 6:
-                r = Math.round(255 * (c + m));
-                g = Math.round(255 * m);
-                b = Math.round(255 * (x + m));
-                break;
-        }
-
-        r = Math.max(0, Math.min(255, r));
-        g = Math.max(0, Math.min(255, g));
-        b = Math.max(0, Math.min(255, b));
-
-        return Color.rgb(r, g, b);
-    }
-
-    /**
-     * Set the alpha component of {@code color} to be {@code alpha}.
-     */
-    static int modifyAlpha(int color, int alpha) {
-        return (color & 0x00ffffff) | (alpha << 24);
-    }
-
-}
diff --git a/v7/palette/src/android/support/v7/graphics/Palette.java b/v7/palette/src/android/support/v7/graphics/Palette.java
deleted file mode 100644
index 84543d5..0000000
--- a/v7/palette/src/android/support/v7/graphics/Palette.java
+++ /dev/null
@@ -1,685 +0,0 @@
-/*
- * Copyright 2014 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.v7.graphics;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.os.AsyncTask;
-import android.support.v4.os.AsyncTaskCompat;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A helper class to extract prominent colors from an image.
- * <p>
- * A number of colors with different profiles are extracted from the image:
- * <ul>
- *     <li>Vibrant</li>
- *     <li>Vibrant Dark</li>
- *     <li>Vibrant Light</li>
- *     <li>Muted</li>
- *     <li>Muted Dark</li>
- *     <li>Muted Light</li>
- * </ul>
- * These can be retrieved from the appropriate getter method.
- *
- * <p>
- * Instances can be created with the synchronous factory methods {@link #generate(Bitmap)} and
- * {@link #generate(Bitmap, int)}.
- * <p>
- * These should be called on a background thread, ideally the one in
- * which you load your images on. Sometimes that is not possible, so asynchronous factory methods
- * have also been provided: {@link #generateAsync(Bitmap, PaletteAsyncListener)} and
- * {@link #generateAsync(Bitmap, int, PaletteAsyncListener)}. These can be used as so:
- *
- * <pre>
- * Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
- *     public void onGenerated(Palette palette) {
- *         // Do something with colors...
- *     }
- * });
- * </pre>
- */
-public final class Palette {
-
-    /**
-     * Listener to be used with {@link #generateAsync(Bitmap, PaletteAsyncListener)} or
-     * {@link #generateAsync(Bitmap, int, PaletteAsyncListener)}
-     */
-    public interface PaletteAsyncListener {
-
-        /**
-         * Called when the {@link Palette} has been generated.
-         */
-        void onGenerated(Palette palette);
-    }
-
-    private static final int CALCULATE_BITMAP_MIN_DIMENSION = 100;
-    private static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16;
-
-    private static final float TARGET_DARK_LUMA = 0.26f;
-    private static final float MAX_DARK_LUMA = 0.45f;
-
-    private static final float MIN_LIGHT_LUMA = 0.55f;
-    private static final float TARGET_LIGHT_LUMA = 0.74f;
-
-    private static final float MIN_NORMAL_LUMA = 0.3f;
-    private static final float TARGET_NORMAL_LUMA = 0.5f;
-    private static final float MAX_NORMAL_LUMA = 0.7f;
-
-    private static final float TARGET_MUTED_SATURATION = 0.3f;
-    private static final float MAX_MUTED_SATURATION = 0.4f;
-
-    private static final float TARGET_VIBRANT_SATURATION = 1f;
-    private static final float MIN_VIBRANT_SATURATION = 0.35f;
-
-    private static final float WEIGHT_SATURATION = 3f;
-    private static final float WEIGHT_LUMA = 6f;
-    private static final float WEIGHT_POPULATION = 1f;
-
-    private static final float MIN_CONTRAST_TITLE_TEXT = 3.0f;
-    private static final float MIN_CONTRAST_BODY_TEXT = 4.5f;
-
-    private final List<Swatch> mSwatches;
-    private final int mHighestPopulation;
-
-    private Swatch mVibrantSwatch;
-    private Swatch mMutedSwatch;
-
-    private Swatch mDarkVibrantSwatch;
-    private Swatch mDarkMutedSwatch;
-
-    private Swatch mLightVibrantSwatch;
-    private Swatch mLightMutedColor;
-
-    /**
-     * Generate a {@link Palette} from a {@link Bitmap} using the default number of colors.
-     */
-    public static Palette generate(Bitmap bitmap) {
-        return generate(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS);
-    }
-
-    /**
-     * Generate a {@link Palette} from a {@link Bitmap} using the specified {@code numColors}.
-     * Good values for {@code numColors} depend on the source image type.
-     * For landscapes, a good values are in the range 12-16. For images which are largely made up
-     * of people's faces then this value should be increased to 24-32.
-     *
-     * @param numColors The maximum number of colors in the generated palette. Increasing this
-     *                  number will increase the time needed to compute the values.
-     */
-    public static Palette generate(Bitmap bitmap, int numColors) {
-        checkBitmapParam(bitmap);
-        checkNumberColorsParam(numColors);
-
-        // First we'll scale down the bitmap so it's shortest dimension is 100px
-        final Bitmap scaledBitmap = scaleBitmapDown(bitmap);
-
-        // Now generate a quantizer from the Bitmap
-        ColorCutQuantizer quantizer = ColorCutQuantizer.fromBitmap(scaledBitmap, numColors);
-
-        // If created a new bitmap, recycle it
-        if (scaledBitmap != bitmap) {
-            scaledBitmap.recycle();
-        }
-
-        // Now return a ColorExtractor instance
-        return new Palette(quantizer.getQuantizedColors());
-    }
-
-    /**
-     * Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)}
-     * will be called with the created instance. The resulting {@link Palette} is the same as
-     * what would be created by calling {@link #generate(Bitmap)}.
-     *
-     * @param listener Listener to be invoked when the {@link Palette} has been generated.
-     *
-     * @return the {@link android.os.AsyncTask} used to asynchronously generate the instance.
-     */
-    public static AsyncTask<Bitmap, Void, Palette> generateAsync(
-            Bitmap bitmap, PaletteAsyncListener listener) {
-        return generateAsync(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS, listener);
-    }
-
-    /**
-     * Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)}
-     * will be called with the created instance. The resulting {@link Palette} is the same as what
-     * would be created by calling {@link #generate(Bitmap, int)}.
-     *
-     * @param listener Listener to be invoked when the {@link Palette} has been generated.
-     *
-     * @return the {@link android.os.AsyncTask} used to asynchronously generate the instance.
-     */
-    public static AsyncTask<Bitmap, Void, Palette> generateAsync(
-            final Bitmap bitmap, final int numColors, final PaletteAsyncListener listener) {
-        checkBitmapParam(bitmap);
-        checkNumberColorsParam(numColors);
-        checkAsyncListenerParam(listener);
-
-        return AsyncTaskCompat.executeParallel(
-                new AsyncTask<Bitmap, Void, Palette>() {
-                    @Override
-                    protected Palette doInBackground(Bitmap... params) {
-                        return generate(params[0], numColors);
-                    }
-
-                    @Override
-                    protected void onPostExecute(Palette colorExtractor) {
-                        listener.onGenerated(colorExtractor);
-                    }
-                }, bitmap);
-    }
-
-    /**
-     * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches.
-     * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a
-     * list of swatches. Will return null if the {@code swatches} is null.
-     */
-    public static Palette from(List<Swatch> swatches) {
-        if (swatches == null) {
-            return null;
-        }
-        return new Palette(swatches);
-    }
-
-    private Palette(List<Swatch> swatches) {
-        mSwatches = swatches;
-        mHighestPopulation = findMaxPopulation();
-
-        mVibrantSwatch = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,
-                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
-
-        mLightVibrantSwatch = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,
-                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
-
-        mDarkVibrantSwatch = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,
-                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
-
-        mMutedSwatch = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,
-                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
-
-        mLightMutedColor = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,
-                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
-
-        mDarkMutedSwatch = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,
-                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
-
-        // Now try and generate any missing colors
-        generateEmptySwatches();
-    }
-
-    /**
-     * Returns all of the swatches which make up the palette.
-     */
-    public List<Swatch> getSwatches() {
-        return Collections.unmodifiableList(mSwatches);
-    }
-
-    /**
-     * Returns the most vibrant swatch in the palette. Might be null.
-     */
-    public Swatch getVibrantSwatch() {
-        return mVibrantSwatch;
-    }
-
-    /**
-     * Returns a light and vibrant swatch from the palette. Might be null.
-     */
-    public Swatch getLightVibrantSwatch() {
-        return mLightVibrantSwatch;
-    }
-
-    /**
-     * Returns a dark and vibrant swatch from the palette. Might be null.
-     */
-    public Swatch getDarkVibrantSwatch() {
-        return mDarkVibrantSwatch;
-    }
-
-    /**
-     * Returns a muted swatch from the palette. Might be null.
-     */
-    public Swatch getMutedSwatch() {
-        return mMutedSwatch;
-    }
-
-    /**
-     * Returns a muted and light swatch from the palette. Might be null.
-     */
-    public Swatch getLightMutedSwatch() {
-        return mLightMutedColor;
-    }
-
-    /**
-     * Returns a muted and dark swatch from the palette. Might be null.
-     */
-    public Swatch getDarkMutedSwatch() {
-        return mDarkMutedSwatch;
-    }
-
-    /**
-     * Returns the most vibrant color in the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     */
-    public int getVibrantColor(int defaultColor) {
-        return mVibrantSwatch != null ? mVibrantSwatch.getRgb() : defaultColor;
-    }
-
-    /**
-     * Returns a light and vibrant color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     */
-    public int getLightVibrantColor(int defaultColor) {
-        return mLightVibrantSwatch != null ? mLightVibrantSwatch.getRgb() : defaultColor;
-    }
-
-    /**
-     * Returns a dark and vibrant color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     */
-    public int getDarkVibrantColor(int defaultColor) {
-        return mDarkVibrantSwatch != null ? mDarkVibrantSwatch.getRgb() : defaultColor;
-    }
-
-    /**
-     * Returns a muted color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     */
-    public int getMutedColor(int defaultColor) {
-        return mMutedSwatch != null ? mMutedSwatch.getRgb() : defaultColor;
-    }
-
-    /**
-     * Returns a muted and light color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     */
-    public int getLightMutedColor(int defaultColor) {
-        return mLightMutedColor != null ? mLightMutedColor.getRgb() : defaultColor;
-    }
-
-    /**
-     * Returns a muted and dark color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     */
-    public int getDarkMutedColor(int defaultColor) {
-        return mDarkMutedSwatch != null ? mDarkMutedSwatch.getRgb() : defaultColor;
-    }
-
-    /**
-     * @return true if we have already selected {@code swatch}
-     */
-    private boolean isAlreadySelected(Swatch swatch) {
-        return mVibrantSwatch == swatch || mDarkVibrantSwatch == swatch ||
-                mLightVibrantSwatch == swatch || mMutedSwatch == swatch ||
-                mDarkMutedSwatch == swatch || mLightMutedColor == swatch;
-    }
-
-    private Swatch findColor(float targetLuma, float minLuma, float maxLuma,
-                             float targetSaturation, float minSaturation, float maxSaturation) {
-        Swatch max = null;
-        float maxValue = 0f;
-
-        for (Swatch swatch : mSwatches) {
-            final float sat = swatch.getHsl()[1];
-            final float luma = swatch.getHsl()[2];
-
-            if (sat >= minSaturation && sat <= maxSaturation &&
-                    luma >= minLuma && luma <= maxLuma &&
-                    !isAlreadySelected(swatch)) {
-                float thisValue = createComparisonValue(sat, targetSaturation, luma, targetLuma,
-                        swatch.getPopulation(), mHighestPopulation);
-                if (max == null || thisValue > maxValue) {
-                    max = swatch;
-                    maxValue = thisValue;
-                }
-            }
-        }
-
-        return max;
-    }
-
-    /**
-     * Try and generate any missing swatches from the swatches we did find.
-     */
-    private void generateEmptySwatches() {
-        if (mVibrantSwatch == null) {
-            // If we do not have a vibrant color...
-            if (mDarkVibrantSwatch != null) {
-                // ...but we do have a dark vibrant, generate the value by modifying the luma
-                final float[] newHsl = copyHslValues(mDarkVibrantSwatch);
-                newHsl[2] = TARGET_NORMAL_LUMA;
-                mVibrantSwatch = new Swatch(ColorUtils.HSLtoRGB(newHsl), 0);
-            }
-        }
-
-        if (mDarkVibrantSwatch == null) {
-            // If we do not have a dark vibrant color...
-            if (mVibrantSwatch != null) {
-                // ...but we do have a vibrant, generate the value by modifying the luma
-                final float[] newHsl = copyHslValues(mVibrantSwatch);
-                newHsl[2] = TARGET_DARK_LUMA;
-                mDarkVibrantSwatch = new Swatch(ColorUtils.HSLtoRGB(newHsl), 0);
-            }
-        }
-    }
-
-    /**
-     * Find the {@link Swatch} with the highest population value and return the population.
-     */
-    private int findMaxPopulation() {
-        int population = 0;
-        for (Swatch swatch : mSwatches) {
-            population = Math.max(population, swatch.getPopulation());
-        }
-        return population;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        Palette palette = (Palette) o;
-
-        if (mSwatches != null ? !mSwatches.equals(palette.mSwatches) : palette.mSwatches != null) {
-            return false;
-        }
-        if (mDarkMutedSwatch != null ? !mDarkMutedSwatch.equals(palette.mDarkMutedSwatch)
-                : palette.mDarkMutedSwatch != null) {
-            return false;
-        }
-        if (mDarkVibrantSwatch != null ? !mDarkVibrantSwatch.equals(palette.mDarkVibrantSwatch)
-                : palette.mDarkVibrantSwatch != null) {
-            return false;
-        }
-        if (mLightMutedColor != null ? !mLightMutedColor.equals(palette.mLightMutedColor)
-                : palette.mLightMutedColor != null) {
-            return false;
-        }
-        if (mLightVibrantSwatch != null ? !mLightVibrantSwatch.equals(palette.mLightVibrantSwatch)
-                : palette.mLightVibrantSwatch != null) {
-            return false;
-        }
-        if (mMutedSwatch != null ? !mMutedSwatch.equals(palette.mMutedSwatch)
-                : palette.mMutedSwatch != null) {
-            return false;
-        }
-        if (mVibrantSwatch != null ? !mVibrantSwatch.equals(palette.mVibrantSwatch)
-                : palette.mVibrantSwatch != null) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = mSwatches != null ? mSwatches.hashCode() : 0;
-        result = 31 * result + (mVibrantSwatch != null ? mVibrantSwatch.hashCode() : 0);
-        result = 31 * result + (mMutedSwatch != null ? mMutedSwatch.hashCode() : 0);
-        result = 31 * result + (mDarkVibrantSwatch != null ? mDarkVibrantSwatch.hashCode() : 0);
-        result = 31 * result + (mDarkMutedSwatch != null ? mDarkMutedSwatch.hashCode() : 0);
-        result = 31 * result + (mLightVibrantSwatch != null ? mLightVibrantSwatch.hashCode() : 0);
-        result = 31 * result + (mLightMutedColor != null ? mLightMutedColor.hashCode() : 0);
-        return result;
-    }
-
-    /**
-     * Scale the bitmap down so that it's smallest dimension is
-     * {@value #CALCULATE_BITMAP_MIN_DIMENSION}px. If {@code bitmap} is smaller than this, than it
-     * is returned.
-     */
-    private static Bitmap scaleBitmapDown(Bitmap bitmap) {
-        final int minDimension = Math.min(bitmap.getWidth(), bitmap.getHeight());
-
-        if (minDimension <= CALCULATE_BITMAP_MIN_DIMENSION) {
-            // If the bitmap is small enough already, just return it
-            return bitmap;
-        }
-
-        final float scaleRatio = CALCULATE_BITMAP_MIN_DIMENSION / (float) minDimension;
-        return Bitmap.createScaledBitmap(bitmap,
-                Math.round(bitmap.getWidth() * scaleRatio),
-                Math.round(bitmap.getHeight() * scaleRatio),
-                false);
-    }
-
-    private static float createComparisonValue(float saturation, float targetSaturation,
-            float luma, float targetLuma,
-            int population, int highestPopulation) {
-        return weightedMean(
-                invertDiff(saturation, targetSaturation), WEIGHT_SATURATION,
-                invertDiff(luma, targetLuma), WEIGHT_LUMA,
-                population / (float) highestPopulation, WEIGHT_POPULATION
-        );
-    }
-
-    /**
-     * Copy a {@link Swatch}'s HSL values into a new float[].
-     */
-    private static float[] copyHslValues(Swatch color) {
-        final float[] newHsl = new float[3];
-        System.arraycopy(color.getHsl(), 0, newHsl, 0, 3);
-        return newHsl;
-    }
-
-    /**
-     * Returns a value in the range 0-1. 1 is returned when {@code value} equals the
-     * {@code targetValue} and then decreases as the absolute difference between {@code value} and
-     * {@code targetValue} increases.
-     *
-     * @param value the item's value
-     * @param targetValue the value which we desire
-     */
-    private static float invertDiff(float value, float targetValue) {
-        return 1f - Math.abs(value - targetValue);
-    }
-
-    private static float weightedMean(float... values) {
-        float sum = 0f;
-        float sumWeight = 0f;
-
-        for (int i = 0; i < values.length; i += 2) {
-            float value = values[i];
-            float weight = values[i + 1];
-
-            sum += (value * weight);
-            sumWeight += weight;
-        }
-
-        return sum / sumWeight;
-    }
-
-    private static void checkBitmapParam(Bitmap bitmap) {
-        if (bitmap == null) {
-            throw new IllegalArgumentException("bitmap can not be null");
-        }
-        if (bitmap.isRecycled()) {
-            throw new IllegalArgumentException("bitmap can not be recycled");
-        }
-    }
-
-    private static void checkNumberColorsParam(int numColors) {
-        if (numColors < 1) {
-            throw new IllegalArgumentException("numColors must be 1 of greater");
-        }
-    }
-
-    private static void checkAsyncListenerParam(PaletteAsyncListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener can not be null");
-        }
-    }
-
-    /**
-     * Represents a color swatch generated from an image's palette. The RGB color can be retrieved
-     * by calling {@link #getRgb()}.
-     */
-    public static final class Swatch {
-        private final int mRed, mGreen, mBlue;
-        private final int mRgb;
-        private final int mPopulation;
-
-        private boolean mGeneratedTextColors;
-        private int mTitleTextColor;
-        private int mBodyTextColor;
-
-        private float[] mHsl;
-
-        public Swatch(int color, int population) {
-            mRed = Color.red(color);
-            mGreen = Color.green(color);
-            mBlue = Color.blue(color);
-            mRgb = color;
-            mPopulation = population;
-        }
-
-        Swatch(int red, int green, int blue, int population) {
-            mRed = red;
-            mGreen = green;
-            mBlue = blue;
-            mRgb = Color.rgb(red, green, blue);
-            mPopulation = population;
-        }
-
-        /**
-         * @return this swatch's RGB color value
-         */
-        public int getRgb() {
-            return mRgb;
-        }
-
-        /**
-         * Return this swatch's HSL values.
-         *     hsv[0] is Hue [0 .. 360)
-         *     hsv[1] is Saturation [0...1]
-         *     hsv[2] is Lightness [0...1]
-         */
-        public float[] getHsl() {
-            if (mHsl == null) {
-                // Lazily generate HSL values from RGB
-                mHsl = new float[3];
-                ColorUtils.RGBtoHSL(mRed, mGreen, mBlue, mHsl);
-            }
-            return mHsl;
-        }
-
-        /**
-         * @return the number of pixels represented by this swatch
-         */
-        public int getPopulation() {
-            return mPopulation;
-        }
-
-        /**
-         * Returns an appropriate color to use for any 'title' text which is displayed over this
-         * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
-         */
-        public int getTitleTextColor() {
-            ensureTextColorsGenerated();
-            return mTitleTextColor;
-        }
-
-        /**
-         * Returns an appropriate color to use for any 'body' text which is displayed over this
-         * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
-         */
-        public int getBodyTextColor() {
-            ensureTextColorsGenerated();
-            return mBodyTextColor;
-        }
-
-        private void ensureTextColorsGenerated() {
-            if (!mGeneratedTextColors) {
-                // First check white, as most colors will be dark
-                final int lightBody = ColorUtils.getTextColorForBackground(
-                        mRgb, Color.WHITE,  MIN_CONTRAST_BODY_TEXT);
-                final int lightTitle = ColorUtils.getTextColorForBackground(
-                        mRgb, Color.WHITE, MIN_CONTRAST_TITLE_TEXT);
-
-                if (lightBody != -1 && lightTitle != -1) {
-                    // If we found valid light values, use them and return
-                    mBodyTextColor = lightBody;
-                    mTitleTextColor = lightTitle;
-                    mGeneratedTextColors = true;
-                    return;
-                }
-
-                final int darkBody = ColorUtils.getTextColorForBackground(
-                        mRgb, Color.BLACK, MIN_CONTRAST_BODY_TEXT);
-                final int darkTitle = ColorUtils.getTextColorForBackground(
-                        mRgb, Color.BLACK, MIN_CONTRAST_TITLE_TEXT);
-
-                if (darkBody != -1 && darkBody != -1) {
-                    // If we found valid dark values, use them and return
-                    mBodyTextColor = darkBody;
-                    mTitleTextColor = darkTitle;
-                    mGeneratedTextColors = true;
-                    return;
-                }
-
-                // If we reach here then we can not find title and body values which use the same
-                // lightness, we need to use mismatched values
-                mBodyTextColor = lightBody != -1 ? lightBody : darkBody;
-                mTitleTextColor = lightTitle != -1 ? lightTitle : darkTitle;
-                mGeneratedTextColors = true;
-            }
-        }
-
-        @Override
-        public String toString() {
-            return new StringBuilder(getClass().getSimpleName())
-                    .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']')
-                    .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']')
-                    .append(" [Population: ").append(mPopulation).append(']')
-                    .append(" [Title Text: #").append(Integer.toHexString(mTitleTextColor)).append(']')
-                    .append(" [Body Text: #").append(Integer.toHexString(mBodyTextColor)).append(']')
-                    .toString();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            Swatch swatch = (Swatch) o;
-            return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb;
-        }
-
-        @Override
-        public int hashCode() {
-            return 31 * mRgb + mPopulation;
-        }
-    }
-
-}
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/BucketTests.java b/v7/palette/src/androidTest/java/android/support/v7/graphics/BucketTests.java
new file mode 100644
index 0000000..dc0ec1d
--- /dev/null
+++ b/v7/palette/src/androidTest/java/android/support/v7/graphics/BucketTests.java
@@ -0,0 +1,69 @@
+/*
+ * 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.v7.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.test.InstrumentationTestCase;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ */
+public class BucketTests extends InstrumentationTestCase {
+
+    private Bitmap mSource;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mSource = BitmapFactory.decodeResource(getInstrumentation().getContext().getResources(),
+                android.R.drawable.sym_def_app_icon);
+    }
+
+    public void testSourceBitmapNotRecycled() {
+        Palette.from(mSource).generate();
+        assertFalse(mSource.isRecycled());
+    }
+
+    public void testSwatchesUnmodifiable() {
+        Palette p = Palette.from(mSource).generate();
+        boolean thrown = false;
+
+        try {
+            p.getSwatches().remove(0);
+        } catch (UnsupportedOperationException e) {
+            thrown = true;
+        }
+
+        assertTrue(thrown);
+    }
+
+    public void testSwatchesBuilder() {
+        ArrayList<Palette.Swatch> swatches = new ArrayList<>();
+        swatches.add(new Palette.Swatch(Color.BLACK, 40));
+        swatches.add(new Palette.Swatch(Color.GREEN, 60));
+        swatches.add(new Palette.Swatch(Color.BLUE, 10));
+
+        Palette p = Palette.from(swatches);
+
+        assertEquals(swatches, p.getSwatches());
+    }
+}
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/ConsistencyTest.java b/v7/palette/src/androidTest/java/android/support/v7/graphics/ConsistencyTest.java
new file mode 100644
index 0000000..0c4dc89
--- /dev/null
+++ b/v7/palette/src/androidTest/java/android/support/v7/graphics/ConsistencyTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.v7.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.test.InstrumentationTestCase;
+
+/**
+ * @hide
+ */
+public class ConsistencyTest extends InstrumentationTestCase {
+
+    private static final int NUMBER_TRIALS = 10;
+
+    public void testConsistency() {
+        Bitmap icon = BitmapFactory.decodeResource(getInstrumentation().getContext().getResources(),
+                android.R.drawable.sym_def_app_icon);
+
+        testConsistencyForBitmap(icon);
+    }
+
+    private void testConsistencyForBitmap(Bitmap bitmap) {
+        Palette lastPalette = null;
+
+        for (int i = 0; i < NUMBER_TRIALS; i++) {
+            Palette newPalette = Palette.from(bitmap).generate();
+            if (lastPalette != null) {
+                assetPalettesEqual(lastPalette, newPalette);
+            }
+            lastPalette = newPalette;
+        }
+    }
+
+    private static void assetPalettesEqual(Palette p1, Palette p2) {
+        assertEquals(p1.getVibrantSwatch(), p2.getVibrantSwatch());
+        assertEquals(p1.getLightVibrantSwatch(), p2.getLightVibrantSwatch());
+        assertEquals(p1.getDarkVibrantSwatch(), p2.getDarkVibrantSwatch());
+        assertEquals(p1.getMutedSwatch(), p2.getMutedSwatch());
+        assertEquals(p1.getLightMutedSwatch(), p2.getLightMutedSwatch());
+        assertEquals(p1.getDarkMutedSwatch(), p2.getDarkMutedSwatch());
+    }
+}
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/MaxColorsTest.java b/v7/palette/src/androidTest/java/android/support/v7/graphics/MaxColorsTest.java
new file mode 100644
index 0000000..4b7b0e4
--- /dev/null
+++ b/v7/palette/src/androidTest/java/android/support/v7/graphics/MaxColorsTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.v7.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.test.InstrumentationTestCase;
+
+/**
+ * @hide
+ */
+public class MaxColorsTest extends InstrumentationTestCase {
+
+    private Bitmap mSource;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mSource = BitmapFactory.decodeResource(getInstrumentation().getContext().getResources(),
+                android.R.drawable.sym_def_app_icon);
+    }
+
+    public void testMaxColorCount32() {
+        testMaxColorCount(32);
+    }
+
+    public void testMaxColorCount1() {
+        testMaxColorCount(1);
+    }
+
+    public void testMaxColorCount15() {
+        testMaxColorCount(15);
+    }
+
+    private void testMaxColorCount(int colorCount) {
+        Palette newPalette = Palette.from(mSource).maximumColorCount(colorCount).generate();
+        assertTrue(newPalette.getSwatches().size() <= colorCount);
+    }
+}
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/SwatchTests.java b/v7/palette/src/androidTest/java/android/support/v7/graphics/SwatchTests.java
new file mode 100644
index 0000000..396ded9
--- /dev/null
+++ b/v7/palette/src/androidTest/java/android/support/v7/graphics/SwatchTests.java
@@ -0,0 +1,101 @@
+/*
+ * 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.v7.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.support.v4.graphics.ColorUtils;
+import android.test.InstrumentationTestCase;
+
+import static android.support.v4.graphics.ColorUtils.HSLToColor;
+import static android.support.v4.graphics.ColorUtils.calculateContrast;
+
+/**
+ * @hide
+ */
+public class SwatchTests extends InstrumentationTestCase {
+
+    private static final float MIN_CONTRAST_TITLE_TEXT = 3.0f;
+    private static final float MIN_CONTRAST_BODY_TEXT = 4.5f;
+
+    private Bitmap mSource;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mSource = BitmapFactory.decodeResource(getInstrumentation().getContext().getResources(),
+                android.R.drawable.sym_def_app_icon);
+    }
+
+    public void testTextColorContrasts() {
+        Palette p = Palette.from(mSource).generate();
+
+        for (Palette.Swatch swatch : p.getSwatches()) {
+            testSwatchTextColorContrasts(swatch);
+        }
+    }
+
+    public void testHslNotNull() {
+        Palette p = Palette.from(mSource).generate();
+
+        for (Palette.Swatch swatch : p.getSwatches()) {
+            assertNotNull(swatch.getHsl());
+        }
+    }
+
+    public void testHslIsRgb() {
+        Palette p = Palette.from(mSource).generate();
+
+        for (Palette.Swatch swatch : p.getSwatches()) {
+            assertEquals(HSLToColor(swatch.getHsl()), swatch.getRgb());
+        }
+    }
+
+    private void testSwatchTextColorContrasts(Palette.Swatch swatch) {
+        final int bodyTextColor = swatch.getBodyTextColor();
+        assertTrue(calculateContrast(bodyTextColor, swatch.getRgb()) >= MIN_CONTRAST_BODY_TEXT);
+
+        final int titleTextColor = swatch.getTitleTextColor();
+        assertTrue(calculateContrast(titleTextColor, swatch.getRgb()) >= MIN_CONTRAST_TITLE_TEXT);
+    }
+
+    public void testEqualsWhenSame() {
+        Palette.Swatch swatch1 = new Palette.Swatch(Color.WHITE, 50);
+        Palette.Swatch swatch2 = new Palette.Swatch(Color.WHITE, 50);
+        assertEquals(swatch1, swatch2);
+    }
+
+    public void testEqualsWhenColorDifferent() {
+        Palette.Swatch swatch1 = new Palette.Swatch(Color.BLACK, 50);
+        Palette.Swatch swatch2 = new Palette.Swatch(Color.WHITE, 50);
+        assertFalse(swatch1.equals(swatch2));
+    }
+
+    public void testEqualsWhenPopulationDifferent() {
+        Palette.Swatch swatch1 = new Palette.Swatch(Color.BLACK, 50);
+        Palette.Swatch swatch2 = new Palette.Swatch(Color.BLACK, 100);
+        assertFalse(swatch1.equals(swatch2));
+    }
+
+    public void testEqualsWhenDifferent() {
+        Palette.Swatch swatch1 = new Palette.Swatch(Color.BLUE, 50);
+        Palette.Swatch swatch2 = new Palette.Swatch(Color.BLACK, 100);
+        assertFalse(swatch1.equals(swatch2));
+    }
+}
diff --git a/v7/palette/AndroidManifest.xml b/v7/palette/src/main/AndroidManifest.xml
similarity index 100%
rename from v7/palette/AndroidManifest.xml
rename to v7/palette/src/main/AndroidManifest.xml
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java b/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java
new file mode 100644
index 0000000..557bb66
--- /dev/null
+++ b/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright 2014 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.v7.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v7.graphics.Palette.Swatch;
+import android.util.TimingLogger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.PriorityQueue;
+
+/**
+ * An color quantizer based on the Median-cut algorithm, but optimized for picking out distinct
+ * colors rather than representation colors.
+ *
+ * The color space is represented as a 3-dimensional cube with each dimension being an RGB
+ * component. The cube is then repeatedly divided until we have reduced the color space to the
+ * requested number of colors. An average color is then generated from each cube.
+ *
+ * What makes this different to median-cut is that median-cut divided cubes so that all of the cubes
+ * have roughly the same population, where this quantizer divides boxes based on their color volume.
+ * This means that the color space is divided into distinct colors, rather than representative
+ * colors.
+ */
+final class ColorCutQuantizer {
+
+    private static final String LOG_TAG = "ColorCutQuantizer";
+    private static final boolean LOG_TIMINGS = false;
+
+    private static final float BLACK_MAX_LIGHTNESS = 0.05f;
+    private static final float WHITE_MIN_LIGHTNESS = 0.95f;
+
+    private static final int COMPONENT_RED = -3;
+    private static final int COMPONENT_GREEN = -2;
+    private static final int COMPONENT_BLUE = -1;
+
+    private static final int QUANTIZE_WORD_WIDTH = 5;
+    private static final int QUANTIZE_WORD_MASK = (1 << QUANTIZE_WORD_WIDTH) - 1;
+
+    final int[] mColors;
+    final int[] mHistogram;
+    final List<Swatch> mQuantizedColors;
+    final TimingLogger mTimingLogger;
+
+    private final float[] mTempHsl = new float[3];
+
+    /**
+     * Factory-method to generate a {@link ColorCutQuantizer} from a {@link Bitmap} object.
+     *
+     * @param bitmap Bitmap to extract the pixel data from
+     * @param maxColors The maximum number of colors that should be in the result palette.
+     */
+    static ColorCutQuantizer fromBitmap(Bitmap bitmap, int maxColors) {
+        final int width = bitmap.getWidth();
+        final int height = bitmap.getHeight();
+
+        final int[] pixels = new int[width * height];
+        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+
+        return new ColorCutQuantizer(pixels, maxColors);
+    }
+
+    /**
+     * Private constructor.
+     *
+     * @param pixels histogram representing an image's pixel data
+     * @param maxColors The maximum number of colors that should be in the result palette.
+     */
+    private ColorCutQuantizer(final int[] pixels, final int maxColors) {
+        mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null;
+
+        final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)];
+        for (int i = 0; i < pixels.length; i++) {
+            final int quantizedColor = quantizeFromRgb888(pixels[i]);
+            // Now update the pixel value to the quantized value
+            pixels[i] = quantizedColor;
+            // And update the histogram
+            hist[quantizedColor]++;
+        }
+
+        if (LOG_TIMINGS) {
+            mTimingLogger.addSplit("Histogram created");
+        }
+
+        // Now let's count the number of distinct colors
+        int distinctColorCount = 0;
+        for (int color = 0; color < hist.length; color++) {
+            if (hist[color] > 0 && shouldIgnoreColor(color)) {
+                // If we should ignore the color, set the population to 0
+                hist[color] = 0;
+            }
+            if (hist[color] > 0) {
+                // If the color has population, increase the distinct color count
+                distinctColorCount++;
+            }
+        }
+
+        if (LOG_TIMINGS) {
+            mTimingLogger.addSplit("Filtered colors and distinct colors counted");
+        }
+
+        // Now lets go through create an array consisting of only distinct colors
+        final int[] colors = mColors = new int[distinctColorCount];
+        int distinctColorIndex = 0;
+        for (int color = 0; color < hist.length; color++) {
+            if (hist[color] > 0) {
+                colors[distinctColorIndex++] = color;
+            }
+        }
+
+        if (LOG_TIMINGS) {
+            mTimingLogger.addSplit("Distinct colors copied into array");
+        }
+
+        if (distinctColorCount <= maxColors) {
+            // The image has fewer colors than the maximum requested, so just return the colors
+            mQuantizedColors = new ArrayList<>();
+            for (int color : colors) {
+                mQuantizedColors.add(new Swatch(approximateToRgb888(color), hist[color]));
+            }
+
+            if (LOG_TIMINGS) {
+                mTimingLogger.addSplit("Too few colors present. Copied to Swatches");
+                mTimingLogger.dumpToLog();
+            }
+        } else {
+            // We need use quantization to reduce the number of colors
+            mQuantizedColors = quantizePixels(maxColors);
+
+            if (LOG_TIMINGS) {
+                mTimingLogger.addSplit("Quantized colors computed");
+                mTimingLogger.dumpToLog();
+            }
+        }
+    }
+
+    /**
+     * @return the list of quantized colors
+     */
+    List<Swatch> getQuantizedColors() {
+        return mQuantizedColors;
+    }
+
+    private List<Swatch> quantizePixels(int maxColors) {
+        // Create the priority queue which is sorted by volume descending. This means we always
+        // split the largest box in the queue
+        final PriorityQueue<Vbox> pq = new PriorityQueue<>(maxColors, VBOX_COMPARATOR_VOLUME);
+
+        // To start, offer a box which contains all of the colors
+        pq.offer(new Vbox(0, mColors.length - 1));
+
+        // Now go through the boxes, splitting them until we have reached maxColors or there are no
+        // more boxes to split
+        splitBoxes(pq, maxColors);
+
+        // Finally, return the average colors of the color boxes
+        return generateAverageColors(pq);
+    }
+
+    /**
+     * Iterate through the {@link java.util.Queue}, popping
+     * {@link ColorCutQuantizer.Vbox} objects from the queue
+     * and splitting them. Once split, the new box and the remaining box are offered back to the
+     * queue.
+     *
+     * @param queue {@link java.util.PriorityQueue} to poll for boxes
+     * @param maxSize Maximum amount of boxes to split
+     */
+    private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) {
+        while (queue.size() < maxSize) {
+            final Vbox vbox = queue.poll();
+
+            if (vbox != null && vbox.canSplit()) {
+                // First split the box, and offer the result
+                queue.offer(vbox.splitBox());
+
+                if (LOG_TIMINGS) {
+                    mTimingLogger.addSplit("Box split");
+                }
+                // Then offer the box back
+                queue.offer(vbox);
+            } else {
+                if (LOG_TIMINGS) {
+                    mTimingLogger.addSplit("All boxes split");
+                }
+                // If we get here then there are no more boxes to split, so return
+                return;
+            }
+        }
+    }
+
+    private List<Swatch> generateAverageColors(Collection<Vbox> vboxes) {
+        ArrayList<Swatch> colors = new ArrayList<>(vboxes.size());
+        for (Vbox vbox : vboxes) {
+            Swatch swatch = vbox.getAverageColor();
+            if (!shouldIgnoreColor(swatch)) {
+                // As we're averaging a color box, we can still get colors which we do not want, so
+                // we check again here
+                colors.add(swatch);
+            }
+        }
+        return colors;
+    }
+
+    /**
+     * Represents a tightly fitting box around a color space.
+     */
+    private class Vbox {
+        // lower and upper index are inclusive
+        private int mLowerIndex;
+        private int mUpperIndex;
+        // Population of colors within this box
+        private int mPopulation;
+
+        private int mMinRed, mMaxRed;
+        private int mMinGreen, mMaxGreen;
+        private int mMinBlue, mMaxBlue;
+
+        Vbox(int lowerIndex, int upperIndex) {
+            mLowerIndex = lowerIndex;
+            mUpperIndex = upperIndex;
+            fitBox();
+        }
+
+        final int getVolume() {
+            return (mMaxRed - mMinRed + 1) * (mMaxGreen - mMinGreen + 1) *
+                    (mMaxBlue - mMinBlue + 1);
+        }
+
+        final boolean canSplit() {
+            return getColorCount() > 1;
+        }
+
+        final int getColorCount() {
+            return 1 + mUpperIndex - mLowerIndex;
+        }
+
+        /**
+         * Recomputes the boundaries of this box to tightly fit the colors within the box.
+         */
+        final void fitBox() {
+            final int[] colors = mColors;
+            final int[] hist = mHistogram;
+
+            // Reset the min and max to opposite values
+            int minRed, minGreen, minBlue;
+            minRed = minGreen = minBlue = Integer.MAX_VALUE;
+            int maxRed, maxGreen, maxBlue;
+            maxRed = maxGreen = maxBlue = Integer.MIN_VALUE;
+            int count = 0;
+
+            for (int i = mLowerIndex; i <= mUpperIndex; i++) {
+                final int color = colors[i];
+                count += hist[color];
+
+                final int r = quantizedRed(color);
+                final int g = quantizedGreen(color);
+                final int b = quantizedBlue(color);
+                if (r > maxRed) {
+                    maxRed = r;
+                }
+                if (r < minRed) {
+                    minRed = r;
+                }
+                if (g > maxGreen) {
+                    maxGreen = g;
+                }
+                if (g < minGreen) {
+                    minGreen = g;
+                }
+                if (b > maxBlue) {
+                    maxBlue = b;
+                }
+                if (b < minBlue) {
+                    minBlue = b;
+                }
+            }
+
+            mMinRed = minRed;
+            mMaxRed = maxRed;
+            mMinGreen = minGreen;
+            mMaxGreen = maxGreen;
+            mMinBlue = minBlue;
+            mMaxBlue = maxBlue;
+            mPopulation = count;
+        }
+
+        /**
+         * Split this color box at the mid-point along it's longest dimension
+         *
+         * @return the new ColorBox
+         */
+        final Vbox splitBox() {
+            if (!canSplit()) {
+                throw new IllegalStateException("Can not split a box with only 1 color");
+            }
+
+            // find median along the longest dimension
+            final int splitPoint = findSplitPoint();
+
+            Vbox newBox = new Vbox(splitPoint + 1, mUpperIndex);
+
+            // Now change this box's upperIndex and recompute the color boundaries
+            mUpperIndex = splitPoint;
+            fitBox();
+
+            return newBox;
+        }
+
+        /**
+         * @return the dimension which this box is largest in
+         */
+        final int getLongestColorDimension() {
+            final int redLength = mMaxRed - mMinRed;
+            final int greenLength = mMaxGreen - mMinGreen;
+            final int blueLength = mMaxBlue - mMinBlue;
+
+            if (redLength >= greenLength && redLength >= blueLength) {
+                return COMPONENT_RED;
+            } else if (greenLength >= redLength && greenLength >= blueLength) {
+                return COMPONENT_GREEN;
+            } else {
+                return COMPONENT_BLUE;
+            }
+        }
+
+        /**
+         * Finds the point within this box's lowerIndex and upperIndex index of where to split.
+         *
+         * This is calculated by finding the longest color dimension, and then sorting the
+         * sub-array based on that dimension value in each color. The colors are then iterated over
+         * until a color is found with at least the midpoint of the whole box's dimension midpoint.
+         *
+         * @return the index of the colors array to split from
+         */
+        final int findSplitPoint() {
+            final int longestDimension = getLongestColorDimension();
+            final int[] colors = mColors;
+            final int[] hist = mHistogram;
+
+            // We need to sort the colors in this box based on the longest color dimension.
+            // As we can't use a Comparator to define the sort logic, we modify each color so that
+            // it's most significant is the desired dimension
+            modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex);
+
+            // Now sort... Arrays.sort uses a exclusive toIndex so we need to add 1
+            Arrays.sort(colors, mLowerIndex, mUpperIndex + 1);
+
+            // Now revert all of the colors so that they are packed as RGB again
+            modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex);
+
+            final int midPoint = mPopulation / 2;
+            for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++)  {
+                count += hist[colors[i]];
+                if (count >= midPoint) {
+                    return i;
+                }
+            }
+
+            return mLowerIndex;
+        }
+
+        /**
+         * @return the average color of this box.
+         */
+        final Swatch getAverageColor() {
+            final int[] colors = mColors;
+            final int[] hist = mHistogram;
+            int redSum = 0;
+            int greenSum = 0;
+            int blueSum = 0;
+            int totalPopulation = 0;
+
+            for (int i = mLowerIndex; i <= mUpperIndex; i++) {
+                final int color = colors[i];
+                final int colorPopulation = hist[color];
+
+                totalPopulation += colorPopulation;
+                redSum += colorPopulation * quantizedRed(color);
+                greenSum += colorPopulation * quantizedGreen(color);
+                blueSum += colorPopulation * quantizedBlue(color);
+            }
+
+            final int redMean = Math.round(redSum / (float) totalPopulation);
+            final int greenMean = Math.round(greenSum / (float) totalPopulation);
+            final int blueMean = Math.round(blueSum / (float) totalPopulation);
+
+            return new Swatch(approximateToRgb888(redMean, greenMean, blueMean), totalPopulation);
+        }
+    }
+
+    /**
+     * Modify the significant octet in a packed color int. Allows sorting based on the value of a
+     * single color component. This relies on all components being the same word size.
+     *
+     * @see Vbox#findSplitPoint()
+     */
+    private static void modifySignificantOctet(final int[] a, final int dimension,
+            final int lower, final int upper) {
+        switch (dimension) {
+            case COMPONENT_RED:
+                // Already in RGB, no need to do anything
+                break;
+            case COMPONENT_GREEN:
+                // We need to do a RGB to GRB swap, or vice-versa
+                for (int i = lower; i <= upper; i++) {
+                    final int color = a[i];
+                    a[i] = quantizedGreen(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)
+                            | quantizedRed(color) << QUANTIZE_WORD_WIDTH
+                            | quantizedBlue(color);
+                }
+                break;
+            case COMPONENT_BLUE:
+                // We need to do a RGB to BGR swap, or vice-versa
+                for (int i = lower; i <= upper; i++) {
+                    final int color = a[i];
+                    a[i] = quantizedBlue(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)
+                            | quantizedGreen(color) << QUANTIZE_WORD_WIDTH
+                            | quantizedRed(color);
+                }
+                break;
+        }
+    }
+
+    private boolean shouldIgnoreColor(int color565) {
+        ColorUtils.colorToHSL(approximateToRgb888(color565), mTempHsl);
+        return shouldIgnoreColor(mTempHsl);
+    }
+
+    private static boolean shouldIgnoreColor(Swatch color) {
+        return shouldIgnoreColor(color.getHsl());
+    }
+
+    private static boolean shouldIgnoreColor(float[] hslColor) {
+        return isWhite(hslColor) || isBlack(hslColor) || isNearRedILine(hslColor);
+    }
+
+    /**
+     * @return true if the color represents a color which is close to black.
+     */
+    private static boolean isBlack(float[] hslColor) {
+        return hslColor[2] <= BLACK_MAX_LIGHTNESS;
+    }
+
+    /**
+     * @return true if the color represents a color which is close to white.
+     */
+    private static boolean isWhite(float[] hslColor) {
+        return hslColor[2] >= WHITE_MIN_LIGHTNESS;
+    }
+
+    /**
+     * @return true if the color lies close to the red side of the I line.
+     */
+    private static boolean isNearRedILine(float[] hslColor) {
+        return hslColor[0] >= 10f && hslColor[0] <= 37f && hslColor[1] <= 0.82f;
+    }
+
+    /**
+     * Comparator which sorts {@link Vbox} instances based on their volume, in descending order
+     */
+    private static final Comparator<Vbox> VBOX_COMPARATOR_VOLUME = new Comparator<Vbox>() {
+        @Override
+        public int compare(Vbox lhs, Vbox rhs) {
+            return rhs.getVolume() - lhs.getVolume();
+        }
+    };
+
+    /**
+     * Quantized a RGB888 value to have a word width of {@value #QUANTIZE_WORD_WIDTH}.
+     */
+    private static int quantizeFromRgb888(int color) {
+        int r = modifyWordWidth(Color.red(color), 8, QUANTIZE_WORD_WIDTH);
+        int g = modifyWordWidth(Color.green(color), 8, QUANTIZE_WORD_WIDTH);
+        int b = modifyWordWidth(Color.blue(color), 8, QUANTIZE_WORD_WIDTH);
+        return r << (QUANTIZE_WORD_WIDTH * 2) | g << QUANTIZE_WORD_WIDTH | b;
+    }
+
+    /**
+     * Quantized RGB888 values to have a word width of {@value #QUANTIZE_WORD_WIDTH}.
+     */
+    private static int approximateToRgb888(int r, int g, int b) {
+        return Color.rgb(modifyWordWidth(r, QUANTIZE_WORD_WIDTH, 8),
+                modifyWordWidth(g, QUANTIZE_WORD_WIDTH, 8),
+                modifyWordWidth(b, QUANTIZE_WORD_WIDTH, 8));
+    }
+
+    private static int approximateToRgb888(int color) {
+        return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color));
+    }
+
+    /**
+     * @return red component of the quantized color
+     */
+    private static int quantizedRed(int color) {
+        return (color >> (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) & QUANTIZE_WORD_MASK;
+    }
+
+    /**
+     * @return green component of a quantized color
+     */
+    private static int quantizedGreen(int color) {
+        return (color >> QUANTIZE_WORD_WIDTH) & QUANTIZE_WORD_MASK;
+    }
+
+    /**
+     * @return blue component of a quantized color
+     */
+    private static int quantizedBlue(int color) {
+        return color & QUANTIZE_WORD_MASK;
+    }
+
+    private static int modifyWordWidth(int value, int currentWidth, int targetWidth) {
+        final int newValue;
+        if (targetWidth > currentWidth) {
+            // If we're approximating up in word width, we'll use scaling to approximate the
+            // new value
+            newValue = value * ((1 << targetWidth) - 1) / ((1 << currentWidth) - 1);
+        } else {
+            // Else, we will just shift and keep the MSB
+            newValue = value >> (currentWidth - targetWidth);
+        }
+        return newValue & ((1 << targetWidth) - 1);
+    }
+
+}
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/DefaultGenerator.java b/v7/palette/src/main/java/android/support/v7/graphics/DefaultGenerator.java
new file mode 100644
index 0000000..3ee2bfa
--- /dev/null
+++ b/v7/palette/src/main/java/android/support/v7/graphics/DefaultGenerator.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 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.v7.graphics;
+
+import android.support.v4.graphics.ColorUtils;
+import android.support.v7.graphics.Palette.Swatch;
+
+import java.util.List;
+
+class DefaultGenerator extends Palette.Generator {
+
+    private static final float TARGET_DARK_LUMA = 0.26f;
+    private static final float MAX_DARK_LUMA = 0.45f;
+
+    private static final float MIN_LIGHT_LUMA = 0.55f;
+    private static final float TARGET_LIGHT_LUMA = 0.74f;
+
+    private static final float MIN_NORMAL_LUMA = 0.3f;
+    private static final float TARGET_NORMAL_LUMA = 0.5f;
+    private static final float MAX_NORMAL_LUMA = 0.7f;
+
+    private static final float TARGET_MUTED_SATURATION = 0.3f;
+    private static final float MAX_MUTED_SATURATION = 0.4f;
+
+    private static final float TARGET_VIBRANT_SATURATION = 1f;
+    private static final float MIN_VIBRANT_SATURATION = 0.35f;
+
+    private static final float WEIGHT_SATURATION = 3f;
+    private static final float WEIGHT_LUMA = 6f;
+    private static final float WEIGHT_POPULATION = 1f;
+
+    private List<Swatch> mSwatches;
+
+    private int mHighestPopulation;
+
+    private Swatch mVibrantSwatch;
+    private Swatch mMutedSwatch;
+    private Swatch mDarkVibrantSwatch;
+    private Swatch mDarkMutedSwatch;
+    private Swatch mLightVibrantSwatch;
+    private Swatch mLightMutedSwatch;
+
+    @Override
+    public void generate(final List<Swatch> swatches) {
+        mSwatches = swatches;
+
+        mHighestPopulation = findMaxPopulation();
+
+        generateVariationColors();
+
+        // Now try and generate any missing colors
+        generateEmptySwatches();
+    }
+
+    @Override
+    public Swatch getVibrantSwatch() {
+        return mVibrantSwatch;
+    }
+
+    @Override
+    public Swatch getLightVibrantSwatch() {
+        return mLightVibrantSwatch;
+    }
+
+    @Override
+    public Swatch getDarkVibrantSwatch() {
+        return mDarkVibrantSwatch;
+    }
+
+    @Override
+    public Swatch getMutedSwatch() {
+        return mMutedSwatch;
+    }
+
+    @Override
+    public Swatch getLightMutedSwatch() {
+        return mLightMutedSwatch;
+    }
+
+    @Override
+    public Swatch getDarkMutedSwatch() {
+        return mDarkMutedSwatch;
+    }
+
+    private void generateVariationColors() {
+        mVibrantSwatch = findColorVariation(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,
+                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
+
+        mLightVibrantSwatch = findColorVariation(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,
+                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
+
+        mDarkVibrantSwatch = findColorVariation(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,
+                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
+
+        mMutedSwatch = findColorVariation(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,
+                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
+
+        mLightMutedSwatch = findColorVariation(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,
+                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
+
+        mDarkMutedSwatch = findColorVariation(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,
+                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
+    }
+
+    /**
+     * Try and generate any missing swatches from the swatches we did find.
+     */
+    private void generateEmptySwatches() {
+        if (mVibrantSwatch == null) {
+            // If we do not have a vibrant color...
+            if (mDarkVibrantSwatch != null) {
+                // ...but we do have a dark vibrant, generate the value by modifying the luma
+                final float[] newHsl = copyHslValues(mDarkVibrantSwatch);
+                newHsl[2] = TARGET_NORMAL_LUMA;
+                mVibrantSwatch = new Swatch(ColorUtils.HSLToColor(newHsl), 0);
+            }
+        }
+
+        if (mDarkVibrantSwatch == null) {
+            // If we do not have a dark vibrant color...
+            if (mVibrantSwatch != null) {
+                // ...but we do have a vibrant, generate the value by modifying the luma
+                final float[] newHsl = copyHslValues(mVibrantSwatch);
+                newHsl[2] = TARGET_DARK_LUMA;
+                mDarkVibrantSwatch = new Swatch(ColorUtils.HSLToColor(newHsl), 0);
+            }
+        }
+    }
+
+    /**
+     * Find the {@link Palette.Swatch} with the highest population value and return the population.
+     */
+    private int findMaxPopulation() {
+        int population = 0;
+        for (Swatch swatch : mSwatches) {
+            population = Math.max(population, swatch.getPopulation());
+        }
+        return population;
+    }
+
+    private Swatch findColorVariation(float targetLuma, float minLuma, float maxLuma,
+            float targetSaturation, float minSaturation, float maxSaturation) {
+        Swatch max = null;
+        float maxValue = 0f;
+
+        for (Swatch swatch : mSwatches) {
+            final float sat = swatch.getHsl()[1];
+            final float luma = swatch.getHsl()[2];
+
+            if (sat >= minSaturation && sat <= maxSaturation &&
+                    luma >= minLuma && luma <= maxLuma &&
+                    !isAlreadySelected(swatch)) {
+                float value = createComparisonValue(sat, targetSaturation, luma, targetLuma,
+                        swatch.getPopulation(), mHighestPopulation);
+                if (max == null || value > maxValue) {
+                    max = swatch;
+                    maxValue = value;
+                }
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * @return true if we have already selected {@code swatch}
+     */
+    private boolean isAlreadySelected(Swatch swatch) {
+        return mVibrantSwatch == swatch || mDarkVibrantSwatch == swatch ||
+                mLightVibrantSwatch == swatch || mMutedSwatch == swatch ||
+                mDarkMutedSwatch == swatch || mLightMutedSwatch == swatch;
+    }
+
+    private static float createComparisonValue(float saturation, float targetSaturation,
+            float luma, float targetLuma,
+            int population, int maxPopulation) {
+        return createComparisonValue(saturation, targetSaturation, WEIGHT_SATURATION,
+                luma, targetLuma, WEIGHT_LUMA,
+                population, maxPopulation, WEIGHT_POPULATION);
+    }
+
+    private static float createComparisonValue(
+            float saturation, float targetSaturation, float saturationWeight,
+            float luma, float targetLuma, float lumaWeight,
+            int population, int maxPopulation, float populationWeight) {
+        return weightedMean(
+                invertDiff(saturation, targetSaturation), saturationWeight,
+                invertDiff(luma, targetLuma), lumaWeight,
+                population / (float) maxPopulation, populationWeight
+        );
+    }
+
+    /**
+     * Copy a {@link Swatch}'s HSL values into a new float[].
+     */
+    private static float[] copyHslValues(Swatch color) {
+        final float[] newHsl = new float[3];
+        System.arraycopy(color.getHsl(), 0, newHsl, 0, 3);
+        return newHsl;
+    }
+
+    /**
+     * Returns a value in the range 0-1. 1 is returned when {@code value} equals the
+     * {@code targetValue} and then decreases as the absolute difference between {@code value} and
+     * {@code targetValue} increases.
+     *
+     * @param value the item's value
+     * @param targetValue the value which we desire
+     */
+    private static float invertDiff(float value, float targetValue) {
+        return 1f - Math.abs(value - targetValue);
+    }
+
+    private static float weightedMean(float... values) {
+        float sum = 0f;
+        float sumWeight = 0f;
+
+        for (int i = 0; i < values.length; i += 2) {
+            float value = values[i];
+            float weight = values[i + 1];
+
+            sum += (value * weight);
+            sumWeight += weight;
+        }
+
+        return sum / sumWeight;
+    }
+}
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
new file mode 100644
index 0000000..5b83d1a
--- /dev/null
+++ b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
@@ -0,0 +1,650 @@
+/*
+ * Copyright 2014 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.v7.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.os.AsyncTask;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v4.os.AsyncTaskCompat;
+import android.util.TimingLogger;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A helper class to extract prominent colors from an image.
+ * <p>
+ * A number of colors with different profiles are extracted from the image:
+ * <ul>
+ *     <li>Vibrant</li>
+ *     <li>Vibrant Dark</li>
+ *     <li>Vibrant Light</li>
+ *     <li>Muted</li>
+ *     <li>Muted Dark</li>
+ *     <li>Muted Light</li>
+ * </ul>
+ * These can be retrieved from the appropriate getter method.
+ *
+ * <p>
+ * Instances are created with a {@link Builder} which supports several options to tweak the
+ * generated Palette. See that class' documentation for more information.
+ * <p>
+ * Generation should always be completed on a background thread, ideally the one in
+ * which you load your image on. {@link Builder} supports both synchronous and asynchronous
+ * generation:
+ *
+ * <pre>
+ * // Synchronous
+ * Palette p = Palette.from(bitmap).generate();
+ *
+ * // Asynchronous
+ * Palette.from(bitmap).generate(new PaletteAsyncListener() {
+ *     public void onGenerated(Palette p) {
+ *         // Use generated instance
+ *     }
+ * });
+ * </pre>
+ */
+public final class Palette {
+
+    /**
+     * Listener to be used with {@link #generateAsync(Bitmap, PaletteAsyncListener)} or
+     * {@link #generateAsync(Bitmap, int, PaletteAsyncListener)}
+     */
+    public interface PaletteAsyncListener {
+
+        /**
+         * Called when the {@link Palette} has been generated.
+         */
+        void onGenerated(Palette palette);
+    }
+
+    private static final int DEFAULT_RESIZE_BITMAP_MAX_DIMENSION = 192;
+    private static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16;
+
+    private static final float MIN_CONTRAST_TITLE_TEXT = 3.0f;
+    private static final float MIN_CONTRAST_BODY_TEXT = 4.5f;
+
+    private static final String LOG_TAG = "Palette";
+    private static final boolean LOG_TIMINGS = false;
+
+    /**
+     * Start generating a {@link Palette} with the returned {@link Builder} instance.
+     */
+    public static Builder from(Bitmap bitmap) {
+        return new Builder(bitmap);
+    }
+
+    /**
+     * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches.
+     * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a
+     * list of swatches. Will return null if the {@code swatches} is null.
+     */
+    public static Palette from(List<Swatch> swatches) {
+        return new Builder(swatches).generate();
+    }
+
+    /**
+     * @deprecated Use {@link Builder} to generate the Palette.
+     */
+    @Deprecated
+    public static Palette generate(Bitmap bitmap) {
+        return from(bitmap).generate();
+    }
+
+    /**
+     * @deprecated Use {@link Builder} to generate the Palette.
+     */
+    @Deprecated
+    public static Palette generate(Bitmap bitmap, int numColors) {
+        return from(bitmap).maximumColorCount(numColors).generate();
+    }
+
+    /**
+     * @deprecated Use {@link Builder} to generate the Palette.
+     */
+    @Deprecated
+    public static AsyncTask<Bitmap, Void, Palette> generateAsync(
+            Bitmap bitmap, PaletteAsyncListener listener) {
+        return from(bitmap).generate(listener);
+    }
+
+    /**
+     * @deprecated Use {@link Builder} to generate the Palette.
+     */
+    @Deprecated
+    public static AsyncTask<Bitmap, Void, Palette> generateAsync(
+            final Bitmap bitmap, final int numColors, final PaletteAsyncListener listener) {
+        return from(bitmap).maximumColorCount(numColors).generate(listener);
+    }
+
+    private final List<Swatch> mSwatches;
+    private final Generator mGenerator;
+
+    private Palette(List<Swatch> swatches, Generator generator) {
+        mSwatches = swatches;
+        mGenerator = generator;
+    }
+
+    /**
+     * Returns all of the swatches which make up the palette.
+     */
+    public List<Swatch> getSwatches() {
+        return Collections.unmodifiableList(mSwatches);
+    }
+
+    /**
+     * Returns the most vibrant swatch in the palette. Might be null.
+     */
+    public Swatch getVibrantSwatch() {
+        return mGenerator.getVibrantSwatch();
+    }
+
+    /**
+     * Returns a light and vibrant swatch from the palette. Might be null.
+     */
+    public Swatch getLightVibrantSwatch() {
+        return mGenerator.getLightVibrantSwatch();
+    }
+
+    /**
+     * Returns a dark and vibrant swatch from the palette. Might be null.
+     */
+    public Swatch getDarkVibrantSwatch() {
+        return mGenerator.getDarkVibrantSwatch();
+    }
+
+    /**
+     * Returns a muted swatch from the palette. Might be null.
+     */
+    public Swatch getMutedSwatch() {
+        return mGenerator.getMutedSwatch();
+    }
+
+    /**
+     * Returns a muted and light swatch from the palette. Might be null.
+     */
+    public Swatch getLightMutedSwatch() {
+        return mGenerator.getLightMutedSwatch();
+    }
+
+    /**
+     * Returns a muted and dark swatch from the palette. Might be null.
+     */
+    public Swatch getDarkMutedSwatch() {
+        return mGenerator.getDarkMutedSwatch();
+    }
+
+    /**
+     * Returns the most vibrant color in the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     */
+    public int getVibrantColor(int defaultColor) {
+        Swatch swatch = getVibrantSwatch();
+        return swatch != null ? swatch.getRgb() : defaultColor;
+    }
+
+    /**
+     * Returns a light and vibrant color from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     */
+    public int getLightVibrantColor(int defaultColor) {
+        Swatch swatch = getLightVibrantSwatch();
+        return swatch != null ? swatch.getRgb() : defaultColor;
+    }
+
+    /**
+     * Returns a dark and vibrant color from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     */
+    public int getDarkVibrantColor(int defaultColor) {
+        Swatch swatch = getDarkVibrantSwatch();
+        return swatch != null ? swatch.getRgb() : defaultColor;
+    }
+
+    /**
+     * Returns a muted color from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     */
+    public int getMutedColor(int defaultColor) {
+        Swatch swatch = getMutedSwatch();
+        return swatch != null ? swatch.getRgb() : defaultColor;
+    }
+
+    /**
+     * Returns a muted and light color from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     */
+    public int getLightMutedColor(int defaultColor) {
+        Swatch swatch = getLightMutedSwatch();
+        return swatch != null ? swatch.getRgb() : defaultColor;
+    }
+
+    /**
+     * Returns a muted and dark color from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     */
+    public int getDarkMutedColor(int defaultColor) {
+        Swatch swatch = getDarkMutedSwatch();
+        return swatch != null ? swatch.getRgb() : defaultColor;
+    }
+
+    /**
+     * Scale the bitmap down so that it's largest dimension is {@code targetMaxDimension}.
+     * If {@code bitmap} is smaller than this, then it is returned.
+     */
+    private static Bitmap scaleBitmapDown(Bitmap bitmap, final int targetMaxDimension) {
+        final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
+
+        if (maxDimension <= targetMaxDimension) {
+            // If the bitmap is small enough already, just return it
+            return bitmap;
+        }
+
+        final float scaleRatio = targetMaxDimension / (float) maxDimension;
+        return Bitmap.createScaledBitmap(bitmap,
+                Math.round(bitmap.getWidth() * scaleRatio),
+                Math.round(bitmap.getHeight() * scaleRatio),
+                false);
+    }
+
+    /**
+     * Represents a color swatch generated from an image's palette. The RGB color can be retrieved
+     * by calling {@link #getRgb()}.
+     */
+    public static final class Swatch {
+        private final int mRed, mGreen, mBlue;
+        private final int mRgb;
+        private final int mPopulation;
+
+        private boolean mGeneratedTextColors;
+        private int mTitleTextColor;
+        private int mBodyTextColor;
+
+        private float[] mHsl;
+
+        public Swatch(int color, int population) {
+            mRed = Color.red(color);
+            mGreen = Color.green(color);
+            mBlue = Color.blue(color);
+            mRgb = color;
+            mPopulation = population;
+        }
+
+        Swatch(int red, int green, int blue, int population) {
+            mRed = red;
+            mGreen = green;
+            mBlue = blue;
+            mRgb = Color.rgb(red, green, blue);
+            mPopulation = population;
+        }
+
+        /**
+         * @return this swatch's RGB color value
+         */
+        public int getRgb() {
+            return mRgb;
+        }
+
+        /**
+         * Return this swatch's HSL values.
+         *     hsv[0] is Hue [0 .. 360)
+         *     hsv[1] is Saturation [0...1]
+         *     hsv[2] is Lightness [0...1]
+         */
+        public float[] getHsl() {
+            if (mHsl == null) {
+                mHsl = new float[3];
+                ColorUtils.RGBToHSL(mRed, mGreen, mBlue, mHsl);
+            }
+            return mHsl;
+        }
+
+        /**
+         * @return the number of pixels represented by this swatch
+         */
+        public int getPopulation() {
+            return mPopulation;
+        }
+
+        /**
+         * Returns an appropriate color to use for any 'title' text which is displayed over this
+         * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
+         */
+        public int getTitleTextColor() {
+            ensureTextColorsGenerated();
+            return mTitleTextColor;
+        }
+
+        /**
+         * Returns an appropriate color to use for any 'body' text which is displayed over this
+         * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
+         */
+        public int getBodyTextColor() {
+            ensureTextColorsGenerated();
+            return mBodyTextColor;
+        }
+
+        private void ensureTextColorsGenerated() {
+            if (!mGeneratedTextColors) {
+                // First check white, as most colors will be dark
+                final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha(
+                        Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT);
+                final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha(
+                        Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT);
+
+                if (lightBodyAlpha != -1 && lightTitleAlpha != -1) {
+                    // If we found valid light values, use them and return
+                    mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha);
+                    mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha);
+                    mGeneratedTextColors = true;
+                    return;
+                }
+
+                final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha(
+                        Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT);
+                final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha(
+                        Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT);
+
+                if (darkBodyAlpha != -1 && darkBodyAlpha != -1) {
+                    // If we found valid dark values, use them and return
+                    mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
+                    mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
+                    mGeneratedTextColors = true;
+                    return;
+                }
+
+                // If we reach here then we can not find title and body values which use the same
+                // lightness, we need to use mismatched values
+                mBodyTextColor = lightBodyAlpha != -1
+                        ? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha)
+                        : ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
+                mTitleTextColor = lightTitleAlpha != -1
+                        ? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha)
+                        : ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
+                mGeneratedTextColors = true;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder(getClass().getSimpleName())
+                    .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']')
+                    .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']')
+                    .append(" [Population: ").append(mPopulation).append(']')
+                    .append(" [Title Text: #").append(Integer.toHexString(getTitleTextColor()))
+                    .append(']')
+                    .append(" [Body Text: #").append(Integer.toHexString(getBodyTextColor()))
+                    .append(']').toString();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            Swatch swatch = (Swatch) o;
+            return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb;
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * mRgb + mPopulation;
+        }
+    }
+
+    /**
+     * Builder class for generating {@link Palette} instances.
+     */
+    public static final class Builder {
+        private List<Swatch> mSwatches;
+        private Bitmap mBitmap;
+        private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS;
+        private int mResizeMaxDimension = DEFAULT_RESIZE_BITMAP_MAX_DIMENSION;
+
+        private Generator mGenerator;
+
+        /**
+         * Construct a new {@link Builder} using a source {@link Bitmap}
+         */
+        public Builder(Bitmap bitmap) {
+            if (bitmap == null || bitmap.isRecycled()) {
+                throw new IllegalArgumentException("Bitmap is not valid");
+            }
+            mBitmap = bitmap;
+        }
+
+        /**
+         * Construct a new {@link Builder} using a list of {@link Swatch} instances.
+         * Typically only used for testing.
+         */
+        public Builder(List<Swatch> swatches) {
+            if (swatches == null || swatches.isEmpty()) {
+                throw new IllegalArgumentException("List of Swatches is not valid");
+            }
+            mSwatches = swatches;
+        }
+
+        /**
+         * Set the {@link Generator} to use when generating the {@link Palette}. If this is called
+         * with {@code null} then the default generator will be used.
+         */
+        public Builder generator(Generator generator) {
+            mGenerator = generator;
+            return this;
+        }
+
+        /**
+         * Set the maximum number of colors to use in the quantization step when using a
+         * {@link android.graphics.Bitmap} as the source.
+         * <p>
+         * Good values for depend on the source image type. For landscapes, good values are in
+         * the range 10-16. For images which are largely made up of people's faces then this
+         * value should be increased to ~24.
+         */
+        public Builder maximumColorCount(int colors) {
+            mMaxColors = colors;
+            return this;
+        }
+
+        /**
+         * Set the resize value when using a {@link android.graphics.Bitmap} as the source.
+         * If the bitmap's largest dimension is greater than the value specified, then the bitmap
+         * will be resized so that it's largest dimension matches {@code maxDimension}. If the
+         * bitmap is smaller or equal, the original is used as-is.
+         * <p>
+         * This value has a large effect on the processing time. The larger the resized image is,
+         * the greater time it will take to generate the palette. The smaller the image is, the
+         * more detail is lost in the resulting image and thus less precision for color selection.
+         */
+        public Builder resizeBitmapSize(int maxDimension) {
+            mResizeMaxDimension = maxDimension;
+            return this;
+        }
+
+        /**
+         * Generate and return the {@link Palette} synchronously.
+         */
+        public Palette generate() {
+            final TimingLogger logger = LOG_TIMINGS
+                    ? new TimingLogger(LOG_TAG, "Generation")
+                    : null;
+
+            List<Swatch> swatches;
+
+            if (mBitmap != null) {
+                // We have a Bitmap so we need to quantization to reduce the number of colors
+
+                if (mResizeMaxDimension <= 0) {
+                    throw new IllegalArgumentException(
+                            "Minimum dimension size for resizing should should be >= 1");
+                }
+
+                // First we'll scale down the bitmap so it's largest dimension is as specified
+                final Bitmap scaledBitmap = scaleBitmapDown(mBitmap, mResizeMaxDimension);
+
+                if (logger != null) {
+                    logger.addSplit("Processed Bitmap");
+                }
+
+                // Now generate a quantizer from the Bitmap
+                ColorCutQuantizer quantizer = ColorCutQuantizer
+                        .fromBitmap(scaledBitmap, mMaxColors);
+
+                // If created a new bitmap, recycle it
+                if (scaledBitmap != mBitmap) {
+                    scaledBitmap.recycle();
+                }
+                swatches = quantizer.getQuantizedColors();
+
+                if (logger != null) {
+                    logger.addSplit("Color quantization completed");
+                }
+            } else {
+                // Else we're using the provided swatches
+                swatches = mSwatches;
+            }
+
+            // If we haven't been provided with a generator, use the default
+            if (mGenerator == null) {
+                mGenerator = new DefaultGenerator();
+            }
+
+            // Now call let the Generator do it's thing
+            mGenerator.generate(swatches);
+
+            if (logger != null) {
+                logger.addSplit("Generator.generate() completed");
+            }
+
+            // Now create a Palette instance
+            Palette p = new Palette(swatches, mGenerator);
+
+            if (logger != null) {
+                logger.addSplit("Created Palette");
+                logger.dumpToLog();
+            }
+
+            return p;
+        }
+
+        /**
+         * Generate the {@link Palette} asynchronously. The provided listener's
+         * {@link PaletteAsyncListener#onGenerated} method will be called with the palette when
+         * generated.
+         */
+        public AsyncTask<Bitmap, Void, Palette> generate(final PaletteAsyncListener listener) {
+            if (listener == null) {
+                throw new IllegalArgumentException("listener can not be null");
+            }
+
+            return AsyncTaskCompat.executeParallel(
+                    new AsyncTask<Bitmap, Void, Palette>() {
+                        @Override
+                        protected Palette doInBackground(Bitmap... params) {
+                            return generate();
+                        }
+
+                        @Override
+                        protected void onPostExecute(Palette colorExtractor) {
+                            listener.onGenerated(colorExtractor);
+                        }
+                    }, mBitmap);
+        }
+    }
+
+    /**
+     * Extension point for {@link Palette} which allows custom processing of the list of
+     * {@link Palette.Swatch} instances which represent an image.
+     * <p>
+     * You should do as much processing as possible during the
+     * {@link #generate(java.util.List)} method call. The other methods in this class
+     * may be called multiple times to retrieve an appropriate {@link Palette.Swatch}.
+     * <p>
+     * Usage of a custom {@link Generator} is done with {@link Builder#generator(Generator)} as so:
+     * <pre>
+     * Generator customGenerator = ...;
+     * Palette.from(bitmap).generator(customGenerator).generate();
+     * </pre>
+     */
+    public static abstract class Generator {
+
+        /**
+         * This method will be called with the {@link Palette.Swatch} that represent an image.
+         * You should process this list so that you have appropriate values when the other methods in
+         * class are called.
+         * <p>
+         * This method will probably be called on a background thread.
+         */
+        public abstract void generate(List<Palette.Swatch> swatches);
+
+        /**
+         * Return the most vibrant {@link Palette.Swatch}
+         */
+        public Palette.Swatch getVibrantSwatch() {
+            return null;
+        }
+
+        /**
+         * Return a light and vibrant {@link Palette.Swatch}
+         */
+        public Palette.Swatch getLightVibrantSwatch() {
+            return null;
+        }
+
+        /**
+         * Return a dark and vibrant {@link Palette.Swatch}
+         */
+        public Palette.Swatch getDarkVibrantSwatch() {
+            return null;
+        }
+
+        /**
+         * Return a muted {@link Palette.Swatch}
+         */
+        public Palette.Swatch getMutedSwatch() {
+            return null;
+        }
+
+        /**
+         * Return a muted and light {@link Palette.Swatch}
+         */
+        public Palette.Swatch getLightMutedSwatch() {
+            return null;
+        }
+
+        /**
+         * Return a muted and dark {@link Palette.Swatch}
+         */
+        public Palette.Swatch getDarkMutedSwatch() {
+            return null;
+        }
+    }
+
+}
diff --git a/v7/recyclerview/src/android/support/v7/util/SortedList.java b/v7/recyclerview/src/android/support/v7/util/SortedList.java
new file mode 100644
index 0000000..688e032
--- /dev/null
+++ b/v7/recyclerview/src/android/support/v7/util/SortedList.java
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2014 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.v7.util;
+
+import java.lang.reflect.Array;
+
+/**
+ * A Sorted list implementation that can keep items in order and also notify for changes in the
+ * list
+ * such that it can be bound to a {@link android.support.v7.widget.RecyclerView.Adapter
+ * RecyclerView.Adapter}.
+ * <p>
+ * It keeps items ordered using the {@link Callback#compare(Object, Object)} method and uses
+ * binary search to retrieve items. If the sorting criteria of your items may change, make sure you
+ * call appropriate methods while editing them to avoid data inconsistencies.
+ * <p>
+ * You can control the order of items and change notifications via the {@link Callback} parameter.
+ */
+@SuppressWarnings("unchecked")
+public class SortedList<T> {
+
+    /**
+     * Used by {@link #indexOf(Object)} when he item cannot be found in the list.
+     */
+    public static final int INVALID_POSITION = -1;
+
+    private static final int MIN_CAPACITY = 10;
+    private static final int CAPACITY_GROWTH = MIN_CAPACITY;
+    private static final int INSERTION = 1;
+    private static final int DELETION = 1 << 1;
+    private static final int LOOKUP = 1 << 2;
+    T[] mData;
+
+    /**
+     * The callback instance that controls the behavior of the SortedList and get notified when
+     * changes happen.
+     */
+    private Callback mCallback;
+
+    private BatchedCallback mBatchedCallback;
+
+    private int mSize;
+    private final Class<T> mTClass;
+
+    /**
+     * Creates a new SortedList of type T.
+     *
+     * @param klass    The class of the contents of the SortedList.
+     * @param callback The callback that controls the behavior of SortedList.
+     */
+    public SortedList(Class<T> klass, Callback<T> callback) {
+        this(klass, callback, MIN_CAPACITY);
+    }
+
+    /**
+     * Creates a new SortedList of type T.
+     *
+     * @param klass           The class of the contents of the SortedList.
+     * @param callback        The callback that controls the behavior of SortedList.
+     * @param initialCapacity The initial capacity to hold items.
+     */
+    public SortedList(Class<T> klass, Callback<T> callback, int initialCapacity) {
+        mTClass = klass;
+        mData = (T[]) Array.newInstance(klass, initialCapacity);
+        mCallback = callback;
+        mSize = 0;
+    }
+
+    /**
+     * The number of items in the list.
+     *
+     * @return The number of items in the list.
+     */
+    public int size() {
+        return mSize;
+    }
+
+    /**
+     * Adds the given item to the list. If this is a new item, SortedList calls
+     * {@link Callback#onInserted(int, int)}.
+     * <p>
+     * If the item already exists in the list and its sorting criteria is not changed, it is
+     * replaced with the existing Item. SortedList uses
+     * {@link Callback#areItemsTheSame(Object, Object)} to check if two items are the same item
+     * and uses {@link Callback#areContentsTheSame(Object, Object)} to decide whether it should
+     * call {@link Callback#onChanged(int, int)} or not. In both cases, it always removes the
+     * reference to the old item and puts the new item into the backing array even if
+     * {@link Callback#areContentsTheSame(Object, Object)} returns false.
+     * <p>
+     * If the sorting criteria of the item is changed, SortedList won't be able to find
+     * its duplicate in the list which will result in having a duplicate of the Item in the list.
+     * If you need to update sorting criteria of an item that already exists in the list,
+     * use {@link #updateItemAt(int, Object)}. You can find the index of the item using
+     * {@link #indexOf(Object)} before you update the object.
+     *
+     * @param item The item to be added into the list.
+     * @return The index of the newly added item.
+     * @see {@link Callback#compare(Object, Object)}
+     * @see {@link Callback#areItemsTheSame(Object, Object)}
+     * @see {@link Callback#areContentsTheSame(Object, Object)}}
+     */
+    public int add(T item) {
+        return add(item, true);
+    }
+
+    /**
+     * Batches adapter updates that happen between calling this method until calling
+     * {@link #endBatchedUpdates()}. For example, if you add multiple items in a loop
+     * and they are placed into consecutive indices, SortedList calls
+     * {@link Callback#onInserted(int, int)} only once with the proper item count. If an event
+     * cannot be merged with the previous event, the previous event is dispatched
+     * to the callback instantly.
+     * <p>
+     * After running your data updates, you <b>must</b> call {@link #endBatchedUpdates()}
+     * which will dispatch any deferred data change event to the current callback.
+     * <p>
+     * A sample implementation may look like this:
+     * <pre>
+     *     mSortedList.beginBatchedUpdates();
+     *     try {
+     *         mSortedList.add(item1)
+     *         mSortedList.add(item2)
+     *         mSortedList.remove(item3)
+     *         ...
+     *     } finally {
+     *         mSortedList.endBatchedUpdates();
+     *     }
+     * </pre>
+     * <p>
+     * Instead of using this method to batch calls, you can use a Callback that extends
+     * {@link BatchedCallback}. In that case, you must make sure that you are manually calling
+     * {@link BatchedCallback#dispatchLastEvent()} right after you complete your data changes.
+     * Failing to do so may create data inconsistencies with the Callback.
+     * <p>
+     * If the current Callback in an instance of {@link BatchedCallback}, calling this method
+     * has no effect.
+     */
+    public void beginBatchedUpdates() {
+        if (mCallback instanceof BatchedCallback) {
+            return;
+        }
+        if (mBatchedCallback == null) {
+            mBatchedCallback = new BatchedCallback(mCallback);
+        }
+        mCallback = mBatchedCallback;
+    }
+
+    /**
+     * Ends the update transaction and dispatches any remaining event to the callback.
+     */
+    public void endBatchedUpdates() {
+        if (mCallback instanceof BatchedCallback) {
+            ((BatchedCallback) mCallback).dispatchLastEvent();
+        }
+        if (mCallback == mBatchedCallback) {
+            mCallback = mBatchedCallback.mWrappedCallback;
+        }
+    }
+
+    private int add(T item, boolean notify) {
+        int index = findIndexOf(item, INSERTION);
+        if (index == INVALID_POSITION) {
+            index = 0;
+        } else if (index < mSize) {
+            T existing = mData[index];
+            if (mCallback.areItemsTheSame(existing, item)) {
+                if (mCallback.areContentsTheSame(existing, item)) {
+                    //no change but still replace the item
+                    mData[index] = item;
+                    return index;
+                } else {
+                    mData[index] = item;
+                    mCallback.onChanged(index, 1);
+                    return index;
+                }
+            }
+        }
+        addToData(index, item);
+        if (notify) {
+            mCallback.onInserted(index, 1);
+        }
+        return index;
+    }
+
+    /**
+     * Removes the provided item from the list and calls {@link Callback#onRemoved(int, int)}.
+     *
+     * @param item The item to be removed from the list.
+     * @return True if item is removed, false if item cannot be found in the list.
+     */
+    public boolean remove(T item) {
+        return remove(item, true);
+    }
+
+    /**
+     * Removes the item at the given index and calls {@link Callback#onRemoved(int, int)}.
+     *
+     * @param index The index of the item to be removed.
+     * @return The removed item.
+     */
+    public T removeItemAt(int index) {
+        T item = get(index);
+        removeItemAtIndex(index, true);
+        return item;
+    }
+
+    private boolean remove(T item, boolean notify) {
+        int index = findIndexOf(item, DELETION);
+        if (index == INVALID_POSITION) {
+            return false;
+        }
+        removeItemAtIndex(index, notify);
+        return true;
+    }
+
+    private void removeItemAtIndex(int index, boolean notify) {
+        System.arraycopy(mData, index + 1, mData, index, mSize - index - 1);
+        mSize--;
+        mData[mSize] = null;
+        if (notify) {
+            mCallback.onRemoved(index, 1);
+        }
+    }
+
+    /**
+     * Updates the item at the given index and calls {@link Callback#onChanged(int, int)} and/or
+     * {@link Callback#onMoved(int, int)} if necessary.
+     * <p>
+     * You can use this method if you need to change an existing Item such that its position in the
+     * list may change.
+     * <p>
+     * If the new object is a different object (<code>get(index) != item</code>) and
+     * {@link Callback#areContentsTheSame(Object, Object)} returns <code>true</code>, SortedList
+     * avoids calling {@link Callback#onChanged(int, int)} otherwise it calls
+     * {@link Callback#onChanged(int, int)}.
+     * <p>
+     * If the new position of the item is different than the provided <code>index</code>,
+     * SortedList
+     * calls {@link Callback#onMoved(int, int)}.
+     *
+     * @param index The index of the item to replace
+     * @param item  The item to replace the item at the given Index.
+     * @see #add(Object)
+     */
+    public void updateItemAt(int index, T item) {
+        final T existing = get(index);
+        // assume changed if the same object is given back
+        boolean contentsChanged = existing == item || !mCallback.areContentsTheSame(existing, item);
+        if (existing != item) {
+            // different items, we can use comparison and may avoid lookup
+            final int cmp = mCallback.compare(existing, item);
+            if (cmp == 0) {
+                mData[index] = item;
+                if (contentsChanged) {
+                    mCallback.onChanged(index, 1);
+                }
+                return;
+            }
+        }
+        if (contentsChanged) {
+            mCallback.onChanged(index, 1);
+        }
+        // TODO this done in 1 pass to avoid shifting twice.
+        removeItemAtIndex(index, false);
+        int newIndex = add(item, false);
+        if (index != newIndex) {
+            mCallback.onMoved(index, newIndex);
+        }
+    }
+
+    /**
+     * This method can be used to recalculate the position of the item at the given index, without
+     * triggering an {@link Callback#onChanged(int, int)} callback.
+     * <p>
+     * If you are editing objects in the list such that their position in the list may change but
+     * you don't want to trigger an onChange animation, you can use this method to re-position it.
+     * If the item changes position, SortedList will call {@link Callback#onMoved(int, int)}
+     * without
+     * calling {@link Callback#onChanged(int, int)}.
+     * <p>
+     * A sample usage may look like:
+     *
+     * <pre>
+     *     final int position = mSortedList.indexOf(item);
+     *     item.incrementPriority(); // assume items are sorted by priority
+     *     mSortedList.recalculatePositionOfItemAt(position);
+     * </pre>
+     * In the example above, because the sorting criteria of the item has been changed,
+     * mSortedList.indexOf(item) will not be able to find the item. This is why the code above
+     * first
+     * gets the position before editing the item, edits it and informs the SortedList that item
+     * should be repositioned.
+     *
+     * @param index The current index of the Item whose position should be re-calculated.
+     * @see #updateItemAt(int, Object)
+     * @see #add(Object)
+     */
+    public void recalculatePositionOfItemAt(int index) {
+        // TODO can be improved
+        final T item = get(index);
+        removeItemAtIndex(index, false);
+        int newIndex = add(item, false);
+        if (index != newIndex) {
+            mCallback.onMoved(index, newIndex);
+        }
+    }
+
+    /**
+     * Returns the item at the given index.
+     *
+     * @param index The index of the item to retrieve.
+     * @return The item at the given index.
+     * @throws java.lang.IndexOutOfBoundsException if provided index is negative or larger than the
+     *                                             size of the list.
+     */
+    public T get(int index) throws IndexOutOfBoundsException {
+        if (index >= mSize || index < 0) {
+            throw new IndexOutOfBoundsException("Asked to get item at " + index + " but size is "
+                    + mSize);
+        }
+        return mData[index];
+    }
+
+    /**
+     * Returns the position of the provided item.
+     *
+     * @param item The item to query for position.
+     * @return The position of the provided item or {@link #INVALID_POSITION} if item is not in the
+     * list.
+     */
+    public int indexOf(T item) {
+        return findIndexOf(item, LOOKUP);
+    }
+
+    private int findIndexOf(T item, int reason) {
+        int left = 0;
+        int right = mSize;
+        while (left < right) {
+            final int middle = (left + right) / 2;
+            T myItem = mData[middle];
+            final int cmp = mCallback.compare(myItem, item);
+            if (cmp < 0) {
+                left = middle + 1;
+            } else if (cmp == 0) {
+                if (mCallback.areItemsTheSame(myItem, item)) {
+                    return middle;
+                } else {
+                    int exact = linearEqualitySearch(item, middle, left, right);
+                    if (reason == INSERTION) {
+                        return exact == INVALID_POSITION ? middle : exact;
+                    } else {
+                        return exact;
+                    }
+                }
+            } else {
+                right = middle;
+            }
+        }
+        return reason == INSERTION ? left : INVALID_POSITION;
+    }
+
+    private int linearEqualitySearch(T item, int middle, int left, int right) {
+        // go left
+        for (int next = middle - 1; next >= left; next--) {
+            T nextItem = mData[next];
+            int cmp = mCallback.compare(nextItem, item);
+            if (cmp != 0) {
+                break;
+            }
+            if (mCallback.areItemsTheSame(nextItem, item)) {
+                return next;
+            }
+        }
+        for (int next = middle + 1; next < right; next++) {
+            T nextItem = mData[next];
+            int cmp = mCallback.compare(nextItem, item);
+            if (cmp != 0) {
+                break;
+            }
+            if (mCallback.areItemsTheSame(nextItem, item)) {
+                return next;
+            }
+        }
+        return INVALID_POSITION;
+    }
+
+    private void addToData(int index, T item) {
+        if (index > mSize) {
+            throw new IndexOutOfBoundsException(
+                    "cannot add item to " + index + " because size is " + mSize);
+        }
+        if (mSize == mData.length) {
+            // we are at the limit enlarge
+            T[] newData = (T[]) Array.newInstance(mTClass, mData.length + CAPACITY_GROWTH);
+            System.arraycopy(mData, 0, newData, 0, index);
+            newData[index] = item;
+            System.arraycopy(mData, index, newData, index + 1, mSize - index);
+            mData = newData;
+        } else {
+            // just shift, we fit
+            System.arraycopy(mData, index, mData, index + 1, mSize - index);
+            mData[index] = item;
+        }
+        mSize++;
+    }
+
+    /**
+     * The class that controls the behavior of the {@link SortedList}.
+     * <p>
+     * It defines how items should be sorted and how duplicates should be handled.
+     * <p>
+     * SortedList calls the callback methods on this class to notify changes about the underlying
+     * data.
+     */
+    public static abstract class Callback<T2> {
+
+        /**
+         * Similar to {@link java.util.Comparator#compare(Object, Object)}, should compare two and
+         * return how they should be ordered.
+         *
+         * @param o1 The first object to compare.
+         * @param o2 The second object to compare.
+         * @return a negative integer, zero, or a positive integer as the
+         * first argument is less than, equal to, or greater than the
+         * second.
+         */
+        abstract public int compare(T2 o1, T2 o2);
+
+        /**
+         * Called by the SortedList when an item is inserted at the given position.
+         *
+         * @param position The position of the new item.
+         * @param count    The number of items that have been added.
+         */
+        abstract public void onInserted(int position, int count);
+
+        /**
+         * Called by the SortedList when an item is removed from the given position.
+         *
+         * @param position The position of the item which has been removed.
+         * @param count    The number of items which have been removed.
+         */
+        abstract public void onRemoved(int position, int count);
+
+        /**
+         * Called by the SortedList when an item changes its position in the list.
+         *
+         * @param fromPosition The previous position of the item before the move.
+         * @param toPosition   The new position of the item.
+         */
+        abstract public void onMoved(int fromPosition, int toPosition);
+
+        /**
+         * Called by the SortedList when the item at the given position is updated.
+         *
+         * @param position The position of the item which has been updated.
+         * @param count    The number of items which has changed.
+         */
+        abstract public void onChanged(int position, int count);
+
+        /**
+         * Called by the SortedList when it wants to check whether two items have the same data
+         * or not. SortedList uses this information to decide whether it should call
+         * {@link #onChanged(int, int)} or not.
+         * <p>
+         * SortedList uses this method to check equality instead of {@link Object#equals(Object)}
+         * so
+         * that you can change its behavior depending on your UI.
+         * <p>
+         * For example, if you are using SortedList with a {@link android.support.v7.widget.RecyclerView.Adapter
+         * RecyclerView.Adapter}, you should
+         * return whether the items' visual representations are the same or not.
+         *
+         * @param oldItem The previous representation of the object.
+         * @param newItem The new object that replaces the previous one.
+         * @return True if the contents of the items are the same or false if they are different.
+         */
+        abstract public boolean areContentsTheSame(T2 oldItem, T2 newItem);
+
+        /**
+         * Called by the SortedList to decide whether two object represent the same Item or not.
+         * <p>
+         * For example, if your items have unique ids, this method should check their equality.
+         *
+         * @param item1 The first item to check.
+         * @param item2 The second item to check.
+         * @return True if the two items represent the same object or false if they are different.
+         */
+        abstract public boolean areItemsTheSame(T2 item1, T2 item2);
+    }
+
+    /**
+     * A callback implementation that can batch notify events dispatched by the SortedList.
+     * <p>
+     * This class can be useful if you want to do multiple operations on a SortedList but don't
+     * want to dispatch each event one by one, which may result in a performance issue.
+     * <p>
+     * For example, if you are going to add multiple items to a SortedList, BatchedCallback call
+     * convert individual <code>onInserted(index, 1)</code> calls into one
+     * <code>onInserted(index, N)</code> if items are added into consecutive indices. This change
+     * can help RecyclerView resolve changes much more easily.
+     * <p>
+     * If consecutive changes in the SortedList are not suitable for batching, BatchingCallback
+     * dispatches them as soon as such case is detected. After your edits on the SortedList is
+     * complete, you <b>must</b> always call {@link BatchedCallback#dispatchLastEvent()} to flush
+     * all changes to the Callback.
+     */
+    public static class BatchedCallback<T2> extends Callback<T2> {
+
+        private final Callback<T2> mWrappedCallback;
+        static final int TYPE_NONE = 0;
+        static final int TYPE_ADD = 1;
+        static final int TYPE_REMOVE = 2;
+        static final int TYPE_CHANGE = 3;
+        static final int TYPE_MOVE = 4;
+
+        int mLastEventType = TYPE_NONE;
+        int mLastEventPosition = -1;
+        int mLastEventCount = -1;
+
+        /**
+         * Creates a new BatchedCallback that wraps the provided Callback.
+         *
+         * @param wrappedCallback The Callback which should received the data change callbacks.
+         *                        Other method calls (e.g. {@link #compare(Object, Object)} from
+         *                        the SortedList are directly forwarded to this Callback.
+         */
+        public BatchedCallback(Callback<T2> wrappedCallback) {
+            mWrappedCallback = wrappedCallback;
+        }
+
+        @Override
+        public int compare(T2 o1, T2 o2) {
+            return mWrappedCallback.compare(o1, o2);
+        }
+
+        @Override
+        public void onInserted(int position, int count) {
+            if (mLastEventType == TYPE_ADD && position >= mLastEventPosition
+                    && position <= mLastEventPosition + mLastEventCount) {
+                mLastEventCount += count;
+                mLastEventPosition = Math.min(position, mLastEventPosition);
+                return;
+            }
+            dispatchLastEvent();
+            mLastEventPosition = position;
+            mLastEventCount = count;
+            mLastEventType = TYPE_ADD;
+        }
+
+        @Override
+        public void onRemoved(int position, int count) {
+            if (mLastEventType == TYPE_REMOVE && mLastEventPosition == position) {
+                mLastEventCount += count;
+                return;
+            }
+            dispatchLastEvent();
+            mLastEventPosition = position;
+            mLastEventCount = count;
+            mLastEventType = TYPE_REMOVE;
+        }
+
+        @Override
+        public void onMoved(int fromPosition, int toPosition) {
+            dispatchLastEvent();//moves are not merged
+            mWrappedCallback.onMoved(fromPosition, toPosition);
+        }
+
+        @Override
+        public void onChanged(int position, int count) {
+            if (mLastEventType == TYPE_CHANGE &&
+                    !(position > mLastEventPosition + mLastEventCount
+                            || position + count < mLastEventPosition)) {
+                // take potential overlap into account
+                int previousEnd = mLastEventPosition + mLastEventCount;
+                mLastEventPosition = Math.min(position, mLastEventPosition);
+                mLastEventCount = Math.max(previousEnd, position + count) - mLastEventPosition;
+                return;
+            }
+            dispatchLastEvent();
+            mLastEventPosition = position;
+            mLastEventCount = count;
+            mLastEventType = TYPE_CHANGE;
+        }
+
+        @Override
+        public boolean areContentsTheSame(T2 oldItem, T2 newItem) {
+            return mWrappedCallback.areContentsTheSame(oldItem, newItem);
+        }
+
+        @Override
+        public boolean areItemsTheSame(T2 item1, T2 item2) {
+            return mWrappedCallback.areItemsTheSame(item1, item2);
+        }
+
+
+        /**
+         * This method dispatches any pending event notifications to the wrapped Callback.
+         * You <b>must</b> always call this method after you are done with editing the SortedList.
+         */
+        public void dispatchLastEvent() {
+            if (mLastEventType == TYPE_NONE) {
+                return;
+            }
+            switch (mLastEventType) {
+                case TYPE_ADD:
+                    mWrappedCallback.onInserted(mLastEventPosition, mLastEventCount);
+                    break;
+                case TYPE_REMOVE:
+                    mWrappedCallback.onRemoved(mLastEventPosition, mLastEventCount);
+                    break;
+                case TYPE_CHANGE:
+                    mWrappedCallback.onChanged(mLastEventPosition, mLastEventCount);
+                    break;
+            }
+            mLastEventType = TYPE_NONE;
+        }
+    }
+}
diff --git a/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java b/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java
index d6e409d..bf6014e 100644
--- a/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java
@@ -76,11 +76,11 @@
         } else {
             offset = getOffset(index);
         }
-        mCallback.addView(child, offset);
         mBucket.insert(offset, hidden);
         if (hidden) {
             mHiddenViews.add(child);
         }
+        mCallback.addView(child, offset);
         if (DEBUG) {
             Log.d(TAG, "addViewAt " + index + ",h:" + hidden + ", " + this);
         }
@@ -117,10 +117,10 @@
         if (index < 0) {
             return;
         }
-        mCallback.removeViewAt(index);
         if (mBucket.remove(index)) {
             mHiddenViews.remove(view);
         }
+        mCallback.removeViewAt(index);
         if (DEBUG) {
             Log.d(TAG, "remove View off:" + index + "," + this);
         }
@@ -138,10 +138,10 @@
         if (view == null) {
             return;
         }
-        mCallback.removeViewAt(offset);
         if (mBucket.remove(offset)) {
             mHiddenViews.remove(view);
         }
+        mCallback.removeViewAt(offset);
         if (DEBUG) {
             Log.d(TAG, "removeViewAt " + index + ", off:" + offset + ", " + this);
         }
@@ -161,9 +161,9 @@
      * Removes all views from the ViewGroup including the hidden ones.
      */
     void removeAllViewsUnfiltered() {
-        mCallback.removeAllViews();
         mBucket.reset();
         mHiddenViews.clear();
+        mCallback.removeAllViews();
         if (DEBUG) {
             Log.d(TAG, "removeAllViewsUnfiltered");
         }
@@ -205,11 +205,11 @@
         } else {
             offset = getOffset(index);
         }
-        mCallback.attachViewToParent(child, offset, layoutParams);
         mBucket.insert(offset, hidden);
         if (hidden) {
             mHiddenViews.add(child);
         }
+        mCallback.attachViewToParent(child, offset, layoutParams);
         if (DEBUG) {
             Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + "," +
                     "h:" + hidden + ", " + this);
@@ -253,8 +253,8 @@
      */
     void detachViewFromParent(int index) {
         final int offset = getOffset(index);
-        mCallback.detachViewFromParent(offset);
         mBucket.remove(offset);
+        mCallback.detachViewFromParent(offset);
         if (DEBUG) {
             Log.d(TAG, "detach view from parent " + index + ", off:" + offset);
         }
@@ -333,11 +333,11 @@
         }
         if (mBucket.get(index)) {
             mBucket.remove(index);
-            mCallback.removeViewAt(index);
             if (!mHiddenViews.remove(view) && DEBUG) {
                 throw new IllegalStateException(
                         "removed a hidden view but it is not in hidden views list");
             }
+            mCallback.removeViewAt(index);
             return true;
         }
         return false;
diff --git a/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java b/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
index 950b254..cdc5418 100644
--- a/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
+++ b/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
@@ -192,12 +192,14 @@
     private void animateRemoveImpl(final ViewHolder holder) {
         final View view = holder.itemView;
         final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
+        mRemoveAnimations.add(holder);
         animation.setDuration(getRemoveDuration())
                 .alpha(0).setListener(new VpaListenerAdapter() {
             @Override
             public void onAnimationStart(View view) {
                 dispatchRemoveStarting(holder);
             }
+
             @Override
             public void onAnimationEnd(View view) {
                 animation.setListener(null);
@@ -207,7 +209,6 @@
                 dispatchFinishedWhenDone();
             }
         }).start();
-        mRemoveAnimations.add(holder);
     }
 
     @Override
@@ -220,8 +221,8 @@
 
     private void animateAddImpl(final ViewHolder holder) {
         final View view = holder.itemView;
-        mAddAnimations.add(holder);
         final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
+        mAddAnimations.add(holder);
         animation.alpha(1).setDuration(getAddDuration()).
                 setListener(new VpaListenerAdapter() {
                     @Override
@@ -279,8 +280,8 @@
         // TODO: make EndActions end listeners instead, since end actions aren't called when
         // vpas are canceled (and can't end them. why?)
         // need listener functionality in VPACompat for this. Ick.
-        mMoveAnimations.add(holder);
         final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
+        mMoveAnimations.add(holder);
         animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
             @Override
             public void onAnimationStart(View view) {
@@ -335,9 +336,9 @@
         final ViewHolder newHolder = changeInfo.newHolder;
         final View newView = newHolder != null ? newHolder.itemView : null;
         if (view != null) {
-            mChangeAnimations.add(changeInfo.oldHolder);
             final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
                     getChangeDuration());
+            mChangeAnimations.add(changeInfo.oldHolder);
             oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
             oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
             oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {
@@ -359,8 +360,8 @@
             }).start();
         }
         if (newView != null) {
-            mChangeAnimations.add(changeInfo.newHolder);
             final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);
+            mChangeAnimations.add(changeInfo.newHolder);
             newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).
                     alpha(1).setListener(new VpaListenerAdapter() {
                 @Override
diff --git a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
index 383717e..ad5c7f8 100644
--- a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
@@ -511,7 +511,7 @@
             diff = -1;
         }
         if (mOrientation == VERTICAL && isLayoutRTL()) { // start from last span
-            span = consumedSpanCount - 1;
+            span = mSpanCount - 1;
             spanDiff = -1;
         } else {
             span = 0;
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
index 8d6dbf7..5f958c8 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
@@ -17,9 +17,9 @@
 package android.support.v7.widget;
 
 import android.content.Context;
+import android.graphics.PointF;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.graphics.PointF;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
@@ -28,10 +28,10 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
 import java.util.List;
 
+import static android.support.v7.widget.RecyclerView.NO_POSITION;
+
 /**
  * A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides
  * similar functionality to {@link android.widget.ListView}.
@@ -461,10 +461,9 @@
         int extraForStart;
         int extraForEnd;
         final int extra = getExtraLayoutSpace(state);
-        // default extra space to the tail of the list.
-        boolean before = state.hasTargetScrollPosition() &&
-                state.getTargetScrollPosition() < mAnchorInfo.mPosition;
-        if (before == mAnchorInfo.mLayoutFromEnd) {
+        // If the previous scroll delta was less than zero, the extra space should be laid out
+        // at the start. Otherwise, it should be at the end.
+        if (mLayoutState.mLastScrollDelta >= 0) {
             extraForEnd = extra;
             extraForStart = 0;
         } else {
@@ -668,18 +667,14 @@
         if (getChildCount() == 0) {
             return false;
         }
-        View focused = getFocusedChild();
-        if (focused != null && anchorInfo.assignFromViewIfValid(focused, state)) {
-            if (DEBUG) {
-                Log.d(TAG, "decided anchor child from focused view");
-            }
+        final View focused = getFocusedChild();
+        if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) {
+            anchorInfo.assignFromViewAndKeepVisibleRect(focused);
             return true;
         }
-
         if (mLastStackFromEnd != mStackFromEnd) {
             return false;
         }
-
         View referenceChild = anchorInfo.mLayoutFromEnd ? findReferenceChildClosestToEnd(state)
                 : findReferenceChildClosestToStart(state);
         if (referenceChild != null) {
@@ -1111,6 +1106,7 @@
         if (DEBUG) {
             Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
         }
+        mLayoutState.mLastScrollDelta = scrolled;
         return scrolled;
     }
 
@@ -1834,6 +1830,11 @@
         boolean mIsPreLayout = false;
 
         /**
+         * The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)} amount.
+         */
+        int mLastScrollDelta;
+
+        /**
          * When LLM needs to layout particular views, it sets this list in which case, LayoutState
          * will only return views from this list and return null if it cannot find an item.
          */
@@ -2001,15 +2002,66 @@
          * child.
          */
         public boolean assignFromViewIfValid(View child, RecyclerView.State state) {
-            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
-            if (!lp.isItemRemoved() && lp.getViewLayoutPosition() >= 0
-                    && lp.getViewLayoutPosition() < state.getItemCount()) {
+            if (isViewValidAsAnchor(child, state)) {
                 assignFromView(child);
                 return true;
             }
             return false;
         }
 
+        private boolean isViewValidAsAnchor(View child, RecyclerView.State state) {
+            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
+            return !lp.isItemRemoved() && lp.getViewLayoutPosition() >= 0
+                    && lp.getViewLayoutPosition() < state.getItemCount();
+        }
+
+        public void assignFromViewAndKeepVisibleRect(View child) {
+            final int spaceChange = mOrientationHelper.getTotalSpaceChange();
+            if (spaceChange >= 0) {
+                assignFromView(child);
+                return;
+            }
+            mPosition = getPosition(child);
+            if (mLayoutFromEnd) {
+                final int prevLayoutEnd = mOrientationHelper.getEndAfterPadding() - spaceChange;
+                final int childEnd = mOrientationHelper.getDecoratedEnd(child);
+                final int previousEndMargin = prevLayoutEnd - childEnd;
+                mCoordinate = mOrientationHelper.getEndAfterPadding() - previousEndMargin;
+                // ensure we did not push child's top out of bounds because of this
+                if (previousEndMargin > 0) {// we have room to shift bottom if necessary
+                    final int childSize = mOrientationHelper.getDecoratedMeasurement(child);
+                    final int estimatedChildStart = mCoordinate - childSize;
+                    final int layoutStart = mOrientationHelper.getStartAfterPadding();
+                    final int previousStartMargin = mOrientationHelper.getDecoratedStart(child) -
+                            layoutStart;
+                    final int startReference = layoutStart + Math.min(previousStartMargin, 0);
+                    final int startMargin = estimatedChildStart - startReference;
+                    if (startMargin < 0) {
+                        // offset to make top visible but not too much
+                        mCoordinate += Math.min(previousEndMargin, -startMargin);
+                    }
+                }
+            } else {
+                final int childStart = mOrientationHelper.getDecoratedStart(child);
+                final int startMargin = childStart - mOrientationHelper.getStartAfterPadding();
+                mCoordinate = childStart;
+                if (startMargin > 0) { // we have room to fix end as well
+                    final int estimatedEnd = childStart +
+                            mOrientationHelper.getDecoratedMeasurement(child);
+                    final int previousLayoutEnd = mOrientationHelper.getEndAfterPadding() -
+                            spaceChange;
+                    final int previousEndMargin = previousLayoutEnd -
+                            mOrientationHelper.getDecoratedEnd(child);
+                    final int endReference = mOrientationHelper.getEndAfterPadding() -
+                            Math.min(0, previousEndMargin);
+                    final int endMargin = endReference - estimatedEnd;
+                    if (endMargin < 0) {
+                        mCoordinate -= Math.min(startMargin, -endMargin);
+                    }
+                }
+            }
+        }
+
         public void assignFromView(View child) {
             if (mLayoutFromEnd) {
                 mCoordinate = mOrientationHelper.getDecoratedEnd(child) +
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index e5d4a29..c9aefd2 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -26,9 +26,12 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.CallSuper;
 import android.support.annotation.Nullable;
 import android.support.v4.util.ArrayMap;
+import android.support.v4.view.InputDeviceCompat;
 import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ScrollingView;
 import android.support.v4.view.VelocityTrackerCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewConfigurationCompat;
@@ -44,6 +47,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.TypedValue;
 import android.view.FocusFinder;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -124,20 +128,20 @@
  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
  * writing an {@link Adapter}, you probably want to use adapter positions.
  */
-public class RecyclerView extends ViewGroup {
+public class RecyclerView extends ViewGroup implements ScrollingView {
     private static final String TAG = "RecyclerView";
 
     private static final boolean DEBUG = false;
 
     /**
-     * On Kitkat, there is a bug which prevents DisplayList from being invalidated if a View is two
-     * levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by setting
-     * View's visibility to INVISIBLE when View is detached. On Kitkat, Recycler recursively
-     * traverses itemView and invalidates display list for each ViewGroup that matches this
-     * criteria.
+     * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
+     * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
+     * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
+     * recursively traverses itemView and invalidates display list for each ViewGroup that matches
+     * this criteria.
      */
-    private static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 19 ||
-            Build.VERSION.SDK_INT == 20;
+    private static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
+            || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
 
     private static final boolean DISPATCH_TEMP_DETACH = false;
     public static final int HORIZONTAL = 0;
@@ -279,12 +283,15 @@
     private int mTouchSlop;
     private final int mMinFlingVelocity;
     private final int mMaxFlingVelocity;
+    // This value is used when handling generic motion events.
+    private float mScrollFactor = Float.MIN_VALUE;
 
     private final ViewFlinger mViewFlinger = new ViewFlinger();
 
     final State mState = new State();
 
     private OnScrollListener mScrollListener;
+    private List<OnScrollListener> mScrollListeners;
 
     // For use in item animations
     boolean mItemsAddedOrRemoved = false;
@@ -319,13 +326,13 @@
         this(context, null);
     }
 
-    public RecyclerView(Context context, AttributeSet attrs) {
+    public RecyclerView(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public RecyclerView(Context context, AttributeSet attrs, int defStyle) {
+    public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-
+        setFocusableInTouchMode(true);
         final int version = Build.VERSION.SDK_INT;
         mPostUpdatesOnAnimation = version >= 16;
 
@@ -455,7 +462,19 @@
         mAdapterHelper = new AdapterHelper(new Callback() {
             @Override
             public ViewHolder findViewHolder(int position) {
-                return findViewHolderForPosition(position, true);
+                final ViewHolder vh = findViewHolderForPosition(position, true);
+                if (vh == null) {
+                    return null;
+                }
+                // ensure it is not hidden because for adapter helper, the only thing matter is that
+                // LM thinks view is a child.
+                if (mChildHelper.isHidden(vh.itemView)) {
+                    if (DEBUG) {
+                        Log.d(TAG, "assuming view holder cannot be find because it is hidden");
+                    }
+                    return null;
+                }
+                return vh;
             }
 
             @Override
@@ -680,6 +699,23 @@
     }
 
     /**
+     * <p>Return the offset of the RecyclerView's text baseline from the its top
+     * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
+     * this method returns -1.</p>
+     *
+     * @return the offset of the baseline within the RecyclerView's bounds or -1
+     *         if baseline alignment is not supported
+     */
+    @Override
+    public int getBaseline() {
+        if (mLayout != null) {
+            return mLayout.getBaseline();
+        } else {
+            return super.getBaseline();
+        }
+    }
+
+    /**
      * Set the {@link LayoutManager} that this RecyclerView will use.
      *
      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
@@ -860,18 +896,14 @@
             return;
         }
         if (DEBUG) {
-            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState, new Exception());
+            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
+                    new Exception());
         }
         mScrollState = state;
         if (state != SCROLL_STATE_SETTLING) {
             stopScrollersInternal();
         }
-        if (mScrollListener != null) {
-            mScrollListener.onScrollStateChanged(this, state);
-        }
-        if (mLayout != null) {
-            mLayout.onScrollStateChanged(state);
-        }
+        dispatchOnScrollStateChanged(state);
     }
 
     /**
@@ -947,12 +979,52 @@
      * Set a listener that will be notified of any changes in scroll state or position.
      *
      * @param listener Listener to set or null to clear
+     *
+     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
+     *             {@link #removeOnScrollListener(OnScrollListener)}
      */
+    @Deprecated
     public void setOnScrollListener(OnScrollListener listener) {
         mScrollListener = listener;
     }
 
     /**
+     * Add a listener that will be notified of any changes in scroll state or position.
+     *
+     * <p>Components that add a listener should take care to remove it when finished.
+     * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
+     * to remove all attached listeners.</p>
+     *
+     * @param listener listener to set or null to clear
+     */
+    public void addOnScrollListener(OnScrollListener listener) {
+        if (mScrollListeners == null) {
+            mScrollListeners = new ArrayList<OnScrollListener>();
+        }
+        mScrollListeners.add(listener);
+    }
+
+    /**
+     * Remove a listener that was notified of any changes in scroll state or position.
+     *
+     * @param listener listener to set or null to clear
+     */
+    public void removeOnScrollListener(OnScrollListener listener) {
+        if (mScrollListeners != null) {
+            mScrollListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Remove all secondary listener that were notified of any changes in scroll state or position.
+     */
+    public void clearOnScrollListeners() {
+        if (mScrollListeners != null) {
+            mScrollListeners.clear();
+        }
+    }
+
+    /**
      * Convenience method to scroll to a certain position.
      *
      * RecyclerView does not implement scrolling logic, rather forwards the call to
@@ -1079,7 +1151,7 @@
             pullGlows(overscrollX, overscrollY);
         }
         if (hresult != 0 || vresult != 0) {
-            notifyOnScrolled(hresult, vresult);
+            dispatchOnScrolled(hresult, vresult);
         }
         if (!awakenScrollBars()) {
             invalidate();
@@ -1106,7 +1178,7 @@
      * (RecyclerView.Adapter)
      */
     @Override
-    protected int computeHorizontalScrollOffset() {
+    public int computeHorizontalScrollOffset() {
         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState)
                 : 0;
     }
@@ -1129,7 +1201,7 @@
      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
      */
     @Override
-    protected int computeHorizontalScrollExtent() {
+    public int computeHorizontalScrollExtent() {
         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
     }
 
@@ -1149,7 +1221,7 @@
      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
      */
     @Override
-    protected int computeHorizontalScrollRange() {
+    public int computeHorizontalScrollRange() {
         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
     }
 
@@ -1171,7 +1243,7 @@
      * (RecyclerView.Adapter)
      */
     @Override
-    protected int computeVerticalScrollOffset() {
+    public int computeVerticalScrollOffset() {
         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
     }
 
@@ -1192,7 +1264,7 @@
      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
      */
     @Override
-    protected int computeVerticalScrollExtent() {
+    public int computeVerticalScrollExtent() {
         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
     }
 
@@ -1212,7 +1284,7 @@
      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
      */
     @Override
-    protected int computeVerticalScrollRange() {
+    public int computeVerticalScrollRange() {
         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
     }
 
@@ -1469,6 +1541,23 @@
     public void requestChildFocus(View child, View focused) {
         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
             mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
+
+            // get item decor offsets w/o refreshing. If they are invalid, there will be another
+            // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
+            // View in viewport.
+            final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
+            if (focusedLayoutParams instanceof LayoutParams) {
+                // if focused child has item decors, use them. Otherwise, ignore.
+                final LayoutParams lp = (LayoutParams) focusedLayoutParams;
+                if (!lp.mInsetsDirty) {
+                    final Rect insets = lp.mDecorInsets;
+                    mTempRect.left -= insets.left;
+                    mTempRect.right += insets.right;
+                    mTempRect.top -= insets.top;
+                    mTempRect.bottom += insets.bottom;
+                }
+            }
+
             offsetDescendantRectToMyCoords(focused, mTempRect);
             offsetRectIntoDescendantCoords(child, mTempRect);
             requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
@@ -1818,6 +1907,54 @@
         }
     }
 
+    // @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        if (mLayout == null) {
+            return false;
+        }
+        if ((MotionEventCompat.getSource(event) & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
+            if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
+                final float vScroll, hScroll;
+                if (mLayout.canScrollVertically()) {
+                    vScroll = MotionEventCompat
+                            .getAxisValue(event, MotionEventCompat.AXIS_VSCROLL);
+                } else {
+                    vScroll = 0f;
+                }
+                if (mLayout.canScrollHorizontally()) {
+                    hScroll = MotionEventCompat
+                            .getAxisValue(event, MotionEventCompat.AXIS_HSCROLL);
+                } else {
+                    hScroll = 0f;
+                }
+
+                if (vScroll != 0 || hScroll != 0) {
+                    final float scrollFactor = getScrollFactor();
+                    scrollBy((int) (hScroll * scrollFactor), (int) (vScroll * scrollFactor));
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Ported from View.getVerticalScrollFactor.
+     */
+    private float getScrollFactor() {
+        if (mScrollFactor == Float.MIN_VALUE) {
+            TypedValue outValue = new TypedValue();
+            if (getContext().getTheme().resolveAttribute(
+                    android.R.attr.listPreferredItemHeight, outValue, true)) {
+                mScrollFactor = outValue.getDimension(
+                        getContext().getResources().getDisplayMetrics());
+            } else {
+                return 0; //listPreferredItemHeight is not defined, no generic scrolling
+            }
+
+        }
+        return mScrollFactor;
+    }
+
     @Override
     protected void onMeasure(int widthSpec, int heightSpec) {
         if (mAdapterUpdateDuringMeasure) {
@@ -2226,7 +2363,7 @@
         mState.mOldChangedHolders = null;
 
         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
-            notifyOnScrolled(0, 0);
+            dispatchOnScrolled(0, 0);
         }
     }
 
@@ -2696,8 +2833,8 @@
                 } else {
                     // binding to a new view will need re-layout anyways. We can as well trigger
                     // it here so that it happens during layout
-                    holder.addFlags(ViewHolder.FLAG_INVALID);
                     requestLayout();
+                    break;
                 }
             }
         }
@@ -3016,6 +3153,106 @@
         return insets;
     }
 
+    /**
+     * Called when the scroll position of this RecyclerView changes. Subclasses should use
+     * this method to respond to scrolling within the adapter's data set instead of an explicit
+     * listener.
+     *
+     * <p>This method will always be invoked before listeners. If a subclass needs to perform
+     * any additional upkeep or bookkeeping after scrolling but before listeners run,
+     * this is a good place to do so.</p>
+     *
+     * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
+     * the distance scrolled in either direction within the adapter's data set instead of absolute
+     * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
+     * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
+     * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
+     * do not correspond to the data set scroll position. However, some subclasses may choose
+     * to use these fields as special offsets.</p>
+     *
+     * @param dx horizontal distance scrolled in pixels
+     * @param dy vertical distance scrolled in pixels
+     */
+    public void onScrolled(int dx, int dy) {
+        // Do nothing
+    }
+
+    void dispatchOnScrolled(int hresult, int vresult) {
+        // Pass the current scrollX/scrollY values; no actual change in these properties occurred
+        // but some general-purpose code may choose to respond to changes this way.
+        final int scrollX = getScrollX();
+        final int scrollY = getScrollY();
+        onScrollChanged(scrollX, scrollY, scrollX, scrollY);
+
+        // Pass the real deltas to onScrolled, the RecyclerView-specific method.
+        onScrolled(hresult, vresult);
+
+        // Invoke listeners last. Subclassed view methods always handle the event first.
+        // All internal state is consistent by the time listeners are invoked.
+        if (mScrollListener != null) {
+            mScrollListener.onScrolled(this, hresult, vresult);
+        }
+        if (mScrollListeners != null) {
+            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
+                mScrollListeners.get(i).onScrolled(this, hresult, vresult);
+            }
+        }
+    }
+
+    /**
+     * Called when the scroll state of this RecyclerView changes. Subclasses should use this
+     * method to respond to state changes instead of an explicit listener.
+     *
+     * <p>This method will always be invoked before listeners, but after the LayoutManager
+     * responds to the scroll state change.</p>
+     *
+     * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
+     *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
+     */
+    public void onScrollStateChanged(int state) {
+        // Do nothing
+    }
+
+    void dispatchOnScrollStateChanged(int state) {
+        // Let the LayoutManager go first; this allows it to bring any properties into
+        // a consistent state before the RecyclerView subclass responds.
+        if (mLayout != null) {
+            mLayout.onScrollStateChanged(state);
+        }
+
+        // Let the RecyclerView subclass handle this event next; any LayoutManager property
+        // changes will be reflected by this time.
+        onScrollStateChanged(state);
+
+        // Listeners go last. All other internal state is consistent by this point.
+        if (mScrollListener != null) {
+            mScrollListener.onScrollStateChanged(this, state);
+        }
+        if (mScrollListeners != null) {
+            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
+                mScrollListeners.get(i).onScrollStateChanged(this, state);
+            }
+        }
+    }
+
+    /**
+     * Returns whether there are pending adapter updates which are not yet applied to the layout.
+     * <p>
+     * If this method returns <code>true</code>, it means that what user is currently seeing may not
+     * reflect them adapter contents (depending on what has changed).
+     * You may use this information to defer or cancel some operations.
+     * <p>
+     * This method returns true if RecyclerView has not yet calculated the first layout after it is
+     * attached to the Window or the Adapter has been replaced.
+     *
+     * @return True if there are some adapter updates which are not yet reflected to layout or false
+     * if layout is up to date.
+     */
+    public boolean hasPendingAdapterUpdates() {
+        return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
+                || mAdapterHelper.hasPendingUpdates();
+    }
+
     private class ViewFlinger implements Runnable {
         private int mLastFlingX;
         private int mLastFlingY;
@@ -3127,7 +3364,7 @@
                     }
                 }
                 if (hresult != 0 || vresult != 0) {
-                    notifyOnScrolled(hresult, vresult);
+                    dispatchOnScrolled(hresult, vresult);
                 }
 
                 if (!awakenScrollBars()) {
@@ -3241,14 +3478,6 @@
 
     }
 
-    private void notifyOnScrolled(int hresult, int vresult) {
-        // dummy values, View's implementation does not use these.
-        onScrollChanged(0, 0, 0, 0);
-        if (mScrollListener != null) {
-            mScrollListener.onScrolled(this, hresult, vresult);
-        }
-    }
-
     private class RecyclerViewDataObserver extends AdapterDataObserver {
         @Override
         public void onChanged() {
@@ -3539,6 +3768,7 @@
                         + "position " + position + "(offset:" + offsetPosition + ")."
                         + "state:" + mState.getItemCount());
             }
+            holder.mOwnerRecyclerView = RecyclerView.this;
             mAdapter.bindViewHolder(holder, offsetPosition);
             attachAccessibilityDelegate(view);
             if (mState.isPreLayout()) {
@@ -3687,8 +3917,7 @@
                         Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
                                 + "pool");
                     }
-                    holder = getRecycledViewPool()
-                            .getRecycledView(mAdapter.getItemViewType(offsetPosition));
+                    holder = getRecycledViewPool().getRecycledView(type);
                     if (holder != null) {
                         holder.resetInternal();
                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
@@ -3697,8 +3926,7 @@
                     }
                 }
                 if (holder == null) {
-                    holder = mAdapter.createViewHolder(RecyclerView.this,
-                            mAdapter.getItemViewType(offsetPosition));
+                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
                     if (DEBUG) {
                         Log.d(TAG, "getViewForPosition created new ViewHolder");
                     }
@@ -3714,6 +3942,7 @@
                             + " come here only in pre-layout. Holder: " + holder);
                 }
                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
+                holder.mOwnerRecyclerView = RecyclerView.this;
                 mAdapter.bindViewHolder(holder, offsetPosition);
                 attachAccessibilityDelegate(holder.itemView);
                 bound = true;
@@ -3867,11 +4096,14 @@
                         + " should first call stopIgnoringView(view) before calling recycle.");
             }
             //noinspection unchecked
+            final boolean transientStatePreventsRecycling = holder
+                    .doesTransientStatePreventRecycling();
             final boolean forceRecycle = mAdapter != null
-                    && holder.doesTransientStatePreventRecycling()
+                    && transientStatePreventsRecycling
                     && mAdapter.onFailedToRecycleView(holder);
+            boolean cached = false;
+            boolean recycled = false;
             if (forceRecycle || holder.isRecyclable()) {
-                boolean cached = false;
                 if (!holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved()) &&
                         !holder.isChanged()) {
                     // Retire oldest cached view
@@ -3886,20 +4118,25 @@
                 }
                 if (!cached) {
                     addViewHolderToRecycledViewPool(holder);
+                    recycled = true;
                 }
             } else if (DEBUG) {
                 Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
-                        + "re-visit here. We are stil removing it from animation lists");
+                        + "re-visit here. We are still removing it from animation lists");
             }
             // even if the holder is not removed, we still call this method so that it is removed
             // from view holder lists.
             mState.onViewRecycled(holder);
+            if (!cached && !recycled && transientStatePreventsRecycling) {
+                holder.mOwnerRecyclerView = null;
+            }
         }
 
         void addViewHolderToRecycledViewPool(ViewHolder holder) {
             ViewCompat.setAccessibilityDelegate(holder.itemView, null);
-            getRecycledViewPool().putRecycledView(holder);
             dispatchViewRecycled(holder);
+            holder.mOwnerRecyclerView = null;
+            getRecycledViewPool().putRecycledView(holder);
         }
 
         /**
@@ -4189,6 +4426,7 @@
                         holder.offsetPosition(-count, applyToPreLayout);
                     } else if (holder.getLayoutPosition() >= removedFrom) {
                         // Item for this view was removed. Dump it from the cache.
+                        holder.addFlags(ViewHolder.FLAG_REMOVED);
                         recycleCachedViewAt(i);
                     }
                 }
@@ -4399,10 +4637,10 @@
             if (hasStableIds()) {
                 holder.mItemId = getItemId(position);
             }
-            onBindViewHolder(holder, position);
             holder.setFlags(ViewHolder.FLAG_BOUND,
                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
-                    | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
+                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
+            onBindViewHolder(holder, position);
         }
 
         /**
@@ -4476,6 +4714,11 @@
          * attached to the parent RecyclerView. If an item view has large or expensive data
          * bound to it such as large bitmaps, this may be a good place to release those
          * resources.</p>
+         * <p>
+         * RecyclerView calls this method right before clearing ViewHolder's internal data and
+         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
+         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
+         * its adapter position.
          *
          * @param holder The ViewHolder for the view being recycled
          */
@@ -4869,6 +5112,7 @@
          *
          * @param view The RecyclerView this LayoutManager is bound to
          */
+        @CallSuper
         public void onAttachedToWindow(RecyclerView view) {
         }
 
@@ -4892,6 +5136,7 @@
          * @param recycler The recycler to use if you prefer to recycle your children instead of
          *                 keeping them around.
          */
+        @CallSuper
         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
             onDetachedFromWindow(view);
         }
@@ -5310,6 +5555,16 @@
         }
 
         /**
+         * Returns offset of the RecyclerView's text baseline from the its top boundary.
+         *
+         * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
+         * there is no baseline.
+         */
+        public int getBaseline() {
+            return -1;
+        }
+
+        /**
          * Returns the adapter position of the item represented by the given View. This does not
          * contain any adapter changes that might have happened after the last layout.
          *
@@ -6111,6 +6366,7 @@
          * @param state     Transient state of RecyclerView
          * @return The chosen view to be focused
          */
+        @Nullable
         public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
                 State state) {
             return null;
@@ -6548,7 +6804,6 @@
          */
         public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
                 AccessibilityNodeInfoCompat info) {
-            info.setClassName(RecyclerView.class.getName());
             if (ViewCompat.canScrollVertically(mRecyclerView, -1) ||
                     ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
@@ -6934,7 +7189,13 @@
      * An OnScrollListener can be set on a RecyclerView to receive messages
      * when a scrolling event has occurred on that RecyclerView.
      *
-     * @see RecyclerView#setOnScrollListener(OnScrollListener)
+     * @see RecyclerView#setOnScrollListener(OnScrollListener) and
+     * RecyclerView#addOnScrollListener(OnScrollListener)
+     *
+     * If you are planning to have several listeners at the same time, use
+     * RecyclerView#addOnScrollListener. If there will be only one listener at the time and you
+     * want your components to be able to easily replace the listener use
+     * RecyclerView#setOnScrollListener.
      */
     abstract static public class OnScrollListener {
         /**
@@ -6971,6 +7232,11 @@
         /**
          * This method is called whenever the view in the ViewHolder is recycled.
          *
+         * RecyclerView calls this method right before clearing ViewHolder's internal data and
+         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
+         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
+         * its adapter position.
+         *
          * @param holder The ViewHolder containing the view that was recycled
          */
         public void onViewRecycled(ViewHolder holder);
@@ -7077,6 +7343,12 @@
         // scrap container.
         private Recycler mScrapContainer = null;
 
+        /**
+         * Is set when VH is bound from the adapter and cleaned right before it is sent to
+         * {@link RecycledViewPool}.
+         */
+        RecyclerView mOwnerRecyclerView;
+
         public ViewHolder(View itemView) {
             if (itemView == null) {
                 throw new IllegalArgumentException("itemView may not be null");
@@ -7180,15 +7452,13 @@
          * @return The adapter position of the item if it still exists in the adapter.
          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
-         * layout pass or the ViewHolder has been removed from the RecyclerView.
+         * layout pass or the ViewHolder has already been recycled.
          */
         public final int getAdapterPosition() {
-            final ViewParent parent = itemView.getParent();
-            if (!(parent instanceof RecyclerView)) {
-                return -1;
+            if (mOwnerRecyclerView == null) {
+                return NO_POSITION;
             }
-            final RecyclerView rv = (RecyclerView) parent;
-            return rv.getAdapterPositionFor(this);
+            return mOwnerRecyclerView.getAdapterPositionFor(this);
         }
 
         /**
@@ -7280,7 +7550,7 @@
         }
 
         boolean isAdapterPositionUnknown() {
-            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0;
+            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
         }
 
         void setFlags(int flags, int mask) {
@@ -7316,7 +7586,7 @@
             if (isChanged()) sb.append(" changed");
             if (isTmpDetached()) sb.append(" tmpDetached");
             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
-            if (!isAdapterPositionUnknown()) sb.append("undefined adapter position");
+            if (isAdapterPositionUnknown()) sb.append("undefined adapter position");
 
             if (itemView.getParent() == null) sb.append(" no parent");
             sb.append("}");
@@ -7382,8 +7652,9 @@
     }
 
     private int getAdapterPositionFor(ViewHolder viewHolder) {
-        if (viewHolder.hasAnyOfTheFlags(
-                ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
+        if (viewHolder.hasAnyOfTheFlags( ViewHolder.FLAG_INVALID |
+                ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
+                || !viewHolder.isBound()) {
             return RecyclerView.NO_POSITION;
         }
         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
@@ -8164,7 +8435,7 @@
                     mItemCount;
         }
 
-        public void onViewRecycled(ViewHolder holder) {
+        void onViewRecycled(ViewHolder holder) {
             mPreLayoutHolderMap.remove(holder);
             mPostLayoutHolderMap.remove(holder);
             if (mOldChangedHolders != null) {
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java
index ed7dfd6..3fe9abc 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java
@@ -35,12 +35,16 @@
         mRecyclerView = recyclerView;
     }
 
+    private boolean shouldIgnore() {
+        return mRecyclerView.hasPendingAdapterUpdates();
+    }
+
     @Override
     public boolean performAccessibilityAction(View host, int action, Bundle args) {
         if (super.performAccessibilityAction(host, action, args)) {
             return true;
         }
-        if (mRecyclerView.getLayoutManager() != null) {
+        if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
             return mRecyclerView.getLayoutManager().performAccessibilityAction(action, args);
         }
 
@@ -51,7 +55,7 @@
     public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
         super.onInitializeAccessibilityNodeInfo(host, info);
         info.setClassName(RecyclerView.class.getName());
-        if (mRecyclerView.getLayoutManager() != null) {
+        if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
             mRecyclerView.getLayoutManager().onInitializeAccessibilityNodeInfo(info);
         }
     }
@@ -60,7 +64,7 @@
     public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(host, event);
         event.setClassName(RecyclerView.class.getName());
-        if (host instanceof RecyclerView) {
+        if (host instanceof RecyclerView && !shouldIgnore()) {
             RecyclerView rv = (RecyclerView) host;
             if (rv.getLayoutManager() != null) {
                 rv.getLayoutManager().onInitializeAccessibilityEvent(event);
@@ -76,7 +80,7 @@
         @Override
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
             super.onInitializeAccessibilityNodeInfo(host, info);
-            if (mRecyclerView.getLayoutManager() != null) {
+            if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
                 mRecyclerView.getLayoutManager().
                         onInitializeAccessibilityNodeInfoForItem(host, info);
             }
@@ -87,7 +91,7 @@
             if (super.performAccessibilityAction(host, action, args)) {
                 return true;
             }
-            if (mRecyclerView.getLayoutManager() != null) {
+            if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
                 return mRecyclerView.getLayoutManager().
                         performAccessibilityActionForItem(host, action, args);
             }
diff --git a/v7/recyclerview/src/android/support/v7/widget/util/SortedListAdapterCallback.java b/v7/recyclerview/src/android/support/v7/widget/util/SortedListAdapterCallback.java
new file mode 100644
index 0000000..4921541
--- /dev/null
+++ b/v7/recyclerview/src/android/support/v7/widget/util/SortedListAdapterCallback.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 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.v7.widget.util;
+
+import android.support.v7.util.SortedList;
+import android.support.v7.widget.RecyclerView;
+
+/**
+ * A {@link SortedList.Callback} implementation that can bind a {@link SortedList} to a
+ * {@link RecyclerView.Adapter}.
+ */
+public abstract class SortedListAdapterCallback<T2> extends SortedList.Callback<T2> {
+
+    final RecyclerView.Adapter mAdapter;
+
+    /**
+     * Creates a {@link SortedList.Callback} that will forward data change events to the provided
+     * Adapter.
+     *
+     * @param adapter The Adapter instance which should receive events from the SortedList.
+     */
+    public SortedListAdapterCallback(RecyclerView.Adapter adapter) {
+        mAdapter = adapter;
+    }
+
+    @Override
+    public void onInserted(int position, int count) {
+        mAdapter.notifyItemRangeInserted(position, count);
+    }
+
+    @Override
+    public void onRemoved(int position, int count) {
+        mAdapter.notifyItemRangeRemoved(position, count);
+    }
+
+    @Override
+    public void onMoved(int fromPosition, int toPosition) {
+        mAdapter.notifyItemMoved(fromPosition, toPosition);
+    }
+
+    @Override
+    public void onChanged(int position, int count) {
+        mAdapter.notifyItemRangeChanged(position, count);
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/SortedListAdapterCallbackWrapperTest.java b/v7/recyclerview/tests/src/android/support/v7/util/SortedListAdapterCallbackWrapperTest.java
new file mode 100644
index 0000000..041526e
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/util/SortedListAdapterCallbackWrapperTest.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2014 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.v7.util;
+
+import junit.framework.TestCase;
+
+import static android.support.v7.util.SortedList.BatchedCallback.TYPE_NONE;
+import static android.support.v7.util.SortedList.BatchedCallback.TYPE_ADD;
+import static android.support.v7.util.SortedList.BatchedCallback.TYPE_REMOVE;
+import static android.support.v7.util.SortedList.BatchedCallback.TYPE_CHANGE;
+import static android.support.v7.util.SortedList.BatchedCallback.TYPE_MOVE;
+
+public class SortedListAdapterCallbackWrapperTest extends TestCase {
+
+    private int lastReceivedType = TYPE_NONE;
+    private int lastReceivedPosition = -1;
+    private int lastReceivedCount = -1;
+
+    private SortedList.Callback<Object> mCallback = new SortedList.Callback<Object>() {
+        @Override
+        public int compare(Object o1, Object o2) {
+            return 0;
+        }
+
+        @Override
+        public void onInserted(int position, int count) {
+            lastReceivedType = TYPE_ADD;
+            lastReceivedPosition = position;
+            lastReceivedCount = count;
+        }
+
+        @Override
+        public void onRemoved(int position, int count) {
+            lastReceivedType = TYPE_REMOVE;
+            lastReceivedPosition = position;
+            lastReceivedCount = count;
+        }
+
+        @Override
+        public void onMoved(int fromPosition, int toPosition) {
+            lastReceivedType = TYPE_MOVE;
+            lastReceivedPosition = fromPosition;
+            lastReceivedCount = toPosition;
+        }
+
+        @Override
+        public void onChanged(int position, int count) {
+            lastReceivedType = TYPE_CHANGE;
+            lastReceivedPosition = position;
+            lastReceivedCount = count;
+        }
+
+        @Override
+        public boolean areContentsTheSame(Object oldItem, Object newItem) {
+            return false;
+        }
+
+        @Override
+        public boolean areItemsTheSame(Object item1, Object item2) {
+            return false;
+        }
+    };
+
+    private SortedList.BatchedCallback<Object> mBatched =
+            new SortedList.BatchedCallback<Object>(mCallback);
+
+    public void testAdd() throws Throwable {
+        mBatched.onInserted(0, 3);
+        assertPending(TYPE_ADD, 0, 3);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_ADD, 0, 3);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testRemove() throws Throwable {
+        mBatched.onRemoved(0, 3);
+        assertPending(TYPE_REMOVE, 0, 3);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_REMOVE, 0, 3);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testChange() throws Throwable {
+        mBatched.onChanged(0, 3);
+        assertPending(TYPE_CHANGE, 0, 3);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_CHANGE, 0, 3);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testMove() throws Throwable {
+        mBatched.onMoved(0, 3);
+        assertLast(TYPE_MOVE, 0, 3);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchAdd1() throws Throwable {
+        mBatched.onInserted(3, 5);
+        mBatched.onInserted(3, 2);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_ADD, 3, 7);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchAdd2() throws Throwable {
+        mBatched.onInserted(3, 5);
+        mBatched.onInserted(1, 2);
+        assertLast(TYPE_ADD, 3, 5);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_ADD, 1, 2);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchAdd3() throws Throwable {
+        mBatched.onInserted(3, 5);
+        mBatched.onInserted(8, 2);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_ADD, 3, 7);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchAdd4() throws Throwable {
+        mBatched.onInserted(3, 5);
+        mBatched.onInserted(9, 2);
+        assertLast(TYPE_ADD, 3, 5);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_ADD, 9, 2);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchAdd5() throws Throwable {
+        mBatched.onInserted(3, 5);
+        mBatched.onInserted(4, 1);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_ADD, 3, 6);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchAdd6() throws Throwable {
+        mBatched.onInserted(3, 5);
+        mBatched.onInserted(4, 1);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.onInserted(4, 1);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_ADD, 3, 7);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchAddLoop() throws Throwable {
+        for (int i = 0; i < 10; i ++) {
+            mBatched.onInserted(4 + i, 1);
+            assertLast(TYPE_NONE, -1, -1);
+            assertPending(TYPE_ADD, 4, i + 1);
+        }
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_ADD, 4, 10);
+    }
+
+    public void testBatchAddReverseLoop() throws Throwable {
+        for (int i = 10; i >= 0; i --) {
+            mBatched.onInserted(4, 1);
+            assertLast(TYPE_NONE, -1, -1);
+            assertPending(TYPE_ADD, 4, 10 - i + 1);
+        }
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_ADD, 4, 11);
+    }
+
+    public void testBadBatchAddReverseLoop() throws Throwable {
+        for (int i = 10; i >= 0; i --) {
+            mBatched.onInserted(4 + i, 1);
+            if (i < 10) {
+                assertLast(TYPE_ADD, 4 + i + 1, 1);
+            }
+
+        }
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_ADD, 4, 1);
+    }
+
+    public void testBatchRemove1() throws Throwable {
+        mBatched.onRemoved(3, 5);
+        mBatched.onRemoved(3, 1);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_REMOVE, 3, 6);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchRemove2() throws Throwable {
+        mBatched.onRemoved(3, 5);
+        mBatched.onRemoved(4, 1);
+        assertLast(TYPE_REMOVE, 3, 5);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_REMOVE, 4, 1);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchRemove3() throws Throwable {
+        mBatched.onRemoved(3, 5);
+        mBatched.onRemoved(2, 3);
+        assertLast(TYPE_REMOVE, 3, 5);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_REMOVE, 2, 3);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchChange1() throws Throwable {
+        mBatched.onChanged(3, 5);
+        mBatched.onChanged(3, 1);
+        assertPending(TYPE_CHANGE, 3, 5);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_CHANGE, 3, 5);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchChange2() throws Throwable {
+        mBatched.onChanged(3, 5);
+        mBatched.onChanged(2, 7);
+        assertPending(TYPE_CHANGE, 2, 7);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.dispatchLastEvent();
+        assertLast(TYPE_CHANGE, 2, 7);
+        assertPending(TYPE_NONE, -1, -1);
+    }
+
+    public void testBatchChange3() throws Throwable {
+        mBatched.onChanged(3, 5);
+        mBatched.onChanged(2, 1);
+        assertLast(TYPE_NONE, -1, -1);
+        mBatched.onChanged(8, 2);
+        assertLast(TYPE_NONE, -1, -1);
+        assertPending(TYPE_CHANGE, 2, 8);
+    }
+
+    public void testBatchChange4() throws Throwable {
+        mBatched.onChanged(3, 5);
+        mBatched.onChanged(1, 1);
+        assertLast(TYPE_CHANGE, 3, 5);
+        assertPending(TYPE_CHANGE, 1, 1);
+    }
+
+    public void testBatchChange5() throws Throwable {
+        mBatched.onChanged(3, 5);
+        mBatched.onChanged(9, 1);
+        assertLast(TYPE_CHANGE, 3, 5);
+        assertPending(TYPE_CHANGE, 9, 1);
+    }
+
+    private void assertLast(int type, int position, int count) throws Throwable {
+        try {
+            assertEquals(lastReceivedType, type);
+            if (position >= 0) {
+                assertEquals(lastReceivedPosition, position);
+            }
+            if (count >= 0) {
+                assertEquals(lastReceivedCount, count);
+            }
+        } catch (Throwable t) {
+            throw new Throwable("last event: expected=" + log(type, position, count)
+                    + " found=" + log(lastReceivedType, lastReceivedPosition,
+                    lastReceivedCount), t);
+        }
+    }
+    private void assertPending(int type, int position, int count) throws Throwable {
+        try {
+            assertEquals(mBatched.mLastEventType, type);
+            if (position >= 0) {
+                assertEquals(mBatched.mLastEventPosition, position);
+            }
+            if (count >= 0) {
+                assertEquals(mBatched.mLastEventCount, count);
+            }
+        } catch (Throwable t) {
+            throw new Throwable("pending event: expected=" + log(type, position, count)
+                    + " found=" + log(mBatched.mLastEventType, mBatched.mLastEventPosition,
+                    mBatched.mLastEventCount), t);
+        }
+    }
+
+    private String log(int type, int position, int count) {
+        return TYPES_NAMES[type]
+                + ", p:" + position
+                + ", c:" + count;
+    }
+
+    private static final String[] TYPES_NAMES = new String[]{"none", "add", "remove", "change",
+            "move"};
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/SortedListTest.java b/v7/recyclerview/tests/src/android/support/v7/util/SortedListTest.java
new file mode 100644
index 0000000..d2da338
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/util/SortedListTest.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2014 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.v7.util;
+
+import junit.framework.TestCase;
+
+import android.support.v7.util.SortedList;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Random;
+
+public class SortedListTest extends TestCase {
+
+    SortedList<Item> mList;
+    List<Pair> mAdditions = new ArrayList<Pair>();
+    List<Pair> mRemovals = new ArrayList<Pair>();
+    List<Pair> mMoves = new ArrayList<Pair>();
+    List<Pair> mUpdates = new ArrayList<Pair>();
+    private SortedList.Callback<Item> mCallback;
+
+    private Comparator<? super Item> sItemComparator = new Comparator<Item>() {
+        @Override
+        public int compare(Item o1, Item o2) {
+            return mCallback.compare(o1, o2);
+        }
+    };
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mCallback = new SortedList.Callback<Item>() {
+            @Override
+            public int compare(Item o1, Item o2) {
+                return o1.cmpField < o2.cmpField ? -1 : (o1.cmpField == o2.cmpField ? 0 : 1);
+            }
+
+            @Override
+            public void onInserted(int position, int count) {
+                mAdditions.add(new Pair(position, count));
+            }
+
+            @Override
+            public void onRemoved(int position, int count) {
+                mRemovals.add(new Pair(position, count));
+            }
+
+            @Override
+            public void onMoved(int fromPosition, int toPosition) {
+                mMoves.add(new Pair(fromPosition, toPosition));
+            }
+
+            @Override
+            public void onChanged(int position, int count) {
+                mUpdates.add(new Pair(position, count));
+            }
+
+            @Override
+            public boolean areContentsTheSame(Item oldItem, Item newItem) {
+                return oldItem.cmpField == newItem.cmpField && oldItem.data == newItem.data;
+            }
+
+            @Override
+            public boolean areItemsTheSame(Item item1, Item item2) {
+                return item1.id == item2.id;
+            }
+        };
+        mList = new SortedList<Item>(Item.class, mCallback);
+    }
+
+    public void testEmpty() {
+        assertEquals("empty", mList.size(), 0);
+    }
+
+    public void testAdd() {
+        Item item = new Item();
+        assertEquals(insert(item), 0);
+        assertEquals(size(), 1);
+        assertTrue(mAdditions.contains(new Pair(0, 1)));
+        Item item2 = new Item();
+        item2.cmpField = item.cmpField + 1;
+        assertEquals(insert(item2), 1);
+        assertEquals(size(), 2);
+        assertTrue(mAdditions.contains(new Pair(1, 1)));
+        Item item3 = new Item();
+        item3.cmpField = item.cmpField - 1;
+        mAdditions.clear();
+        assertEquals(insert(item3), 0);
+        assertEquals(size(), 3);
+        assertTrue(mAdditions.contains(new Pair(0, 1)));
+    }
+
+    public void testAddDuplicate() {
+        Item item = new Item();
+        Item item2 = new Item(item.id, item.cmpField);
+        item2.data = item.data;
+        insert(item);
+        assertEquals(0, insert(item2));
+        assertEquals(1, size());
+        assertEquals(1, mAdditions.size());
+        assertEquals(0, mUpdates.size());
+    }
+
+    public void testRemove() {
+        Item item = new Item();
+        assertFalse(remove(item));
+        assertEquals(0, mRemovals.size());
+        insert(item);
+        assertTrue(remove(item));
+        assertEquals(1, mRemovals.size());
+        assertTrue(mRemovals.contains(new Pair(0, 1)));
+        assertEquals(0, size());
+        assertFalse(remove(item));
+        assertEquals(1, mRemovals.size());
+    }
+
+    public void testRemove2() {
+        Item item = new Item();
+        Item item2 = new Item(item.cmpField);
+        insert(item);
+        assertFalse(remove(item2));
+        assertEquals(0, mRemovals.size());
+    }
+
+    public void testBatch() {
+        mList.beginBatchedUpdates();
+        for (int i = 0; i < 5; i ++) {
+            mList.add(new Item(i));
+        }
+        assertEquals(0, mAdditions.size());
+        mList.endBatchedUpdates();
+        assertTrue(mAdditions.contains(new Pair(0, 5)));
+    }
+
+    public void testRandom() throws Throwable {
+        Random random = new Random(System.nanoTime());
+        List<Item> copy = new ArrayList<Item>();
+        StringBuilder log = new StringBuilder();
+        try {
+            for (int i = 0; i < 10000; i++) {
+                switch (random.nextInt(3)) {
+                    case 0://ADD
+                        Item item = new Item();
+                        copy.add(item);
+                        insert(item);
+                        log.append("add " + item).append("\n");
+                        break;
+                    case 1://REMOVE
+                        if (copy.size() > 0) {
+                            int index = random.nextInt(mList.size());
+                            item = mList.get(index);
+                            log.append("remove " + item).append("\n");
+                            assertTrue(copy.remove(item));
+                            assertTrue(mList.remove(item));
+                        }
+                        break;
+                    case 2://UPDATE
+                        if (copy.size() > 0) {
+                            int index = random.nextInt(mList.size());
+                            item = mList.get(index);
+                            // TODO this cannot work
+                            Item newItem = new Item(item.id, item.cmpField);
+                            log.append("update " + item + " to " + newItem).append("\n");
+                            while (newItem.data == item.data) {
+                                newItem.data = random.nextInt(1000);
+                            }
+                            int itemIndex = mList.add(newItem);
+                            copy.remove(item);
+                            copy.add(newItem);
+                            assertSame(mList.get(itemIndex), newItem);
+                            assertNotSame(mList.get(index), item);
+                        }
+                        break;
+                    case 3:// UPDATE AT
+                        if (copy.size() > 0) {
+                            int index = random.nextInt(mList.size());
+                            item = mList.get(index);
+                            Item newItem = new Item(item.id, random.nextInt());
+                            mList.updateItemAt(index, newItem);
+                            copy.remove(item);
+                            copy.add(newItem);
+                        }
+                }
+                int lastCmp = Integer.MIN_VALUE;
+                for (int index = 0; index < copy.size(); index ++) {
+                    assertFalse(mList.indexOf(copy.get(index)) == SortedList.INVALID_POSITION);
+                    assertTrue(mList.get(index).cmpField >= lastCmp);
+                    lastCmp = mList.get(index).cmpField;
+                    assertTrue(copy.contains(mList.get(index)));
+                }
+
+                for (int index = 0; index < mList.size(); index ++) {
+                    assertNotNull(mList.mData[index]);
+                }
+                for (int index = mList.size(); index < mList.mData.length; index ++) {
+                    assertNull(mList.mData[index]);
+                }
+
+            }
+        } catch (Throwable t) {
+            Collections.sort(copy, sItemComparator);
+            log.append("Items:\n");
+            for (Item item : copy) {
+                log.append(item).append("\n");
+            }
+            log.append("SortedList:\n");
+            for (int i = 0; i < mList.size(); i ++) {
+                log.append(mList.get(i)).append("\n");
+            }
+
+            throw new Throwable(" \nlog:\n" + log.toString(), t);
+        }
+    }
+
+    private int size() {
+        return mList.size();
+    }
+
+    private int insert(Item item) {
+        return mList.add(item);
+    }
+
+    private boolean remove(Item item ) {
+        return mList.remove(item);
+    }
+
+    static class Item {
+        static int idCounter = 0;
+        final int id;
+
+        int cmpField;
+
+        int data = (int) (Math.random() * 1000);//used for comparison
+
+        public Item() {
+            id = idCounter ++;;
+            cmpField = (int) (Math.random() * 1000);
+        }
+
+        public Item(int cmpField) {
+            id = idCounter ++;;
+            this.cmpField = cmpField;
+        }
+
+        public Item(int id, int cmpField) {
+            this.id = id;
+            this.cmpField = cmpField;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            Item item = (Item) o;
+
+            if (cmpField != item.cmpField) {
+                return false;
+            }
+            if (id != item.id) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = id;
+            result = 31 * result + cmpField;
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "Item{" +
+                    "id=" + id +
+                    ", cmpField=" + cmpField +
+                    ", data=" + data +
+                    '}';
+        }
+    }
+
+    private static final class Pair {
+        final int first, second;
+
+        public Pair(int first) {
+            this.first = first;
+            this.second = Integer.MIN_VALUE;
+        }
+
+        public Pair(int first, int second) {
+            this.first = first;
+            this.second = second;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            Pair pair = (Pair) o;
+
+            if (first != pair.first) {
+                return false;
+            }
+            if (second != pair.second) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = first;
+            result = 31 * result + second;
+            return result;
+        }
+    }
+}
\ No newline at end of file
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
index e96a782..0d7a685 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
@@ -33,6 +33,8 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 abstract public class BaseRecyclerViewInstrumentationTest extends
         ActivityInstrumentationTestCase2<TestActivity> {
@@ -186,6 +188,17 @@
         }
     }
 
+    public boolean requestFocus(final View view) {
+        final boolean[] result = new boolean[1];
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                result[0] = view.requestFocus();
+            }
+        });
+        return result[0];
+    }
+
     public void setRecyclerView(final RecyclerView recyclerView) throws Throwable {
         setRecyclerView(recyclerView, true);
     }
@@ -214,6 +227,7 @@
 
                 @Override
                 public void putRecycledView(RecyclerView.ViewHolder scrap) {
+                    assertNull(scrap.mOwnerRecyclerView);
                     super.putRecycledView(scrap);
                 }
             };
@@ -227,7 +241,7 @@
                     RecyclerView.ViewHolder vh = parent.getChildViewHolder(view);
                     if (!vh.isRemoved()) {
                         assertNotSame("If getItemOffsets is called, child should have a valid"
-                                            + " adapter position unless it is removed",
+                                            + " adapter position unless it is removed : " + vh,
                                     vh.getAdapterPosition(), RecyclerView.NO_POSITION);
                     }
                 }
@@ -288,13 +302,17 @@
 
     void smoothScrollToPosition(final int position)
             throws Throwable {
-        Log.d(TAG, "SMOOTH scrolling to " + position);
+        if (mDebug) {
+            Log.d(TAG, "SMOOTH scrolling to " + position);
+        }
         runTestOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.smoothScrollToPosition(position);
             }
         });
+        getInstrumentation().waitForIdleSync();
+        Thread.sleep(200); //give scroller some time so start
         while (mRecyclerView.getLayoutManager().isSmoothScrolling() ||
                 mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
             if (mDebug) {
@@ -302,7 +320,9 @@
             }
             Thread.sleep(200);
         }
-        Log.d(TAG, "SMOOTH scrolling done");
+        if (mDebug) {
+            Log.d(TAG, "SMOOTH scrolling done");
+        }
         getInstrumentation().waitForIdleSync();
     }
 
@@ -320,7 +340,26 @@
             return super.toString() + " item:" + mBoundItem;
         }
     }
+    class DumbLayoutManager extends TestLayoutManager {
+        ReentrantLock mLayoutLock = new ReentrantLock();
+        public void blockLayout() {
+            mLayoutLock.lock();
+        }
 
+        public void unblockLayout() {
+            mLayoutLock.unlock();
+        }
+        @Override
+        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+            mLayoutLock.lock();
+            detachAndScrapAttachedViews(recycler);
+            layoutRange(recycler, 0, state.getItemCount());
+            if (layoutLatch != null) {
+                layoutLatch.countDown();
+            }
+            mLayoutLock.unlock();
+        }
+    }
     class TestLayoutManager extends RecyclerView.LayoutManager {
 
         CountDownLatch layoutLatch;
@@ -511,11 +550,27 @@
 
         @Override
         public void onBindViewHolder(TestViewHolder holder, int position) {
+            assertNotNull(holder.mOwnerRecyclerView);
+            assertEquals(position, holder.getAdapterPosition());
             final Item item = mItems.get(position);
             ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mAdapterIndex + ")");
             holder.mBoundItem = item;
         }
 
+        @Override
+        public void onViewRecycled(TestViewHolder holder) {
+            super.onViewRecycled(holder);
+            final int adapterPosition = holder.getAdapterPosition();
+            final boolean shouldHavePosition = !holder.isRemoved() && holder.isBound() &&
+                    !holder.isAdapterPositionUnknown() && !holder.isInvalid();
+            String log = "Position check for " + holder.toString();
+            assertEquals(log, shouldHavePosition, adapterPosition != RecyclerView.NO_POSITION);
+            if (shouldHavePosition) {
+                assertTrue(log, mItems.size() > adapterPosition);
+                assertSame(log, holder.mBoundItem, mItems.get(adapterPosition));
+            }
+        }
+
         public void deleteAndNotify(final int start, final int count) throws Throwable {
             deleteAndNotify(new int[]{start, count});
         }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
index c02a0f6..be3557b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
@@ -127,6 +127,67 @@
         checkForMainThreadException();
     }
 
+    public void testRTL() throws Throwable {
+        for (boolean changeRtlAfter : new boolean[]{false, true}) {
+            for (boolean oneLine : new boolean[]{false, true}) {
+                for (Config config : mBaseVariations) {
+                    rtlTest(config, changeRtlAfter, oneLine);
+                    removeRecyclerView();
+                }
+            }
+        }
+    }
+
+    void rtlTest(Config config, boolean changeRtlAfter, boolean oneLine) throws Throwable {
+        if (oneLine && config.mOrientation != VERTICAL) {
+            return;// nothing to test
+        }
+        if (config.mSpanCount == 1) {
+            config.mSpanCount = 2;
+        }
+        String logPrefix = config + ", changeRtlAfterLayout:" + changeRtlAfter + ", oneLine:" + oneLine;
+        config.mItemCount = 5;
+        if (oneLine) {
+            config.mSpanCount = config.mItemCount + 1;
+        } else {
+            config.mSpanCount = Math.min(config.mItemCount - 1, config.mSpanCount);
+        }
+
+        RecyclerView rv = setupBasic(config);
+        if (changeRtlAfter) {
+            waitForFirstLayout(rv);
+            mGlm.expectLayout(1);
+            mGlm.setFakeRtl(true);
+            mGlm.waitForLayout(2);
+        } else {
+            mGlm.mFakeRTL = true;
+            waitForFirstLayout(rv);
+        }
+
+        assertEquals("view should become rtl", true, mGlm.isLayoutRTL());
+        OrientationHelper helper = OrientationHelper.createHorizontalHelper(mGlm);
+        View child0 = mGlm.findViewByPosition(0);
+        final int secondChildPos = config.mOrientation == VERTICAL ? 1
+                : config.mSpanCount;
+        View child1 = mGlm.findViewByPosition(secondChildPos);
+        assertNotNull(logPrefix + " child position 0 should be laid out", child0);
+        assertNotNull(
+                logPrefix + " second child position " + (secondChildPos) + " should be laid out",
+                child1);
+        if (config.mOrientation == VERTICAL || !config.mReverseLayout) {
+            assertTrue(logPrefix + " second child should be to the left of first child",
+                    helper.getDecoratedStart(child0) >= helper.getDecoratedEnd(child1));
+            assertEquals(logPrefix + " first child should be right aligned",
+                    helper.getDecoratedEnd(child0), helper.getEndAfterPadding());
+        } else {
+            assertTrue(logPrefix + " first child should be to the left of second child",
+                    helper.getDecoratedStart(child1) >= helper.getDecoratedEnd(child0));
+            assertEquals(logPrefix + " first child should be left aligned",
+                    helper.getDecoratedStart(child0), helper.getStartAfterPadding());
+        }
+        checkForMainThreadException();
+    }
+
     public void testLayoutParams() throws Throwable {
         layoutParamsTest(GridLayoutManager.HORIZONTAL);
         removeRecyclerView();
@@ -591,6 +652,8 @@
 
         List<Callback> mCallbacks = new ArrayList<Callback>();
 
+        Boolean mFakeRTL;
+
         public WrappedGridLayoutManager(Context context, int spanCount) {
             super(context, spanCount);
         }
@@ -601,6 +664,20 @@
         }
 
         @Override
+        protected boolean isLayoutRTL() {
+            return mFakeRTL == null ? super.isLayoutRTL() : mFakeRTL;
+        }
+
+        public void setFakeRtl(Boolean fakeRtl) {
+            mFakeRTL = fakeRtl;
+            try {
+                requestLayoutOnUIThread(mRecyclerView);
+            } catch (Throwable throwable) {
+                postExceptionToInstrumentation(throwable);
+            }
+        }
+
+        @Override
         public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
             try {
                 for (Callback callback : mCallbacks) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
index fed7a1a..f304941 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
@@ -23,6 +23,7 @@
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -90,7 +91,8 @@
     }
 
     void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable {
-        mRecyclerView = new RecyclerView(getActivity());
+        mRecyclerView = new WrappedRecyclerView(getActivity());
+
         mRecyclerView.setHasFixedSize(true);
         mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount)
                 : config.mTestAdapter;
@@ -132,6 +134,135 @@
                 mLayoutManager.mOrientationHelper.getDecoratedStart(postVH.itemView));
     }
 
+    public void testKeepFullFocusOnResize() throws Throwable {
+        keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), true);
+    }
+
+    public void testKeepPartialFocusOnResize() throws Throwable {
+        keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), false);
+    }
+
+    public void testKeepReverseFullFocusOnResize() throws Throwable {
+        keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), true);
+    }
+
+    public void testKeepReversePartialFocusOnResize() throws Throwable {
+        keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), false);
+    }
+
+    public void testKeepStackFromEndFullFocusOnResize() throws Throwable {
+        keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), true);
+    }
+
+    public void testKeepStackFromEndPartialFocusOnResize() throws Throwable {
+        keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), false);
+    }
+
+    public void keepFocusOnResizeTest(final Config config, boolean fullyVisible) throws Throwable {
+        setupByConfig(config, true);
+        final int targetPosition;
+        if (config.mStackFromEnd) {
+            targetPosition = mLayoutManager.findFirstVisibleItemPosition();
+        } else {
+            targetPosition = mLayoutManager.findLastVisibleItemPosition();
+        }
+        final OrientationHelper helper = mLayoutManager.mOrientationHelper;
+        final RecyclerView.ViewHolder vh = mRecyclerView
+                .findViewHolderForLayoutPosition(targetPosition);
+
+        // scroll enough to offset the child
+        int startMargin = helper.getDecoratedStart(vh.itemView) -
+                helper.getStartAfterPadding();
+        int endMargin = helper.getEndAfterPadding() -
+                helper.getDecoratedEnd(vh.itemView);
+        Log.d(TAG, "initial start margin " + startMargin + " , end margin:" + endMargin);
+        requestFocus(vh.itemView);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue("view should gain the focus", vh.itemView.hasFocus());
+            }
+        });
+        do {
+            Thread.sleep(100);
+        } while (mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE);
+        // scroll enough to offset the child
+        startMargin = helper.getDecoratedStart(vh.itemView) -
+                helper.getStartAfterPadding();
+        endMargin = helper.getEndAfterPadding() -
+                helper.getDecoratedEnd(vh.itemView);
+
+        Log.d(TAG, "start margin " + startMargin + " , end margin:" + endMargin);
+        assertTrue("View should become fully visible", startMargin >= 0 && endMargin >= 0);
+
+        int expectedOffset = 0;
+        boolean offsetAtStart = false;
+        if (!fullyVisible) {
+            // move it a bit such that it is no more fully visible
+            final int childSize = helper
+                    .getDecoratedMeasurement(vh.itemView);
+            expectedOffset = childSize / 3;
+            if (startMargin < endMargin) {
+                scrollBy(expectedOffset);
+                offsetAtStart = true;
+            } else {
+                scrollBy(-expectedOffset);
+                offsetAtStart = false;
+            }
+            startMargin = helper.getDecoratedStart(vh.itemView) -
+                    helper.getStartAfterPadding();
+            endMargin = helper.getEndAfterPadding() -
+                    helper.getDecoratedEnd(vh.itemView);
+            assertTrue("test sanity, view should not be fully visible", startMargin < 0
+                    || endMargin < 0);
+        }
+
+        mLayoutManager.expectLayouts(1);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final ViewGroup.LayoutParams layoutParams = mRecyclerView.getLayoutParams();
+                if (config.mOrientation == HORIZONTAL) {
+                    layoutParams.width = mRecyclerView.getWidth() / 2;
+                } else {
+                    layoutParams.height = mRecyclerView.getHeight() / 2;
+                }
+                mRecyclerView.setLayoutParams(layoutParams);
+            }
+        });
+        Thread.sleep(100);
+        // add a bunch of items right before that view, make sure it keeps its position
+        mLayoutManager.waitForLayout(2);
+        mLayoutManager.waitForAnimationsToEnd(20);
+        assertTrue("view should preserve the focus", vh.itemView.hasFocus());
+        final RecyclerView.ViewHolder postVH = mRecyclerView
+                .findViewHolderForLayoutPosition(targetPosition);
+        assertNotNull("focused child should stay in layout", postVH);
+        assertSame("same view holder should be kept for unchanged child", vh, postVH);
+        View focused = postVH.itemView;
+
+        startMargin = helper.getDecoratedStart(focused) - helper.getStartAfterPadding();
+        endMargin = helper.getEndAfterPadding() - helper.getDecoratedEnd(focused);
+
+        assertTrue("focused child should be somewhat visible",
+                helper.getDecoratedStart(focused) < helper.getEndAfterPadding()
+                        && helper.getDecoratedEnd(focused) > helper.getStartAfterPadding());
+        if (fullyVisible) {
+            assertTrue("focused child end should stay fully visible",
+                    endMargin >= 0);
+            assertTrue("focused child start should stay fully visible",
+                    startMargin >= 0);
+        } else {
+            if (offsetAtStart) {
+                assertTrue("start should preserve its offset", startMargin < 0);
+                assertTrue("end should be visible", endMargin >= 0);
+            } else {
+                assertTrue("end should preserve its offset", endMargin < 0);
+                assertTrue("start should be visible", startMargin >= 0);
+            }
+        }
+    }
+
     public void testResize() throws Throwable {
         for(Config config : addConfigVariation(mBaseVariations, "mItemCount", 5
                 , Config.DEFAULT_ITEM_COUNT)) {
@@ -1163,4 +1294,26 @@
                     '}';
         }
     }
+
+    private static class WrappedRecyclerView extends RecyclerView {
+
+        public WrappedRecyclerView(Context context) {
+            super(context);
+            init(context);
+        }
+
+        public WrappedRecyclerView(Context context, AttributeSet attrs) {
+            super(context, attrs);
+            init(context);
+        }
+
+        public WrappedRecyclerView(Context context, AttributeSet attrs, int defStyle) {
+            super(context, attrs, defStyle);
+            init(context);
+        }
+
+        private void init(Context context) {
+            initializeScrollbars(null);
+        }
+    }
 }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
index f08b3c0..42ad90a 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
@@ -26,6 +26,11 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 public class RecyclerViewAccessibilityTest extends BaseRecyclerViewInstrumentationTest {
+
+    public RecyclerViewAccessibilityTest() {
+        super(false);
+    }
+
     public void testOnInitializeAccessibilityNodeInfo() throws Throwable {
         for (boolean vBefore : new boolean[]{true, false}) {
             for (boolean vAfter : new boolean[]{true, false}) {
@@ -39,6 +44,7 @@
             }
         }
     }
+
     public void onInitializeAccessibilityNodeInfoTest(final boolean verticalScrollBefore,
             final boolean horizontalScrollBefore, final boolean verticalScrollAfter,
             final boolean horizontalScrollAfter) throws Throwable {
@@ -200,15 +206,58 @@
         assertEquals(verticalScrollAfter, vScrolledFwd.get());
     }
 
-    void performAccessibilityAction(final AccessibilityDelegateCompat delegate,
-            final RecyclerView recyclerView,  final int action) throws Throwable {
+    public void testIgnoreAccessibilityIfAdapterHasChanged() throws Throwable {
+        final RecyclerView recyclerView = new RecyclerView(getActivity()) {
+            //@Override
+            public boolean canScrollHorizontally(int direction) {
+                return true;
+            }
+
+            //@Override
+            public boolean canScrollVertically(int direction) {
+                return true;
+            }
+        };
+        final DumbLayoutManager layoutManager = new DumbLayoutManager();
+        final TestAdapter adapter = new TestAdapter(10);
+        recyclerView.setAdapter(adapter);
+        recyclerView.setLayoutManager(layoutManager);
+        layoutManager.expectLayouts(1);
+        setRecyclerView(recyclerView);
+        layoutManager.waitForLayout(1);
+
+        final RecyclerViewAccessibilityDelegate delegateCompat = recyclerView
+                .getCompatAccessibilityDelegate();
+        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
         runTestOnUiThread(new Runnable() {
             @Override
             public void run() {
-                delegate.performAccessibilityAction(recyclerView, action, null);
+                delegateCompat.onInitializeAccessibilityNodeInfo(recyclerView, info);
+            }
+        });
+        assertTrue("test sanity", info.isScrollable());
+        final AccessibilityNodeInfoCompat info2 = AccessibilityNodeInfoCompat.obtain();
+        layoutManager.blockLayout();
+        layoutManager.expectLayouts(1);
+        adapter.deleteAndNotify(1, 1);
+        // we can run this here since we blocked layout.
+        delegateCompat.onInitializeAccessibilityNodeInfo(recyclerView, info2);
+        layoutManager.unblockLayout();
+        assertFalse("info should not be filled if data is out of date", info2.isScrollable());
+        layoutManager.waitForLayout(1);
+    }
+
+    boolean performAccessibilityAction(final AccessibilityDelegateCompat delegate,
+            final RecyclerView recyclerView,  final int action) throws Throwable {
+        final boolean[] result = new boolean[1];
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                result[0] = delegate.performAccessibilityAction(recyclerView, action, null);
             }
         });
         getInstrumentation().waitForIdleSync();
         Thread.sleep(250);
+        return result[0];
     }
 }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
index 2e430e5..5c2f316 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
@@ -18,7 +18,7 @@
 package android.support.v7.widget;
 
 import android.graphics.PointF;
-import android.os.Debug;
+import android.graphics.Rect;
 import android.os.SystemClock;
 import android.support.v4.view.ViewCompat;
 import android.test.TouchUtils;
@@ -46,7 +46,7 @@
 
 public class RecyclerViewLayoutTest extends BaseRecyclerViewInstrumentationTest {
 
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     private static final String TAG = "RecyclerViewLayoutTest";
 
@@ -58,6 +58,7 @@
         RecyclerView recyclerView = new RecyclerView(getActivity());
         TestLayoutManager tlm = new TestLayoutManager() {
             int scrollPos = RecyclerView.NO_POSITION;
+
             @Override
             public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
                 layoutLatch.countDown();
@@ -67,6 +68,7 @@
                     layoutRange(recycler, scrollPos, scrollPos + 10);
                 }
             }
+
             @Override
             public void scrollToPosition(int position) {
                 scrollPos = position;
@@ -278,7 +280,7 @@
             assertTrue("test sanity, fling must run", fling(600, 600));
         }
         assertEquals("horizontal scroll", horizontal, scrolledHorizontal.get());
-        assertEquals("vertical scroll",!horizontal, scrolledVertical.get());
+        assertEquals("vertical scroll", !horizontal, scrolledVertical.get());
     }
 
     private boolean fling(final int velocityX, final int velocityY) throws Throwable {
@@ -301,6 +303,89 @@
         return true;
     }
 
+    private void assertPendingUpdatesAndLayout(TestLayoutManager testLayoutManager,
+            final Runnable runnable) throws Throwable {
+        testLayoutManager.expectLayouts(1);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                runnable.run();
+                assertTrue(mRecyclerView.hasPendingAdapterUpdates());
+            }
+        });
+        testLayoutManager.waitForLayout(1);
+        assertFalse(mRecyclerView.hasPendingAdapterUpdates());
+    }
+
+    private void setupBasic(RecyclerView recyclerView, TestLayoutManager tlm,
+            TestAdapter adapter, boolean waitForFirstLayout) throws Throwable {
+        recyclerView.setLayoutManager(tlm);
+        recyclerView.setAdapter(adapter);
+        if (waitForFirstLayout) {
+            tlm.expectLayouts(1);
+            setRecyclerView(recyclerView);
+            tlm.waitForLayout(1);
+        } else {
+            setRecyclerView(recyclerView);
+        }
+    }
+
+    public void testHasPendingUpdatesBeforeFirstLayout() throws Throwable {
+        RecyclerView recyclerView = new RecyclerView(getActivity());
+        TestLayoutManager layoutManager = new DumbLayoutManager();
+        TestAdapter testAdapter = new TestAdapter(10);
+        setupBasic(recyclerView, layoutManager, testAdapter, false);
+        assertTrue(mRecyclerView.hasPendingAdapterUpdates());
+    }
+
+    public void testNoPendingUpdatesAfterLayout() throws Throwable {
+        RecyclerView recyclerView = new RecyclerView(getActivity());
+        TestLayoutManager layoutManager = new DumbLayoutManager();
+        TestAdapter testAdapter = new TestAdapter(10);
+        setupBasic(recyclerView, layoutManager, testAdapter, true);
+        assertFalse(mRecyclerView.hasPendingAdapterUpdates());
+    }
+
+    public void testHasPendingUpdatesWhenAdapterIsChanged() throws Throwable {
+        RecyclerView recyclerView = new RecyclerView(getActivity());
+        TestLayoutManager layoutManager = new DumbLayoutManager();
+        final TestAdapter testAdapter = new TestAdapter(10);
+        setupBasic(recyclerView, layoutManager, testAdapter, false);
+        assertPendingUpdatesAndLayout(layoutManager, new Runnable() {
+            @Override
+            public void run() {
+                testAdapter.notifyItemRemoved(1);
+            }
+        });
+        assertPendingUpdatesAndLayout(layoutManager, new Runnable() {
+            @Override
+            public void run() {
+                testAdapter.notifyItemInserted(2);
+            }
+        });
+
+        assertPendingUpdatesAndLayout(layoutManager, new Runnable() {
+            @Override
+            public void run() {
+                testAdapter.notifyItemMoved(2, 3);
+            }
+        });
+
+        assertPendingUpdatesAndLayout(layoutManager, new Runnable() {
+            @Override
+            public void run() {
+                testAdapter.notifyItemChanged(2);
+            }
+        });
+
+        assertPendingUpdatesAndLayout(layoutManager, new Runnable() {
+            @Override
+            public void run() {
+                testAdapter.notifyDataSetChanged();
+            }
+        });
+    }
+
     public void testTransientStateRecycleViaAdapter() throws Throwable {
         transientStateRecycleTest(true, false);
     }
@@ -384,13 +469,13 @@
         runTestOnUiThread(new Runnable() {
             @Override
             public void run() {
-                for (int i = 0; i < tlm.getChildCount(); i ++) {
+                for (int i = 0; i < tlm.getChildCount(); i++) {
                     assertNotSame("adapter positions should not be undefined",
                             recyclerView.getChildAdapterPosition(tlm.getChildAt(i)),
                             RecyclerView.NO_POSITION);
                 }
                 adapter.notifyDataSetChanged();
-                for (int i = 0; i < tlm.getChildCount(); i ++) {
+                for (int i = 0; i < tlm.getChildCount(); i++) {
                     assertSame("adapter positions should be undefined",
                             recyclerView.getChildAdapterPosition(tlm.getChildAt(i)),
                             RecyclerView.NO_POSITION);
@@ -453,7 +538,7 @@
             @Override
             public void run(TestAdapter adapter) throws Throwable {
                 adapter.mItems.clear();
-                for (int i = 0; i < 20; i ++) {
+                for (int i = 0; i < 20; i++) {
                     adapter.mItems.add(new Item(i, "added item"));
                 }
                 adapter.notifyDataSetChanged();
@@ -461,6 +546,87 @@
         });
     }
 
+    public void testAvoidLeakingRecyclerViewIfViewIsNotRecycled() throws Throwable {
+        final AtomicBoolean failedToRecycle = new AtomicBoolean(false);
+        RecyclerView rv = new RecyclerView(getActivity());
+        TestLayoutManager tlm = new TestLayoutManager() {
+            @Override
+            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+                detachAndScrapAttachedViews(recycler);
+                layoutRange(recycler, 0, state.getItemCount());
+                layoutLatch.countDown();
+            }
+        };
+        TestAdapter adapter = new TestAdapter(10) {
+            @Override
+            public boolean onFailedToRecycleView(
+                    TestViewHolder holder) {
+                failedToRecycle.set(true);
+                return false;
+            }
+        };
+        rv.setAdapter(adapter);
+        rv.setLayoutManager(tlm);
+        tlm.expectLayouts(1);
+        setRecyclerView(rv);
+        tlm.waitForLayout(1);
+        final RecyclerView.ViewHolder vh = rv.getChildViewHolder(rv.getChildAt(0));
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ViewCompat.setHasTransientState(vh.itemView, true);
+            }
+        });
+        tlm.expectLayouts(1);
+        adapter.deleteAndNotify(0, 10);
+        tlm.waitForLayout(2);
+        final CountDownLatch animationsLatch = new CountDownLatch(1);
+        rv.getItemAnimator().isRunning(
+                new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
+                    @Override
+                    public void onAnimationsFinished() {
+                        animationsLatch.countDown();
+                    }
+                });
+        assertTrue(animationsLatch.await(2, TimeUnit.SECONDS));
+        assertTrue(failedToRecycle.get());
+        assertNull(vh.mOwnerRecyclerView);
+        checkForMainThreadException();
+    }
+
+    public void testAvoidLeakingRecyclerViewViaViewHolder() throws Throwable {
+        RecyclerView rv = new RecyclerView(getActivity());
+        TestLayoutManager tlm = new TestLayoutManager() {
+            @Override
+            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+                detachAndScrapAttachedViews(recycler);
+                layoutRange(recycler, 0, state.getItemCount());
+                layoutLatch.countDown();
+            }
+        };
+        TestAdapter adapter = new TestAdapter(10);
+        rv.setAdapter(adapter);
+        rv.setLayoutManager(tlm);
+        tlm.expectLayouts(1);
+        setRecyclerView(rv);
+        tlm.waitForLayout(1);
+        final RecyclerView.ViewHolder vh = rv.getChildViewHolder(rv.getChildAt(0));
+        tlm.expectLayouts(1);
+        adapter.deleteAndNotify(0, 10);
+        tlm.waitForLayout(2);
+        final CountDownLatch animationsLatch = new CountDownLatch(1);
+        rv.getItemAnimator().isRunning(
+                new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
+                    @Override
+                    public void onAnimationsFinished() {
+                        animationsLatch.countDown();
+                    }
+                });
+        assertTrue(animationsLatch.await(2, TimeUnit.SECONDS));
+        assertNull(vh.mOwnerRecyclerView);
+        checkForMainThreadException();
+    }
+
     public void adapterPositionsTest(final AdapterRunnable adapterChanges) throws Throwable {
         final TestAdapter testAdapter = new TestAdapter(10);
         TestLayoutManager tlm = new TestLayoutManager() {
@@ -488,7 +654,7 @@
                     final int count = recyclerView.getChildCount();
                     Map<View, Integer> layoutPositions = new HashMap<View, Integer>();
                     assertTrue("test sanity", count > 0);
-                    for (int i = 0; i < count; i ++) {
+                    for (int i = 0; i < count; i++) {
                         View view = recyclerView.getChildAt(i);
                         TestViewHolder vh = (TestViewHolder) recyclerView.getChildViewHolder(view);
                         int index = testAdapter.mItems.indexOf(vh.mBoundItem);
@@ -507,7 +673,7 @@
                             int index = testAdapter.mItems.indexOf(vh.mBoundItem);
                             if (index >= 0) {
                                 assertEquals("should be able to find VH with adapter position "
-                                        + index, vh,
+                                                + index, vh,
                                         recyclerView.findViewHolderForAdapterPosition(index));
                             }
                             assertSame("get adapter position should return correct index", index,
@@ -975,7 +1141,7 @@
                 rv.smoothScrollBy(0, 5000 - soFar);
             }
         });
-        while(rv.getScrollState() != SCROLL_STATE_IDLE) {
+        while (rv.getScrollState() != SCROLL_STATE_IDLE) {
             Thread.sleep(100);
         }
         final int soFar = totalScrolled.get();
@@ -1413,7 +1579,8 @@
             changes.put(mRecyclerView.findViewHolderForLayoutPosition(i).getItemId(), false);
         }
         for (int i = 0; i < changedItems.length; i++) {
-            changes.put(mRecyclerView.findViewHolderForLayoutPosition(changedItems[i]).getItemId(), true);
+            changes.put(mRecyclerView.findViewHolderForLayoutPosition(changedItems[i]).getItemId(),
+                    true);
         }
         testLayoutManager.expectLayouts(1);
         adapter.changePositionsAndNotify(changedItems);
@@ -2226,6 +2393,143 @@
         checkForMainThreadException();
     }
 
+    public void testUpdateHiddenView() throws Throwable {
+        final RecyclerView.ViewHolder[] mTargetVH = new RecyclerView.ViewHolder[1];
+        final RecyclerView recyclerView = new RecyclerView(getActivity());
+        final int[] preLayoutRange = new int[]{0, 10};
+        final int[] postLayoutRange = new int[]{0, 10};
+        final AtomicBoolean enableGetViewTest = new AtomicBoolean(false);
+        final List<Integer> disappearingPositions = new ArrayList<Integer>();
+        final TestLayoutManager tlm = new TestLayoutManager() {
+            @Override
+            public boolean supportsPredictiveItemAnimations() {
+                return true;
+            }
+
+            @Override
+            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+                try {
+                    final int[] layoutRange = state.isPreLayout() ? preLayoutRange
+                            : postLayoutRange;
+                    detachAndScrapAttachedViews(recycler);
+                    layoutRange(recycler, layoutRange[0], layoutRange[1]);
+                    if (!state.isPreLayout()) {
+                        for (Integer position : disappearingPositions) {
+                            // test sanity.
+                            assertNull(findViewByPosition(position));
+                            final View view = recycler.getViewForPosition(position);
+                            addDisappearingView(view);
+                            measureChildWithMargins(view, 0, 0);
+                            // position item out of bounds.
+                            view.layout(0, -500, view.getMeasuredWidth(),
+                                    -500 + view.getMeasuredHeight());
+                        }
+                    }
+                } catch (Throwable t) {
+                    postExceptionToInstrumentation(t);
+                }
+                layoutLatch.countDown();
+            }
+        };
+
+        recyclerView.getItemAnimator().setMoveDuration(2000);
+        recyclerView.getItemAnimator().setRemoveDuration(2000);
+        final TestAdapter adapter = new TestAdapter(100);
+        recyclerView.setAdapter(adapter);
+        recyclerView.setLayoutManager(tlm);
+        tlm.expectLayouts(1);
+        setRecyclerView(recyclerView);
+
+        tlm.waitForLayout(1);
+        checkForMainThreadException();
+        mTargetVH[0] = recyclerView.findViewHolderForAdapterPosition(0);
+        // now, a child disappears
+        disappearingPositions.add(0);
+        // layout one shifted
+        postLayoutRange[0] = 1;
+        postLayoutRange[1] = 11;
+        tlm.expectLayouts(2);
+        adapter.addAndNotify(8, 1);
+        tlm.waitForLayout(2);
+        checkForMainThreadException();
+
+        tlm.expectLayouts(2);
+        disappearingPositions.clear();
+        // now that item should be moving, invalidate it and delete it.
+        enableGetViewTest.set(true);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    adapter.changeAndNotify(0, 1);
+                    adapter.deleteAndNotify(0, 1);
+                } catch (Throwable throwable) {
+                    throwable.printStackTrace();
+                }
+            }
+        });
+        tlm.waitForLayout(2);
+        checkForMainThreadException();
+    }
+
+    public void testFocusRectOnScreenWithDecorOffsets() throws Throwable {
+        focusRectOnScreenTest(true);
+    }
+
+    public void testFocusRectOnScreenWithout() throws Throwable {
+        focusRectOnScreenTest(false);
+    }
+
+
+    public void focusRectOnScreenTest(boolean addItemDecors) throws Throwable {
+        RecyclerView rv = new RecyclerView(getActivity());
+        final AtomicInteger scrollDist = new AtomicInteger(0);
+        TestLayoutManager tlm = new TestLayoutManager() {
+            @Override
+            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+                detachAndScrapAttachedViews(recycler);
+                final View view = recycler.getViewForPosition(0);
+                addView(view);
+                measureChildWithMargins(view, 0, 0);
+                view.layout(0, -20, view.getWidth(),
+                        -20 + view.getHeight());// ignore decors on purpose
+                layoutLatch.countDown();
+            }
+
+            @Override
+            public boolean canScrollVertically() {
+                return true;
+            }
+
+            @Override
+            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
+                    RecyclerView.State state) {
+                scrollDist.addAndGet(dy);
+                return dy;
+            }
+        };
+        TestAdapter adapter = new TestAdapter(10);
+        if (addItemDecors) {
+            rv.addItemDecoration(new RecyclerView.ItemDecoration() {
+                @Override
+                public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+                        RecyclerView.State state) {
+                    outRect.set(0, 10, 0, 10);
+                }
+            });
+        }
+        rv.setAdapter(adapter);
+        rv.setLayoutManager(tlm);
+        tlm.expectLayouts(1);
+        setRecyclerView(rv);
+        tlm.waitForLayout(2);
+
+        View view = rv.getChildAt(0);
+        requestFocus(view);
+        Thread.sleep(1000);
+        assertEquals(addItemDecors ? -30 : -20, scrollDist.get());
+    }
+
     private static class TestViewHolder2 extends RecyclerView.ViewHolder {
 
         Object mData;
@@ -2265,6 +2569,7 @@
     }
 
     private static interface AdapterRunnable {
+
         public void run(TestAdapter adapter) throws Throwable;
     }
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
index e23a114..f226307 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
@@ -208,20 +208,6 @@
         checkForMainThreadException();
     }
 
-    public void testGrowLookup() throws Throwable {
-        setupByConfig(new Config(VERTICAL, false, 3, GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS));
-        waitFirstLayout();
-        mLayoutManager.expectLayouts(1);
-        mAdapter.mItems.clear();
-        mAdapter.dispatchDataSetChanged();
-        mLayoutManager.waitForLayout(2);
-        checkForMainThreadException();
-        mLayoutManager.expectLayouts(2);
-        mAdapter.addAndNotify(0, 30);
-        mLayoutManager.waitForLayout(2);
-        checkForMainThreadException();
-    }
-
     public void testRTL() throws Throwable {
         for (boolean changeRtlAfter : new boolean[]{false, true}) {
             for (Config config : mBaseVariations) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/TestActivity.java b/v7/recyclerview/tests/src/android/support/v7/widget/TestActivity.java
index a0b165c..a4cb473 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/TestActivity.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/TestActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.os.Bundle;
+import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 public class TestActivity extends Activity {
@@ -28,6 +29,8 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mContainer = new FrameLayout(this);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         setContentView(mContainer);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
     }
 }
diff --git a/v7/vectordrawable/Android.mk b/v7/vectordrawable/Android.mk
new file mode 100644
index 0000000..c42b209
--- /dev/null
+++ b/v7/vectordrawable/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v7-vectordrawable
+LOCAL_SDK_VERSION := 7
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v7/vectordrawable/runtest.sh b/v7/vectordrawable/runtest.sh
new file mode 100755
index 0000000..f19a537
--- /dev/null
+++ b/v7/vectordrawable/runtest.sh
@@ -0,0 +1,5 @@
+. ../../../../build/envsetup.sh
+mmm -j20 . && mmm -j20 ./tests/ && \
+adb install -r $OUT/data/app/AndroidVectorDrawableTests/AndroidVectorDrawableTests.apk && \
+adb shell am start -n android.support.v7.vectordrawable/android.support.v7.vectordrawable.TestActivity
+
diff --git a/v7/vectordrawable/src/android/support/v7/graphics/drawable/PathParser.java b/v7/vectordrawable/src/android/support/v7/graphics/drawable/PathParser.java
new file mode 100644
index 0000000..2f6b9d1
--- /dev/null
+++ b/v7/vectordrawable/src/android/support/v7/graphics/drawable/PathParser.java
@@ -0,0 +1,718 @@
+/*
+ * 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.v7.graphics.drawable;
+
+import android.graphics.Path;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+// This class is a duplicate from the PathParser.java of frameworks/base, with slight
+// update on incompatible API like copyOfRange().
+class PathParser {
+    private static final String LOGTAG = "PathParser";
+
+    // Copy from Arrays.copyOfRange() which is only available from API level 9.
+    /**
+     * Copies elements from {@code original} into a new array, from indexes start (inclusive) to
+     * end (exclusive). The original order of elements is preserved.
+     * If {@code end} is greater than {@code original.length}, the result is padded
+     * with the value {@code 0.0f}.
+     *
+     * @param original the original array
+     * @param start the start index, inclusive
+     * @param end the end index, exclusive
+     * @return the new array
+     * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length}
+     * @throws IllegalArgumentException if {@code start > end}
+     * @throws NullPointerException if {@code original == null}
+     */
+    private static float[] copyOfRange(float[] original, int start, int end) {
+        if (start > end) {
+            throw new IllegalArgumentException();
+        }
+        int originalLength = original.length;
+        if (start < 0 || start > originalLength) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        int resultLength = end - start;
+        int copyLength = Math.min(resultLength, originalLength - start);
+        float[] result = new float[resultLength];
+        System.arraycopy(original, start, result, 0, copyLength);
+        return result;
+    }
+
+    /**
+     * @param pathData The string representing a path, the same as "d" string in svg file.
+     * @return the generated Path object.
+     */
+    public static Path createPathFromPathData(String pathData) {
+        Path path = new Path();
+        PathDataNode[] nodes = createNodesFromPathData(pathData);
+        if (nodes != null) {
+            try {
+                PathDataNode.nodesToPath(nodes, path);
+            } catch (RuntimeException e) {
+                throw new RuntimeException("Error in parsing " + pathData, e);
+            }
+            return path;
+        }
+        return null;
+    }
+
+    /**
+     * @param pathData The string representing a path, the same as "d" string in svg file.
+     * @return an array of the PathDataNode.
+     */
+    public static PathDataNode[] createNodesFromPathData(String pathData) {
+        if (pathData == null) {
+            return null;
+        }
+        int start = 0;
+        int end = 1;
+
+        ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
+        while (end < pathData.length()) {
+            end = nextStart(pathData, end);
+            String s = pathData.substring(start, end).trim();
+            if (s.length() > 0) {
+                float[] val = getFloats(s);
+                addNode(list, s.charAt(0), val);
+            }
+
+            start = end;
+            end++;
+        }
+        if ((end - start) == 1 && start < pathData.length()) {
+            addNode(list, pathData.charAt(start), new float[0]);
+        }
+        return list.toArray(new PathDataNode[list.size()]);
+    }
+
+    /**
+     * @param source The array of PathDataNode to be duplicated.
+     * @return a deep copy of the <code>source</code>.
+     */
+    public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {
+        if (source == null) {
+            return null;
+        }
+        PathDataNode[] copy = new PathParser.PathDataNode[source.length];
+        for (int i = 0; i < source.length; i ++) {
+            copy[i] = new PathDataNode(source[i]);
+        }
+        return copy;
+    }
+
+    /**
+     * @param nodesFrom The source path represented in an array of PathDataNode
+     * @param nodesTo The target path represented in an array of PathDataNode
+     * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code>
+     */
+    public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
+        if (nodesFrom == null || nodesTo == null) {
+            return false;
+        }
+
+        if (nodesFrom.length != nodesTo.length) {
+            return false;
+        }
+
+        for (int i = 0; i < nodesFrom.length; i ++) {
+            if (nodesFrom[i].mType != nodesTo[i].mType
+                    || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Update the target's data to match the source.
+     * Before calling this, make sure canMorph(target, source) is true.
+     *
+     * @param target The target path represented in an array of PathDataNode
+     * @param source The source path represented in an array of PathDataNode
+     */
+    public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
+        for (int i = 0; i < source.length; i ++) {
+            target[i].mType = source[i].mType;
+            for (int j = 0; j < source[i].mParams.length; j ++) {
+                target[i].mParams[j] = source[i].mParams[j];
+            }
+        }
+    }
+
+    private static int nextStart(String s, int end) {
+        char c;
+
+        while (end < s.length()) {
+            c = s.charAt(end);
+            // Note that 'e' or 'E' are not valid path commands, but could be
+            // used for floating point numbers' scientific notation.
+            // Therefore, when searching for next command, we should ignore 'e'
+            // and 'E'.
+            if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
+                    && c != 'e' && c != 'E') {
+                return end;
+            }
+            end++;
+        }
+        return end;
+    }
+
+    private static void addNode(ArrayList<PathDataNode> list, char cmd, float[] val) {
+        list.add(new PathDataNode(cmd, val));
+    }
+
+    private static class ExtractFloatResult {
+        // We need to return the position of the next separator and whether the
+        // next float starts with a '-' or a '.'.
+        int mEndPosition;
+        boolean mEndWithNegOrDot;
+    }
+
+    /**
+     * Parse the floats in the string.
+     * This is an optimized version of parseFloat(s.split(",|\\s"));
+     *
+     * @param s the string containing a command and list of floats
+     * @return array of floats
+     */
+    private static float[] getFloats(String s) {
+        if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') {
+            return new float[0];
+        }
+        try {
+            float[] results = new float[s.length()];
+            int count = 0;
+            int startPosition = 1;
+            int endPosition = 0;
+
+            ExtractFloatResult result = new ExtractFloatResult();
+            int totalLength = s.length();
+
+            // The startPosition should always be the first character of the
+            // current number, and endPosition is the character after the current
+            // number.
+            while (startPosition < totalLength) {
+                extract(s, startPosition, result);
+                endPosition = result.mEndPosition;
+
+                if (startPosition < endPosition) {
+                    results[count++] = Float.parseFloat(
+                            s.substring(startPosition, endPosition));
+                }
+
+                if (result.mEndWithNegOrDot) {
+                    // Keep the '-' or '.' sign with next number.
+                    startPosition = endPosition;
+                } else {
+                    startPosition = endPosition + 1;
+                }
+            }
+            return copyOfRange(results, 0, count);
+        } catch (NumberFormatException e) {
+            throw new RuntimeException("error in parsing \"" + s + "\"", e);
+        }
+    }
+
+    /**
+     * Calculate the position of the next comma or space or negative sign
+     * @param s the string to search
+     * @param start the position to start searching
+     * @param result the result of the extraction, including the position of the
+     * the starting position of next number, whether it is ending with a '-'.
+     */
+    private static void extract(String s, int start, ExtractFloatResult result) {
+        // Now looking for ' ', ',', '.' or '-' from the start.
+        int currentIndex = start;
+        boolean foundSeparator = false;
+        result.mEndWithNegOrDot = false;
+        boolean secondDot = false;
+        boolean isExponential = false;
+        for (; currentIndex < s.length(); currentIndex++) {
+            boolean isPrevExponential = isExponential;
+            isExponential = false;
+            char currentChar = s.charAt(currentIndex);
+            switch (currentChar) {
+                case ' ':
+                case ',':
+                    foundSeparator = true;
+                    break;
+                case '-':
+                    // The negative sign following a 'e' or 'E' is not a separator.
+                    if (currentIndex != start && !isPrevExponential) {
+                        foundSeparator = true;
+                        result.mEndWithNegOrDot = true;
+                    }
+                    break;
+                case '.':
+                    if (!secondDot) {
+                        secondDot = true;
+                    } else {
+                        // This is the second dot, and it is considered as a separator.
+                        foundSeparator = true;
+                        result.mEndWithNegOrDot = true;
+                    }
+                    break;
+                case 'e':
+                case 'E':
+                    isExponential = true;
+                    break;
+            }
+            if (foundSeparator) {
+                break;
+            }
+        }
+        // When there is nothing found, then we put the end position to the end
+        // of the string.
+        result.mEndPosition = currentIndex;
+    }
+
+    /**
+     * Each PathDataNode represents one command in the "d" attribute of the svg
+     * file.
+     * An array of PathDataNode can represent the whole "d" attribute.
+     */
+    public static class PathDataNode {
+        private char mType;
+        private float[] mParams;
+
+        private PathDataNode(char type, float[] params) {
+            mType = type;
+            mParams = params;
+        }
+
+        private PathDataNode(PathDataNode n) {
+            mType = n.mType;
+            mParams = copyOfRange(n.mParams, 0, n.mParams.length);
+        }
+
+        /**
+         * Convert an array of PathDataNode to Path.
+         *
+         * @param node The source array of PathDataNode.
+         * @param path The target Path object.
+         */
+        public static void nodesToPath(PathDataNode[] node, Path path) {
+            float[] current = new float[6];
+            char previousCommand = 'm';
+            for (int i = 0; i < node.length; i++) {
+                addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
+                previousCommand = node[i].mType;
+            }
+        }
+
+        /**
+         * The current PathDataNode will be interpolated between the
+         * <code>nodeFrom</code> and <code>nodeTo</code> according to the
+         * <code>fraction</code>.
+         *
+         * @param nodeFrom The start value as a PathDataNode.
+         * @param nodeTo The end value as a PathDataNode
+         * @param fraction The fraction to interpolate.
+         */
+        public void interpolatePathDataNode(PathDataNode nodeFrom,
+                PathDataNode nodeTo, float fraction) {
+            for (int i = 0; i < nodeFrom.mParams.length; i++) {
+                mParams[i] = nodeFrom.mParams[i] * (1 - fraction)
+                        + nodeTo.mParams[i] * fraction;
+            }
+        }
+
+        private static void addCommand(Path path, float[] current,
+                char previousCmd, char cmd, float[] val) {
+
+            int incr = 2;
+            float currentX = current[0];
+            float currentY = current[1];
+            float ctrlPointX = current[2];
+            float ctrlPointY = current[3];
+            float currentSegmentStartX = current[4];
+            float currentSegmentStartY = current[5];
+            float reflectiveCtrlPointX;
+            float reflectiveCtrlPointY;
+
+            switch (cmd) {
+                case 'z':
+                case 'Z':
+                    path.close();
+                    // Path is closed here, but we need to move the pen to the
+                    // closed position. So we cache the segment's starting position,
+                    // and restore it here.
+                    currentX = currentSegmentStartX;
+                    currentY = currentSegmentStartY;
+                    ctrlPointX = currentSegmentStartX;
+                    ctrlPointY = currentSegmentStartY;
+                    path.moveTo(currentX, currentY);
+                    break;
+                case 'm':
+                case 'M':
+                case 'l':
+                case 'L':
+                case 't':
+                case 'T':
+                    incr = 2;
+                    break;
+                case 'h':
+                case 'H':
+                case 'v':
+                case 'V':
+                    incr = 1;
+                    break;
+                case 'c':
+                case 'C':
+                    incr = 6;
+                    break;
+                case 's':
+                case 'S':
+                case 'q':
+                case 'Q':
+                    incr = 4;
+                    break;
+                case 'a':
+                case 'A':
+                    incr = 7;
+                    break;
+            }
+
+            for (int k = 0; k < val.length; k += incr) {
+                switch (cmd) {
+                    case 'm': // moveto - Start a new sub-path (relative)
+                        path.rMoveTo(val[k + 0], val[k + 1]);
+                        currentX += val[k + 0];
+                        currentY += val[k + 1];
+                        currentSegmentStartX = currentX;
+                        currentSegmentStartY = currentY;
+                        break;
+                    case 'M': // moveto - Start a new sub-path
+                        path.moveTo(val[k + 0], val[k + 1]);
+                        currentX = val[k + 0];
+                        currentY = val[k + 1];
+                        currentSegmentStartX = currentX;
+                        currentSegmentStartY = currentY;
+                        break;
+                    case 'l': // lineto - Draw a line from the current point (relative)
+                        path.rLineTo(val[k + 0], val[k + 1]);
+                        currentX += val[k + 0];
+                        currentY += val[k + 1];
+                        break;
+                    case 'L': // lineto - Draw a line from the current point
+                        path.lineTo(val[k + 0], val[k + 1]);
+                        currentX = val[k + 0];
+                        currentY = val[k + 1];
+                        break;
+                    case 'h': // horizontal lineto - Draws a horizontal line (relative)
+                        path.rLineTo(val[k + 0], 0);
+                        currentX += val[k + 0];
+                        break;
+                    case 'H': // horizontal lineto - Draws a horizontal line
+                        path.lineTo(val[k + 0], currentY);
+                        currentX = val[k + 0];
+                        break;
+                    case 'v': // vertical lineto - Draws a vertical line from the current point (r)
+                        path.rLineTo(0, val[k + 0]);
+                        currentY += val[k + 0];
+                        break;
+                    case 'V': // vertical lineto - Draws a vertical line from the current point
+                        path.lineTo(currentX, val[k + 0]);
+                        currentY = val[k + 0];
+                        break;
+                    case 'c': // curveto - Draws a cubic Bézier curve (relative)
+                        path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
+                                val[k + 4], val[k + 5]);
+
+                        ctrlPointX = currentX + val[k + 2];
+                        ctrlPointY = currentY + val[k + 3];
+                        currentX += val[k + 4];
+                        currentY += val[k + 5];
+
+                        break;
+                    case 'C': // curveto - Draws a cubic Bézier curve
+                        path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
+                                val[k + 4], val[k + 5]);
+                        currentX = val[k + 4];
+                        currentY = val[k + 5];
+                        ctrlPointX = val[k + 2];
+                        ctrlPointY = val[k + 3];
+                        break;
+                    case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
+                        reflectiveCtrlPointX = 0;
+                        reflectiveCtrlPointY = 0;
+                        if (previousCmd == 'c' || previousCmd == 's'
+                                || previousCmd == 'C' || previousCmd == 'S') {
+                            reflectiveCtrlPointX = currentX - ctrlPointX;
+                            reflectiveCtrlPointY = currentY - ctrlPointY;
+                        }
+                        path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1],
+                                val[k + 2], val[k + 3]);
+
+                        ctrlPointX = currentX + val[k + 0];
+                        ctrlPointY = currentY + val[k + 1];
+                        currentX += val[k + 2];
+                        currentY += val[k + 3];
+                        break;
+                    case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
+                        reflectiveCtrlPointX = currentX;
+                        reflectiveCtrlPointY = currentY;
+                        if (previousCmd == 'c' || previousCmd == 's'
+                                || previousCmd == 'C' || previousCmd == 'S') {
+                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+                        }
+                        path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+                        ctrlPointX = val[k + 0];
+                        ctrlPointY = val[k + 1];
+                        currentX = val[k + 2];
+                        currentY = val[k + 3];
+                        break;
+                    case 'q': // Draws a quadratic Bézier (relative)
+                        path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+                        ctrlPointX = currentX + val[k + 0];
+                        ctrlPointY = currentY + val[k + 1];
+                        currentX += val[k + 2];
+                        currentY += val[k + 3];
+                        break;
+                    case 'Q': // Draws a quadratic Bézier
+                        path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+                        ctrlPointX = val[k + 0];
+                        ctrlPointY = val[k + 1];
+                        currentX = val[k + 2];
+                        currentY = val[k + 3];
+                        break;
+                    case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
+                        reflectiveCtrlPointX = 0;
+                        reflectiveCtrlPointY = 0;
+                        if (previousCmd == 'q' || previousCmd == 't'
+                                || previousCmd == 'Q' || previousCmd == 'T') {
+                            reflectiveCtrlPointX = currentX - ctrlPointX;
+                            reflectiveCtrlPointY = currentY - ctrlPointY;
+                        }
+                        path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1]);
+                        ctrlPointX = currentX + reflectiveCtrlPointX;
+                        ctrlPointY = currentY + reflectiveCtrlPointY;
+                        currentX += val[k + 0];
+                        currentY += val[k + 1];
+                        break;
+                    case 'T': // Draws a quadratic Bézier curve (reflective control point)
+                        reflectiveCtrlPointX = currentX;
+                        reflectiveCtrlPointY = currentY;
+                        if (previousCmd == 'q' || previousCmd == 't'
+                                || previousCmd == 'Q' || previousCmd == 'T') {
+                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+                        }
+                        path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1]);
+                        ctrlPointX = reflectiveCtrlPointX;
+                        ctrlPointY = reflectiveCtrlPointY;
+                        currentX = val[k + 0];
+                        currentY = val[k + 1];
+                        break;
+                    case 'a': // Draws an elliptical arc
+                        // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
+                        drawArc(path,
+                                currentX,
+                                currentY,
+                                val[k + 5] + currentX,
+                                val[k + 6] + currentY,
+                                val[k + 0],
+                                val[k + 1],
+                                val[k + 2],
+                                val[k + 3] != 0,
+                                val[k + 4] != 0);
+                        currentX += val[k + 5];
+                        currentY += val[k + 6];
+                        ctrlPointX = currentX;
+                        ctrlPointY = currentY;
+                        break;
+                    case 'A': // Draws an elliptical arc
+                        drawArc(path,
+                                currentX,
+                                currentY,
+                                val[k + 5],
+                                val[k + 6],
+                                val[k + 0],
+                                val[k + 1],
+                                val[k + 2],
+                                val[k + 3] != 0,
+                                val[k + 4] != 0);
+                        currentX = val[k + 5];
+                        currentY = val[k + 6];
+                        ctrlPointX = currentX;
+                        ctrlPointY = currentY;
+                        break;
+                }
+                previousCmd = cmd;
+            }
+            current[0] = currentX;
+            current[1] = currentY;
+            current[2] = ctrlPointX;
+            current[3] = ctrlPointY;
+            current[4] = currentSegmentStartX;
+            current[5] = currentSegmentStartY;
+        }
+
+        private static void drawArc(Path p,
+                float x0,
+                float y0,
+                float x1,
+                float y1,
+                float a,
+                float b,
+                float theta,
+                boolean isMoreThanHalf,
+                boolean isPositiveArc) {
+
+            /* Convert rotation angle from degrees to radians */
+            double thetaD = Math.toRadians(theta);
+            /* Pre-compute rotation matrix entries */
+            double cosTheta = Math.cos(thetaD);
+            double sinTheta = Math.sin(thetaD);
+            /* Transform (x0, y0) and (x1, y1) into unit space */
+            /* using (inverse) rotation, followed by (inverse) scale */
+            double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
+            double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
+            double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
+            double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
+
+            /* Compute differences and averages */
+            double dx = x0p - x1p;
+            double dy = y0p - y1p;
+            double xm = (x0p + x1p) / 2;
+            double ym = (y0p + y1p) / 2;
+            /* Solve for intersecting unit circles */
+            double dsq = dx * dx + dy * dy;
+            if (dsq == 0.0) {
+                Log.w(LOGTAG, " Points are coincident");
+                return; /* Points are coincident */
+            }
+            double disc = 1.0 / dsq - 1.0 / 4.0;
+            if (disc < 0.0) {
+                Log.w(LOGTAG, "Points are too far apart " + dsq);
+                float adjust = (float) (Math.sqrt(dsq) / 1.99999);
+                drawArc(p, x0, y0, x1, y1, a * adjust,
+                        b * adjust, theta, isMoreThanHalf, isPositiveArc);
+                return; /* Points are too far apart */
+            }
+            double s = Math.sqrt(disc);
+            double sdx = s * dx;
+            double sdy = s * dy;
+            double cx;
+            double cy;
+            if (isMoreThanHalf == isPositiveArc) {
+                cx = xm - sdy;
+                cy = ym + sdx;
+            } else {
+                cx = xm + sdy;
+                cy = ym - sdx;
+            }
+
+            double eta0 = Math.atan2((y0p - cy), (x0p - cx));
+
+            double eta1 = Math.atan2((y1p - cy), (x1p - cx));
+
+            double sweep = (eta1 - eta0);
+            if (isPositiveArc != (sweep >= 0)) {
+                if (sweep > 0) {
+                    sweep -= 2 * Math.PI;
+                } else {
+                    sweep += 2 * Math.PI;
+                }
+            }
+
+            cx *= a;
+            cy *= b;
+            double tcx = cx;
+            cx = cx * cosTheta - cy * sinTheta;
+            cy = tcx * sinTheta + cy * cosTheta;
+
+            arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
+        }
+
+        /**
+         * Converts an arc to cubic Bezier segments and records them in p.
+         *
+         * @param p The target for the cubic Bezier segments
+         * @param cx The x coordinate center of the ellipse
+         * @param cy The y coordinate center of the ellipse
+         * @param a The radius of the ellipse in the horizontal direction
+         * @param b The radius of the ellipse in the vertical direction
+         * @param e1x E(eta1) x coordinate of the starting point of the arc
+         * @param e1y E(eta2) y coordinate of the starting point of the arc
+         * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
+         * @param start The start angle of the arc on the ellipse
+         * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
+         */
+        private static void arcToBezier(Path p,
+                double cx,
+                double cy,
+                double a,
+                double b,
+                double e1x,
+                double e1y,
+                double theta,
+                double start,
+                double sweep) {
+            // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
+            // and http://www.spaceroots.org/documents/ellipse/node22.html
+
+            // Maximum of 45 degrees per cubic Bezier segment
+            int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
+
+            double eta1 = start;
+            double cosTheta = Math.cos(theta);
+            double sinTheta = Math.sin(theta);
+            double cosEta1 = Math.cos(eta1);
+            double sinEta1 = Math.sin(eta1);
+            double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
+            double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
+
+            double anglePerSegment = sweep / numSegments;
+            for (int i = 0; i < numSegments; i++) {
+                double eta2 = eta1 + anglePerSegment;
+                double sinEta2 = Math.sin(eta2);
+                double cosEta2 = Math.cos(eta2);
+                double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
+                double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
+                double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
+                double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
+                double tanDiff2 = Math.tan((eta2 - eta1) / 2);
+                double alpha =
+                        Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
+                double q1x = e1x + alpha * ep1x;
+                double q1y = e1y + alpha * ep1y;
+                double q2x = e2x - alpha * ep2x;
+                double q2y = e2y - alpha * ep2y;
+
+                p.cubicTo((float) q1x,
+                        (float) q1y,
+                        (float) q2x,
+                        (float) q2y,
+                        (float) e2x,
+                        (float) e2y);
+                eta1 = eta2;
+                e1x = e2x;
+                e1y = e2y;
+                ep1x = ep2x;
+                ep1y = ep2y;
+            }
+        }
+    }
+}
diff --git a/v7/vectordrawable/src/android/support/v7/graphics/drawable/VectorDrawableCompat.java b/v7/vectordrawable/src/android/support/v7/graphics/drawable/VectorDrawableCompat.java
new file mode 100644
index 0000000..f651999
--- /dev/null
+++ b/v7/vectordrawable/src/android/support/v7/graphics/drawable/VectorDrawableCompat.java
@@ -0,0 +1,169 @@
+/*
+ * 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.v7.graphics.drawable;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Support lib for {@link android.graphics.drawable.VectorDrawable}
+ * This is a duplication of VectorDrawable.java from frameworks/base, with major
+ * changes in XML parsing parts.
+ */
+public class VectorDrawableCompat extends Drawable {
+    private static final String LOG_TAG = "VectorDrawable";
+
+    private static final String SHAPE_CLIP_PATH = "clip-path";
+    private static final String SHAPE_GROUP = "group";
+    private static final String SHAPE_PATH = "path";
+    private static final String SHAPE_VECTOR = "vector";
+
+    private static final boolean DBG_VECTOR_DRAWABLE = false;
+
+    private Path mPath;
+
+    @Override
+    public void draw(Canvas canvas) {
+        // Now just draw the last path.
+        // TODO: Be able to draw the vector drawable's group tree into the canvas.
+        canvas.drawRGB(255, 0, 0);
+
+        Paint testPaint = new Paint();
+        testPaint.setColor(0xff101010);
+        canvas.drawPath(mPath, testPaint);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+    }
+
+    @Override
+    public int getOpacity() {
+        return 0;
+    }
+
+    public static VectorDrawableCompat createFromResource(Resources res, int id) {
+        XmlPullParserFactory xppf = null;
+        XmlPullParser parser = null;
+        try {
+            xppf = XmlPullParserFactory.newInstance();
+            parser = xppf.newPullParser();
+            InputStream is = res.openRawResource(id);
+            parser.setInput(is, null);
+            // TODO: Use this getXml when the aapt is able to help us to keep the
+            // attributes for v-21 in the compiled version.
+            // XmlPullParser parser = res.getXml(id);
+
+            final AttributeSet attrs = Xml.asAttributeSet(parser);
+            final VectorDrawableCompat drawable = new VectorDrawableCompat();
+
+            drawable.inflateInternal(res, parser, attrs);
+
+            return drawable;
+        } catch (XmlPullParserException e) {
+            Log.e(LOG_TAG, "XmlPullParser exception for res id : " + id);
+        }
+        return null;
+    }
+
+    private Map<String, String> getAttributes(XmlPullParser parser) {
+        Map<String, String> attrs = null;
+        int attrCount = parser.getAttributeCount();
+        if (attrCount != -1) {
+            if (DBG_VECTOR_DRAWABLE) {
+                Log.v(LOG_TAG, "Attributes for [" + parser.getName() + "] " + attrCount);
+            }
+            attrs = new HashMap<String, String>(attrCount);
+            for (int i = 0; i < attrCount; i++) {
+                if (DBG_VECTOR_DRAWABLE) {
+                    Log.v(LOG_TAG, "\t[" + parser.getAttributeName(i) + "]=" +
+                            "[" + parser.getAttributeValue(i) + "]");
+                }
+                attrs.put(parser.getAttributeName(i), parser.getAttributeValue(i));
+            }
+        }
+        return attrs;
+    }
+
+    private void inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs) {
+        // TODO: Add more details in the parsing to reconstruct the
+        // VectorDrawable data structure.
+        int eventType;
+        try {
+            eventType = parser.getEventType();
+
+            while (eventType != XmlPullParser.END_DOCUMENT) {
+                if (eventType == XmlPullParser.START_DOCUMENT) {
+                    if (DBG_VECTOR_DRAWABLE) {
+                        Log.v(LOG_TAG, "Start document");
+                    }
+                } else if (eventType == XmlPullParser.START_TAG) {
+                    if (DBG_VECTOR_DRAWABLE) {
+                        Log.v(LOG_TAG,"Parsing Attributes for ["+parser.getName()+"]");
+                    }
+                    Map<String,String> attributes = getAttributes(parser);
+                    if (attributes != null) {
+                        final String tagName = parser.getName();
+                        if (SHAPE_PATH.equals(tagName)) {
+                            String pathString = attributes.get("android:pathData");
+                            if (DBG_VECTOR_DRAWABLE) {
+                                Log.v(LOG_TAG, "pathData is " + pathString);
+                            }
+                            mPath = PathParser.createPathFromPathData(pathString);
+                        }
+                    }
+                } else if (eventType == XmlPullParser.END_TAG) {
+                    if (DBG_VECTOR_DRAWABLE) {
+                        Log.v(LOG_TAG, "End tag " + parser.getName());
+                    }
+                } else if (eventType == XmlPullParser.TEXT) {
+                    if (DBG_VECTOR_DRAWABLE) {
+                        Log.v(LOG_TAG, "Text " + parser.getText());
+                    }
+                }
+                eventType = parser.next();
+            }
+        } catch (XmlPullParserException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        if (DBG_VECTOR_DRAWABLE) {
+            Log.v(LOG_TAG, "End document");
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/v7/vectordrawable/tests/Android.mk b/v7/vectordrawable/tests/Android.mk
new file mode 100644
index 0000000..1fef869
--- /dev/null
+++ b/v7/vectordrawable/tests/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR = \
+        $(LOCAL_PATH)/res \
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-vectordrawable
+
+LOCAL_PACKAGE_NAME := AndroidVectorDrawableTests
+
+include $(BUILD_PACKAGE)
diff --git a/v7/vectordrawable/tests/AndroidManifest.xml b/v7/vectordrawable/tests/AndroidManifest.xml
new file mode 100644
index 0000000..b0cdc53
--- /dev/null
+++ b/v7/vectordrawable/tests/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.support.v7.vectordrawable" >
+
+    <uses-sdk android:minSdkVersion="7" />
+
+    <application android:icon="@drawable/app_sample_code" android:label="VectorSupportTest" >
+        <activity android:name="android.support.v7.vectordrawable.TestActivity" />
+
+        <intent-filter>
+            <action android:name="android.intent.action.MAIN" />
+
+            <category android:name="android.intent.category.LAUNCHER" />
+        </intent-filter>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/v7/vectordrawable/tests/res/drawable/app_sample_code.png b/v7/vectordrawable/tests/res/drawable/app_sample_code.png
new file mode 100755
index 0000000..66a1984
--- /dev/null
+++ b/v7/vectordrawable/tests/res/drawable/app_sample_code.png
Binary files differ
diff --git a/v7/vectordrawable/tests/res/drawable/vector_drawable01.xml b/v7/vectordrawable/tests/res/drawable/vector_drawable01.xml
new file mode 100644
index 0000000..6c376db
--- /dev/null
+++ b/v7/vectordrawable/tests/res/drawable/vector_drawable01.xml
@@ -0,0 +1,31 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="48dp"
+        android:width="48dp"
+        android:viewportHeight="480"
+        android:viewportWidth="480" >
+
+    <group>
+        <path
+            android:name="box1"
+            android:pathData="m20,200l100,90l180-180l-35-35l-145,145l-60-60l-40,40z"
+            android:fillColor="?android:attr/colorControlActivated"
+            android:strokeColor="?android:attr/colorControlActivated"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round" />
+    </group>
+</vector>
diff --git a/v7/vectordrawable/tests/res/raw/vector_drawable01.xml b/v7/vectordrawable/tests/res/raw/vector_drawable01.xml
new file mode 100644
index 0000000..705cc34
--- /dev/null
+++ b/v7/vectordrawable/tests/res/raw/vector_drawable01.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="48dp"
+        android:width="48dp"
+        android:viewportHeight="480"
+        android:viewportWidth="480" >
+
+    <group>
+        <path
+            android:name="box1"
+            android:pathData="m20,200l100,90l180-180l-35-35l-145,145l-60-60l-40,40z"
+            android:fillColor="?android:attr/colorControlActivated"
+            android:strokeColor="?android:attr/colorControlActivated"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round" />
+    </group>
+</vector>
diff --git a/v7/vectordrawable/tests/src/android/support/v7/vectordrawable/TestActivity.java b/v7/vectordrawable/tests/src/android/support/v7/vectordrawable/TestActivity.java
new file mode 100644
index 0000000..a547086
--- /dev/null
+++ b/v7/vectordrawable/tests/src/android/support/v7/vectordrawable/TestActivity.java
@@ -0,0 +1,97 @@
+/*
+ * 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.v7.vectordrawable;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.support.v7.graphics.drawable.VectorDrawableCompat;
+import android.view.View;
+
+import java.io.InputStream;
+
+/**
+ * @hide from javadoc
+ */
+public class TestActivity extends Activity {
+    private static final String LOG_TAG = "TestActivity";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(new SampleView(this));
+    }
+
+    private static class SampleView extends View {
+        private Bitmap mBitmap;
+        private VectorDrawableCompat mDrawable;
+
+        private static void drawIntoBitmap(Bitmap bm) {
+            float x = bm.getWidth();
+            float y = bm.getHeight();
+            Canvas c = new Canvas(bm);
+            Paint p = new Paint();
+            p.setAntiAlias(true);
+
+            p.setAlpha(0x80);
+            c.drawCircle(x/2, y/2, x/2, p);
+
+            p.setAlpha(0x30);
+            p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+            p.setTextSize(60);
+            p.setTextAlign(Paint.Align.CENTER);
+            Paint.FontMetrics fm = p.getFontMetrics();
+            c.drawText("Alpha", x/2, (y-fm.ascent)/2, p);
+        }
+
+        public SampleView(Context context) {
+            super(context);
+            setFocusable(true);
+
+            InputStream is = context.getResources().openRawResource(R.drawable.app_sample_code);
+            mBitmap = BitmapFactory.decodeStream(is);
+
+            // TODO: use R.drawable.vector_drawable01 when the aapt is able to
+            // support.
+            mDrawable = VectorDrawableCompat.createFromResource(context.getResources(), R.raw.vector_drawable01);
+        }
+
+        @Override protected void onDraw(Canvas canvas) {
+            canvas.drawColor(Color.WHITE);
+
+            mDrawable.draw(canvas);
+
+            Paint p = new Paint();
+            float y = 10;
+
+            p.setColor(Color.RED);
+            canvas.drawBitmap(mBitmap, 10, y, p);
+            y += mBitmap.getHeight() + 10;
+
+        }
+    }
+
+}
diff --git a/v8/renderscript/Android.mk b/v8/renderscript/Android.mk
index a288a86..58f7219 100644
--- a/v8/renderscript/Android.mk
+++ b/v8/renderscript/Android.mk
@@ -21,8 +21,10 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_CFLAGS += -std=c++11
+
 LOCAL_MODULE := android-support-v8-renderscript
-LOCAL_SDK_VERSION := 18
+LOCAL_SDK_VERSION := 19
 LOCAL_SRC_FILES := $(call all-java-files-under, java/src)
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java b/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java
index eec493c..4b019e9 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java
@@ -18,6 +18,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 import android.content.res.Resources;
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
@@ -69,6 +70,7 @@
     boolean mConstrainedZ;
     boolean mReadAllowed = true;
     boolean mWriteAllowed = true;
+    boolean mAutoPadding = false;
     int mSelectedY;
     int mSelectedZ;
     int mSelectedLOD;
@@ -79,6 +81,70 @@
     int mCurrentDimZ;
     int mCurrentCount;
 
+    private Element.DataType validateObjectIsPrimitiveArray(Object d, boolean checkType) {
+        final Class c = d.getClass();
+        if (!c.isArray()) {
+            throw new RSIllegalArgumentException("Object passed is not an array of primitives.");
+        }
+        final Class cmp = c.getComponentType();
+        if (!cmp.isPrimitive()) {
+            throw new RSIllegalArgumentException("Object passed is not an Array of primitives.");
+        }
+
+        if (cmp == Long.TYPE) {
+            if (checkType) {
+                validateIsInt64();
+                return mType.mElement.mType;
+            }
+            return Element.DataType.SIGNED_64;
+        }
+
+        if (cmp == Integer.TYPE) {
+            if (checkType) {
+                validateIsInt32();
+                return mType.mElement.mType;
+            }
+            return Element.DataType.SIGNED_32;
+        }
+
+        if (cmp == Short.TYPE) {
+            if (checkType) {
+                validateIsInt16();
+                return mType.mElement.mType;
+            }
+            return Element.DataType.SIGNED_16;
+        }
+
+        if (cmp == Byte.TYPE) {
+            if (checkType) {
+                validateIsInt8();
+                return mType.mElement.mType;
+            }
+            return Element.DataType.SIGNED_8;
+        }
+
+        if (cmp == Float.TYPE) {
+            if (checkType) {
+                validateIsFloat32();
+            }
+            return Element.DataType.FLOAT_32;
+        }
+
+        if (cmp == Double.TYPE) {
+            if (checkType) {
+                validateIsFloat64();
+            }
+            return Element.DataType.FLOAT_64;
+        }
+        return null;
+    }
+
+    /*
+     * Hold reference to the shared allocation in compat context
+     * for Incremental Support Lib.
+     */
+    long mIncCompatAllocation;
+    boolean mIncAllocDestroyed;
     /**
      * The usage of the Allocation.  These signal to RenderScript where to place
      * the Allocation in memory.
@@ -158,8 +224,18 @@
         }
     }
 
+    /**
+     * Getter & Setter for the dummy allocation for Inc Support Lib.
+     *
+     */
+    public long getIncAllocID() {
+        return mIncCompatAllocation;
+    }
+    public void setIncAllocID(long id) {
+        mIncCompatAllocation = id;
+    }
 
-    private int getIDSafe() {
+    private long getIDSafe() {
         if (mAdaptedAllocation != null) {
             return mAdaptedAllocation.getID(mRS);
         }
@@ -189,6 +265,17 @@
     }
 
     /**
+     * @hide
+     * Enable/Disable AutoPadding for Vec3 elements.
+     *
+     * @param useAutoPadding True: enable AutoPadding; flase: disable AutoPadding
+     *
+     */
+    public void setAutoPadding(boolean useAutoPadding) {
+        mAutoPadding = useAutoPadding;
+    }
+
+    /**
      * Get the size of the Allocation in bytes.
      *
      * @return size of the Allocation in bytes.
@@ -218,7 +305,7 @@
         mBitmap = b;
     }
 
-    Allocation(int id, RenderScript rs, Type t, int usage) {
+    Allocation(long id, RenderScript rs, Type t, int usage) {
         super(id, rs);
         if ((usage & ~(USAGE_SCRIPT |
                        USAGE_GRAPHICS_TEXTURE |
@@ -240,9 +327,14 @@
 
         mType = t;
         mUsage = usage;
-        mSize = mType.getCount() * mType.getElement().getBytesSize();
+        mIncCompatAllocation = 0;
+        mIncAllocDestroyed = false;
 
         if (t != null) {
+            // TODO: A3D doesn't have Type info during creation, so we can't
+            // calculate the size ahead of time. We can possibly add a method
+            // to update the size in the future if it seems reasonable.
+            mSize = mType.getCount() * mType.getElement().getBytesSize();
             updateCacheInfo(t);
         }
         if (RenderScript.sUseGCHooks == true) {
@@ -262,6 +354,14 @@
         super.finalize();
     }
 
+    private void validateIsInt64() {
+        if ((mType.mElement.mType == Element.DataType.SIGNED_64) ||
+            (mType.mElement.mType == Element.DataType.UNSIGNED_64)) {
+            return;
+        }
+        throw new RSIllegalArgumentException(
+            "64 bit integer source does not match allocation type " + mType.mElement.mType);
+    }
 
     private void validateIsInt32() {
         if ((mType.mElement.mType == Element.DataType.SIGNED_32) ||
@@ -298,6 +398,14 @@
             "32 bit float source does not match allocation type " + mType.mElement.mType);
     }
 
+    private void validateIsFloat64() {
+        if (mType.mElement.mType == Element.DataType.FLOAT_64) {
+            return;
+        }
+        throw new RSIllegalArgumentException(
+            "64 bit float source does not match allocation type " + mType.mElement.mType);
+    }
+
     private void validateIsObject() {
         if ((mType.mElement.mType == Element.DataType.RS_ELEMENT) ||
             (mType.mElement.mType == Element.DataType.RS_TYPE) ||
@@ -387,11 +495,20 @@
             throw new RSIllegalArgumentException("Array size mismatch, allocation sizeX = " +
                                                  mCurrentCount + ", array length = " + d.length);
         }
-        int i[] = new int[d.length];
-        for (int ct=0; ct < d.length; ct++) {
-            i[ct] = d[ct].getID(mRS);
+
+        if (RenderScript.sPointerSize == 8) {
+            long i[] = new long[d.length * 4];
+            for (int ct=0; ct < d.length; ct++) {
+                i[ct * 4] = d[ct].getID(mRS);
+            }
+            copy1DRangeFromUnchecked(0, mCurrentCount, i);
+        } else {
+            int i[] = new int[d.length];
+            for (int ct=0; ct < d.length; ct++) {
+                i[ct] = (int)d[ct].getID(mRS);
+            }
+            copy1DRangeFromUnchecked(0, mCurrentCount, i);
         }
-        copy1DRangeFromUnchecked(0, mCurrentCount, i);
     }
 
     private void validateBitmapFormat(Bitmap b) {
@@ -449,6 +566,29 @@
         }
     }
 
+    private void copyFromUnchecked(Object array, Element.DataType dt, int arrayLen) {
+        mRS.validate();
+        if (mCurrentDimZ > 0) {
+            copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, array, dt, arrayLen);
+        } else if (mCurrentDimY > 0) {
+            copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, array, dt, arrayLen);
+        } else {
+            copy1DRangeFromUnchecked(0, mCurrentCount, array, dt, arrayLen);
+        }
+    }
+
+    /**
+     * Copy into this Allocation from an array. This method does not guarantee
+     * that the Allocation is compatible with the input buffer; it copies memory
+     * without reinterpretation.
+     *
+     * @param array The source data array
+     */
+    public void copyFromUnchecked(Object array) {
+        copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, false),
+                          java.lang.reflect.Array.getLength(array));
+    }
+
     /**
      * Copy into this Allocation from an array. This method does not guarantee
      * that the Allocation is compatible with the input buffer; it copies memory
@@ -457,15 +597,9 @@
      * @param d the source data array
      */
     public void copyFromUnchecked(int[] d) {
-        mRS.validate();
-        if (mCurrentDimZ > 0) {
-            copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
-        } else if (mCurrentDimY > 0) {
-            copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
-        } else {
-            copy1DRangeFromUnchecked(0, mCurrentCount, d);
-        }
+        copyFromUnchecked(d, Element.DataType.SIGNED_32, d.length);
     }
+
     /**
      * Copy into this Allocation from an array. This method does not guarantee
      * that the Allocation is compatible with the input buffer; it copies memory
@@ -474,15 +608,9 @@
      * @param d the source data array
      */
     public void copyFromUnchecked(short[] d) {
-        mRS.validate();
-        if (mCurrentDimZ > 0) {
-            copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
-        } else if (mCurrentDimY > 0) {
-            copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
-        } else {
-            copy1DRangeFromUnchecked(0, mCurrentCount, d);
-        }
+        copyFromUnchecked(d, Element.DataType.SIGNED_16, d.length);
     }
+
     /**
      * Copy into this Allocation from an array. This method does not guarantee
      * that the Allocation is compatible with the input buffer; it copies memory
@@ -491,15 +619,9 @@
      * @param d the source data array
      */
     public void copyFromUnchecked(byte[] d) {
-        mRS.validate();
-        if (mCurrentDimZ > 0) {
-            copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
-        } else if (mCurrentDimY > 0) {
-            copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
-        } else {
-            copy1DRangeFromUnchecked(0, mCurrentCount, d);
-        }
+        copyFromUnchecked(d, Element.DataType.SIGNED_8, d.length);
     }
+
     /**
      * Copy into this Allocation from an array. This method does not guarantee
      * that the Allocation is compatible with the input buffer; it copies memory
@@ -508,14 +630,21 @@
      * @param d the source data array
      */
     public void copyFromUnchecked(float[] d) {
-        mRS.validate();
-        if (mCurrentDimZ > 0) {
-            copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
-        } else if (mCurrentDimY > 0) {
-            copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
-        } else {
-            copy1DRangeFromUnchecked(0, mCurrentCount, d);
-        }
+        copyFromUnchecked(d, Element.DataType.FLOAT_32, d.length);
+    }
+
+
+    /**
+     * Copy into this Allocation from an array.  This variant is type checked
+     * and will generate exceptions if the Allocation's {@link
+     * android.renderscript.Element} does not match the array's
+     * primitive type.
+     *
+     * @param array The source data array
+     */
+    public void copyFrom(Object array) {
+        copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, true),
+                          java.lang.reflect.Array.getLength(array));
     }
 
     /**
@@ -526,14 +655,8 @@
      * @param d the source data array
      */
     public void copyFrom(int[] d) {
-        mRS.validate();
-        if (mCurrentDimZ > 0) {
-            copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
-        } else if (mCurrentDimY > 0) {
-            copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
-        } else {
-            copy1DRangeFrom(0, mCurrentCount, d);
-        }
+        validateIsInt32();
+        copyFromUnchecked(d, Element.DataType.SIGNED_32, d.length);
     }
 
     /**
@@ -544,14 +667,8 @@
      * @param d the source data array
      */
     public void copyFrom(short[] d) {
-        mRS.validate();
-        if (mCurrentDimZ > 0) {
-            copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
-        } else if (mCurrentDimY > 0) {
-            copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
-        } else {
-            copy1DRangeFrom(0, mCurrentCount, d);
-        }
+        validateIsInt16();
+        copyFromUnchecked(d, Element.DataType.SIGNED_16, d.length);
     }
 
     /**
@@ -562,14 +679,8 @@
      * @param d the source data array
      */
     public void copyFrom(byte[] d) {
-        mRS.validate();
-        if (mCurrentDimZ > 0) {
-            copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
-        } else if (mCurrentDimY > 0) {
-            copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
-        } else {
-            copy1DRangeFrom(0, mCurrentCount, d);
-        }
+        validateIsInt8();
+        copyFromUnchecked(d, Element.DataType.SIGNED_8, d.length);
     }
 
     /**
@@ -580,14 +691,8 @@
      * @param d the source data array
      */
     public void copyFrom(float[] d) {
-        mRS.validate();
-        if (mCurrentDimZ > 0) {
-            copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
-        } else if (mCurrentDimY > 0) {
-            copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
-        } else {
-            copy1DRangeFrom(0, mCurrentCount, d);
-        }
+        validateIsFloat32();
+        copyFromUnchecked(d, Element.DataType.FLOAT_32, d.length);
     }
 
     /**
@@ -684,7 +789,49 @@
                                      component_number, data, data_length);
     }
 
-    private void data1DChecks(int off, int count, int len, int dataSize) {
+    /**
+     * @hide
+     * This is only intended to be used by auto-generated code reflected from
+     * the RenderScript script files.
+     *
+     * @param xoff
+     * @param yoff
+     * @param zoff
+     * @param component_number
+     * @param fp
+     */
+    /*
+    public void setFromFieldPacker(int xoff, int yoff, int zoff, int component_number, FieldPacker fp) {
+        mRS.validate();
+        if (component_number >= mType.mElement.mElements.length) {
+            throw new RSIllegalArgumentException("Component_number " + component_number + " out of range.");
+        }
+        if(xoff < 0) {
+            throw new RSIllegalArgumentException("Offset x must be >= 0.");
+        }
+        if(yoff < 0) {
+            throw new RSIllegalArgumentException("Offset y must be >= 0.");
+        }
+        if(zoff < 0) {
+            throw new RSIllegalArgumentException("Offset z must be >= 0.");
+        }
+
+        final byte[] data = fp.getData();
+        int data_length = fp.getPos();
+        int eSize = mType.mElement.mElements[component_number].getBytesSize();
+        eSize *= mType.mElement.mArraySizes[component_number];
+
+        if (data_length != eSize) {
+            throw new RSIllegalArgumentException("Field packer sizelength " + data_length +
+                                               " does not match component size " + eSize + ".");
+        }
+
+        mRS.nAllocationElementData(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+                                   component_number, data, data_length);
+    }
+    */
+
+    private void data1DChecks(int off, int count, int len, int dataSize, boolean usePadding) {
         mRS.validate();
         if(off < 0) {
             throw new RSIllegalArgumentException("Offset must be >= 0.");
@@ -696,8 +843,14 @@
             throw new RSIllegalArgumentException("Overflow, Available count " + mCurrentCount +
                                                ", got " + count + " at offset " + off + ".");
         }
-        if(len < dataSize) {
-            throw new RSIllegalArgumentException("Array too small for allocation type.");
+        if(usePadding) {
+            if(len < dataSize / 4 * 3) {
+                throw new RSIllegalArgumentException("Array too small for allocation type.");
+            }
+        } else {
+            if(len < dataSize) {
+                throw new RSIllegalArgumentException("Array too small for allocation type.");
+            }
         }
     }
 
@@ -715,6 +868,33 @@
         mRS.nAllocationGenerateMipmaps(getID(mRS));
     }
 
+    private void copy1DRangeFromUnchecked(int off, int count, Object array,
+                                          Element.DataType dt, int arrayLen) {
+        final int dataSize = mType.mElement.getBytesSize() * count;
+        // AutoPadding for Vec3 Element
+        boolean usePadding = false;
+        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+            usePadding = true;
+        }
+        data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
+        mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
+                              mType.mElement.mType.mSize, usePadding);
+    }
+
+    /**
+     * Copy an array into part of this Allocation.  This method does not
+     * guarantee that the Allocation is compatible with the input buffer.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param array The source data array
+     */
+    public void copy1DRangeFromUnchecked(int off, int count, Object array) {
+        copy1DRangeFromUnchecked(off, count, array,
+                                 validateObjectIsPrimitiveArray(array, false),
+                                 java.lang.reflect.Array.getLength(array));
+    }
+
     /**
      * Copy an array into part of this Allocation.  This method does not
      * guarantee that the Allocation is compatible with the input buffer.
@@ -724,10 +904,9 @@
      * @param d the source data array
      */
     public void copy1DRangeFromUnchecked(int off, int count, int[] d) {
-        int dataSize = mType.mElement.getBytesSize() * count;
-        data1DChecks(off, count, d.length * 4, dataSize);
-        mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+        copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.SIGNED_32, d.length);
     }
+
     /**
      * Copy an array into part of this Allocation.  This method does not
      * guarantee that the Allocation is compatible with the input buffer.
@@ -737,10 +916,9 @@
      * @param d the source data array
      */
     public void copy1DRangeFromUnchecked(int off, int count, short[] d) {
-        int dataSize = mType.mElement.getBytesSize() * count;
-        data1DChecks(off, count, d.length * 2, dataSize);
-        mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+        copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.SIGNED_16, d.length);
     }
+
     /**
      * Copy an array into part of this Allocation.  This method does not
      * guarantee that the Allocation is compatible with the input buffer.
@@ -750,10 +928,9 @@
      * @param d the source data array
      */
     public void copy1DRangeFromUnchecked(int off, int count, byte[] d) {
-        int dataSize = mType.mElement.getBytesSize() * count;
-        data1DChecks(off, count, d.length, dataSize);
-        mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+        copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.SIGNED_8, d.length);
     }
+
     /**
      * Copy an array into part of this Allocation.  This method does not
      * guarantee that the Allocation is compatible with the input buffer.
@@ -763,9 +940,23 @@
      * @param d the source data array
      */
     public void copy1DRangeFromUnchecked(int off, int count, float[] d) {
-        int dataSize = mType.mElement.getBytesSize() * count;
-        data1DChecks(off, count, d.length * 4, dataSize);
-        mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
+        copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.FLOAT_32, d.length);
+    }
+
+
+    /**
+     * Copy an array into part of this Allocation.  This variant is type checked
+     * and will generate exceptions if the Allocation type does not
+     * match the component type of the array passed in.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param array The source data array.
+     */
+    public void copy1DRangeFrom(int off, int count, Object array) {
+        copy1DRangeFromUnchecked(off, count, array,
+                                 validateObjectIsPrimitiveArray(array, true),
+                                 java.lang.reflect.Array.getLength(array));
     }
 
     /**
@@ -779,7 +970,7 @@
      */
     public void copy1DRangeFrom(int off, int count, int[] d) {
         validateIsInt32();
-        copy1DRangeFromUnchecked(off, count, d);
+        copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_32, d.length);
     }
 
     /**
@@ -793,7 +984,7 @@
      */
     public void copy1DRangeFrom(int off, int count, short[] d) {
         validateIsInt16();
-        copy1DRangeFromUnchecked(off, count, d);
+        copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_16, d.length);
     }
 
     /**
@@ -807,7 +998,7 @@
      */
     public void copy1DRangeFrom(int off, int count, byte[] d) {
         validateIsInt8();
-        copy1DRangeFromUnchecked(off, count, d);
+        copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_8, d.length);
     }
 
     /**
@@ -821,7 +1012,7 @@
      */
     public void copy1DRangeFrom(int off, int count, float[] d) {
         validateIsFloat32();
-        copy1DRangeFromUnchecked(off, count, d);
+        copy1DRangeFromUnchecked(off, count, d, Element.DataType.FLOAT_32, d.length);
     }
 
      /**
@@ -857,35 +1048,46 @@
         }
     }
 
-    void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, byte[] data) {
+    void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, Object array,
+                                  Element.DataType dt, int arrayLen) {
         mRS.validate();
         validate2DRange(xoff, yoff, w, h);
-        mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
-                              w, h, data, data.length);
+        final int dataSize = mType.mElement.getBytesSize() * w * h;
+        // AutoPadding for Vec3 Element
+        boolean usePadding = false;
+        int sizeBytes = arrayLen * dt.mSize;
+        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+            if (dataSize / 4 * 3 > sizeBytes) {
+                throw new RSIllegalArgumentException("Array too small for allocation type.");
+            }
+            usePadding = true;
+            sizeBytes = dataSize;
+        } else {
+            if (dataSize > sizeBytes) {
+                throw new RSIllegalArgumentException("Array too small for allocation type.");
+            }
+        }
+        mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
+                              array, sizeBytes, dt,
+                              mType.mElement.mType.mSize, usePadding);
     }
 
-    void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, short[] data) {
-        mRS.validate();
-        validate2DRange(xoff, yoff, w, h);
-        mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
-                              w, h, data, data.length * 2);
+    /**
+     * Copy from an array into a rectangular region in this Allocation.  The
+     * array is assumed to be tightly packed.
+     *
+     * @param xoff X offset of the region to update in this Allocation
+     * @param yoff Y offset of the region to update in this Allocation
+     * @param w Width of the region to update
+     * @param h Height of the region to update
+     * @param array Data to be placed into the Allocation
+     */
+    public void copy2DRangeFrom(int xoff, int yoff, int w, int h, Object array) {
+        copy2DRangeFromUnchecked(xoff, yoff, w, h, array,
+                                 validateObjectIsPrimitiveArray(array, true),
+                                 java.lang.reflect.Array.getLength(array));
     }
 
-    void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, int[] data) {
-        mRS.validate();
-        validate2DRange(xoff, yoff, w, h);
-        mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
-                              w, h, data, data.length * 4);
-    }
-
-    void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, float[] data) {
-        mRS.validate();
-        validate2DRange(xoff, yoff, w, h);
-        mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
-                              w, h, data, data.length * 4);
-    }
-
-
     /**
      * Copy from an array into a rectangular region in this Allocation.  The
      * array is assumed to be tightly packed.
@@ -898,7 +1100,8 @@
      */
     public void copy2DRangeFrom(int xoff, int yoff, int w, int h, byte[] data) {
         validateIsInt8();
-        copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+        copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
+                                 Element.DataType.SIGNED_8, data.length);
     }
 
     /**
@@ -913,7 +1116,8 @@
      */
     public void copy2DRangeFrom(int xoff, int yoff, int w, int h, short[] data) {
         validateIsInt16();
-        copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+        copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
+                                 Element.DataType.SIGNED_16, data.length);
     }
 
     /**
@@ -928,7 +1132,8 @@
      */
     public void copy2DRangeFrom(int xoff, int yoff, int w, int h, int[] data) {
         validateIsInt32();
-        copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+        copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
+                                 Element.DataType.SIGNED_32, data.length);
     }
 
     /**
@@ -943,7 +1148,8 @@
      */
     public void copy2DRangeFrom(int xoff, int yoff, int w, int h, float[] data) {
         validateIsFloat32();
-        copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
+        copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
+                                 Element.DataType.FLOAT_32, data.length);
     }
 
     /**
@@ -1012,49 +1218,32 @@
      * @hide
      *
      */
-    void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, byte[] data) {
+    private void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
+                                          Object array, Element.DataType dt, int arrayLen) {
         mRS.validate();
         validate3DRange(xoff, yoff, zoff, w, h, d);
-        mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
-                              w, h, d, data, data.length);
+        final int dataSize = mType.mElement.getBytesSize() * w * h * d;
+        // AutoPadding for Vec3 Element
+        boolean usePadding = false;
+        int sizeBytes = arrayLen * dt.mSize;
+        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+            if (dataSize / 4 * 3 > sizeBytes) {
+                throw new RSIllegalArgumentException("Array too small for allocation type.");
+            }
+            usePadding = true;
+            sizeBytes = dataSize;
+        } else {
+            if (dataSize > sizeBytes) {
+                throw new RSIllegalArgumentException("Array too small for allocation type.");
+            }
+        }
+        mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
+                              array, sizeBytes, dt,
+                              mType.mElement.mType.mSize, usePadding);
     }
 
     /**
      * @hide
-     *
-     */
-    void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, short[] data) {
-        mRS.validate();
-        validate3DRange(xoff, yoff, zoff, w, h, d);
-        mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
-                              w, h, d, data, data.length * 2);
-    }
-
-    /**
-     * @hide
-     *
-     */
-    void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, int[] data) {
-        mRS.validate();
-        validate3DRange(xoff, yoff, zoff, w, h, d);
-        mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
-                              w, h, d, data, data.length * 4);
-    }
-
-    /**
-     * @hide
-     *
-     */
-    void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, float[] data) {
-        mRS.validate();
-        validate3DRange(xoff, yoff, zoff, w, h, d);
-        mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
-                              w, h, d, data, data.length * 4);
-    }
-
-
-    /**
-     * @hide
      * Copy a rectangular region from the array into the allocation.
      * The array is assumed to be tightly packed.
      *
@@ -1066,36 +1255,10 @@
      * @param d Depth of the region to update
      * @param data to be placed into the allocation
      */
-    public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, byte[] data) {
-        validateIsInt8();
-        copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
-    }
-
-    /**
-     * @hide
-     *
-     */
-    public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, short[] data) {
-        validateIsInt16();
-        copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
-    }
-
-    /**
-     * @hide
-     *
-     */
-    public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, int[] data) {
-        validateIsInt32();
-        copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
-    }
-
-    /**
-     * @hide
-     *
-     */
-    public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, float[] data) {
-        validateIsFloat32();
-        copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+    public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, Object array) {
+        copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, array,
+                                 validateObjectIsPrimitiveArray(array, true),
+                                 java.lang.reflect.Array.getLength(array));
     }
 
     /**
@@ -1137,6 +1300,28 @@
         mRS.nAllocationCopyToBitmap(getID(mRS), b);
     }
 
+    private void copyTo(Object array, Element.DataType dt, int arrayLen) {
+        mRS.validate();
+        boolean usePadding = false;
+        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+            usePadding = true;
+        }
+        mRS.nAllocationRead(getID(mRS), array, dt, mType.mElement.mType.mSize, usePadding);
+    }
+
+    /**
+     * Copy from the Allocation into an array.  The array must be at
+     * least as large as the Allocation.  The
+     * {@link android.renderscript.Element} must match the component
+     * type of the array passed in.
+     *
+     * @param array The array to be set from the Allocation.
+     */
+    public void copyTo(Object array) {
+        copyTo(array, validateObjectIsPrimitiveArray(array, true),
+               java.lang.reflect.Array.getLength(array));
+    }
+
     /**
      * Copy from the Allocation into a byte array.  The array must be at least
      * as large as the Allocation.  The allocation must be of an 8 bit integer
@@ -1146,8 +1331,7 @@
      */
     public void copyTo(byte[] d) {
         validateIsInt8();
-        mRS.validate();
-        mRS.nAllocationRead(getID(mRS), d);
+        copyTo(d, Element.DataType.SIGNED_8, d.length);
     }
 
     /**
@@ -1159,8 +1343,7 @@
      */
     public void copyTo(short[] d) {
         validateIsInt16();
-        mRS.validate();
-        mRS.nAllocationRead(getID(mRS), d);
+        copyTo(d, Element.DataType.SIGNED_16, d.length);
     }
 
     /**
@@ -1172,8 +1355,7 @@
      */
     public void copyTo(int[] d) {
         validateIsInt32();
-        mRS.validate();
-        mRS.nAllocationRead(getID(mRS), d);
+        copyTo(d, Element.DataType.SIGNED_32, d.length);
     }
 
     /**
@@ -1185,10 +1367,362 @@
      */
     public void copyTo(float[] d) {
         validateIsFloat32();
-        mRS.validate();
-        mRS.nAllocationRead(getID(mRS), d);
+        copyTo(d, Element.DataType.FLOAT_32, d.length);
     }
 
+    /**
+     * @hide
+     * This is only intended to be used by auto-generated code reflected from
+     * the RenderScript script files and should not be used by developers.
+     *
+     * @param xoff
+     * @param yoff
+     * @param zoff
+     * @param component_number
+     * @param array
+     */
+    /*
+    public void copyToFieldPacker(int xoff, int yoff, int zoff, int component_number, FieldPacker fp) {
+        mRS.validate();
+        if (component_number >= mType.mElement.mElements.length) {
+            throw new RSIllegalArgumentException("Component_number " + component_number + " out of range.");
+        }
+        if(xoff < 0) {
+            throw new RSIllegalArgumentException("Offset x must be >= 0.");
+        }
+        if(yoff < 0) {
+            throw new RSIllegalArgumentException("Offset y must be >= 0.");
+        }
+        if(zoff < 0) {
+            throw new RSIllegalArgumentException("Offset z must be >= 0.");
+        }
+
+        final byte[] data = fp.getData();
+        int data_length = fp.getPos();
+        int eSize = mType.mElement.mElements[component_number].getBytesSize();
+        eSize *= mType.mElement.mArraySizes[component_number];
+
+        if (data_length != eSize) {
+            throw new RSIllegalArgumentException("Field packer sizelength " + data_length +
+                                               " does not match component size " + eSize + ".");
+        }
+
+        mRS.nAllocationElementRead(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+                                   component_number, data, data_length);
+    }
+    */
+
+    private void copy1DRangeToUnchecked(int off, int count, Object array,
+                                        Element.DataType dt, int arrayLen) {
+        final int dataSize = mType.mElement.getBytesSize() * count;
+        // AutoPadding for Vec3 Element
+        boolean usePadding = false;
+        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+            usePadding = true;
+        }
+        data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
+        mRS.nAllocationRead1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
+                              mType.mElement.mType.mSize, usePadding);
+    }
+
+    /**
+     * @hide
+     * Copy part of this Allocation into an array.  This method does not
+     * guarantee that the Allocation is compatible with the input buffer.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param array The dest data array
+     */
+    public void copy1DRangeToUnchecked(int off, int count, Object array) {
+        copy1DRangeToUnchecked(off, count, array,
+                               validateObjectIsPrimitiveArray(array, false),
+                               java.lang.reflect.Array.getLength(array));
+    }
+
+    /**
+     * @hide
+     * Copy part of this Allocation into an array.  This method does not
+     * guarantee that the Allocation is compatible with the input buffer.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param d the source data array
+     */
+    public void copy1DRangeToUnchecked(int off, int count, int[] d) {
+        copy1DRangeToUnchecked(off, count, (Object)d, Element.DataType.SIGNED_32, d.length);
+    }
+
+    /**
+     * @hide
+     * Copy part of this Allocation into an array.  This method does not
+     * guarantee that the Allocation is compatible with the input buffer.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param d the source data array
+     */
+    public void copy1DRangeToUnchecked(int off, int count, short[] d) {
+        copy1DRangeToUnchecked(off, count, (Object)d, Element.DataType.SIGNED_16, d.length);
+    }
+
+    /**
+     * @hide
+     * Copy part of this Allocation into an array.  This method does not
+     * guarantee that the Allocation is compatible with the input buffer.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param d the source data array
+     */
+    public void copy1DRangeToUnchecked(int off, int count, byte[] d) {
+        copy1DRangeToUnchecked(off, count, (Object)d, Element.DataType.SIGNED_8, d.length);
+    }
+
+    /**
+     * @hide
+     * Copy part of this Allocation into an array.  This method does not
+     * guarantee that the Allocation is compatible with the input buffer.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param d the source data array
+     */
+    public void copy1DRangeToUnchecked(int off, int count, float[] d) {
+        copy1DRangeToUnchecked(off, count, (Object)d, Element.DataType.FLOAT_32, d.length);
+    }
+
+
+    /**
+     * @hide
+     * Copy part of this Allocation into an array.  This method does not
+     * and will generate exceptions if the Allocation type does not
+     * match the component type of the array passed in.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param array The source data array.
+     */
+    public void copy1DRangeTo(int off, int count, Object array) {
+        copy1DRangeToUnchecked(off, count, array,
+                               validateObjectIsPrimitiveArray(array, true),
+                               java.lang.reflect.Array.getLength(array));
+    }
+
+    /**
+     * @hide
+     * Copy part of this Allocation into an array.  This method does not
+     * and will generate exceptions if the Allocation type is not a 32 bit
+     * integer type.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param d the source data array
+     */
+    public void copy1DRangeTo(int off, int count, int[] d) {
+        validateIsInt32();
+        copy1DRangeToUnchecked(off, count, d, Element.DataType.SIGNED_32, d.length);
+    }
+
+    /**
+     * @hide
+     * Copy part of this Allocation into an array.  This method does not
+     * and will generate exceptions if the Allocation type is not a 16 bit
+     * integer type.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param d the source data array
+     */
+    public void copy1DRangeTo(int off, int count, short[] d) {
+        validateIsInt16();
+        copy1DRangeToUnchecked(off, count, d, Element.DataType.SIGNED_16, d.length);
+    }
+
+    /**
+     * @hide
+     * Copy part of this Allocation into an array.  This method does not
+     * and will generate exceptions if the Allocation type is not an 8 bit
+     * integer type.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param d the source data array
+     */
+    public void copy1DRangeTo(int off, int count, byte[] d) {
+        validateIsInt8();
+        copy1DRangeToUnchecked(off, count, d, Element.DataType.SIGNED_8, d.length);
+    }
+
+    /**
+     * @hide
+     * Copy part of this Allocation into an array.  This method does not
+     * and will generate exceptions if the Allocation type is not a 32 bit float
+     * type.
+     *
+     * @param off The offset of the first element to be copied.
+     * @param count The number of elements to be copied.
+     * @param d the source data array.
+     */
+    public void copy1DRangeTo(int off, int count, float[] d) {
+        validateIsFloat32();
+        copy1DRangeToUnchecked(off, count, d, Element.DataType.FLOAT_32, d.length);
+    }
+
+
+    void copy2DRangeToUnchecked(int xoff, int yoff, int w, int h, Object array,
+                                Element.DataType dt, int arrayLen) {
+        mRS.validate();
+        validate2DRange(xoff, yoff, w, h);
+        final int dataSize = mType.mElement.getBytesSize() * w * h;
+        // AutoPadding for Vec3 Element
+        boolean usePadding = false;
+        int sizeBytes = arrayLen * dt.mSize;
+        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+            if (dataSize / 4 * 3 > sizeBytes) {
+                throw new RSIllegalArgumentException("Array too small for allocation type.");
+            }
+            usePadding = true;
+            sizeBytes = dataSize;
+        } else {
+            if (dataSize > sizeBytes) {
+                throw new RSIllegalArgumentException("Array too small for allocation type.");
+            }
+        }
+        mRS.nAllocationRead2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
+                              array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
+    }
+
+    /**
+     * @hide
+     * Copy from a rectangular region in this Allocation into an array.
+     *
+     * @param xoff X offset of the region to copy in this Allocation
+     * @param yoff Y offset of the region to copy in this Allocation
+     * @param w Width of the region to copy
+     * @param h Height of the region to copy
+     * @param array Dest Array to be copied into
+     */
+    public void copy2DRangeTo(int xoff, int yoff, int w, int h, Object array) {
+        copy2DRangeToUnchecked(xoff, yoff, w, h, array,
+                               validateObjectIsPrimitiveArray(array, true),
+                               java.lang.reflect.Array.getLength(array));
+    }
+
+    /**
+     * @hide
+     * Copy from a rectangular region in this Allocation into an array.
+     *
+     * @param xoff X offset of the region to copy in this Allocation
+     * @param yoff Y offset of the region to copy in this Allocation
+     * @param w Width of the region to copy
+     * @param h Height of the region to copy
+     * @param array Dest Array to be copied into
+     */
+    public void copy2DRangeTo(int xoff, int yoff, int w, int h, byte[] data) {
+        validateIsInt8();
+        copy2DRangeToUnchecked(xoff, yoff, w, h, data,
+                               Element.DataType.SIGNED_8, data.length);
+    }
+
+    /**
+     * @hide
+     * Copy from a rectangular region in this Allocation into an array.
+     *
+     * @param xoff X offset of the region to copy in this Allocation
+     * @param yoff Y offset of the region to copy in this Allocation
+     * @param w Width of the region to copy
+     * @param h Height of the region to copy
+     * @param array Dest Array to be copied into
+     */
+    public void copy2DRangeTo(int xoff, int yoff, int w, int h, short[] data) {
+        validateIsInt16();
+        copy2DRangeToUnchecked(xoff, yoff, w, h, data,
+                               Element.DataType.SIGNED_16, data.length);
+    }
+
+    /**
+     * @hide
+     * Copy from a rectangular region in this Allocation into an array.
+     *
+     * @param xoff X offset of the region to copy in this Allocation
+     * @param yoff Y offset of the region to copy in this Allocation
+     * @param w Width of the region to copy
+     * @param h Height of the region to copy
+     * @param array Dest Array to be copied into
+     */
+    public void copy2DRangeTo(int xoff, int yoff, int w, int h, int[] data) {
+        validateIsInt32();
+        copy2DRangeToUnchecked(xoff, yoff, w, h, data,
+                               Element.DataType.SIGNED_32, data.length);
+    }
+
+    /**
+     * @hide
+     * Copy from a rectangular region in this Allocation into an array.
+     *
+     * @param xoff X offset of the region to copy in this Allocation
+     * @param yoff Y offset of the region to copy in this Allocation
+     * @param w Width of the region to copy
+     * @param h Height of the region to copy
+     * @param array Dest Array to be copied into
+     */
+    public void copy2DRangeTo(int xoff, int yoff, int w, int h, float[] data) {
+        validateIsFloat32();
+        copy2DRangeToUnchecked(xoff, yoff, w, h, data,
+                               Element.DataType.FLOAT_32, data.length);
+    }
+
+
+    /**
+     * @hide
+     *
+     */
+    /*
+    private void copy3DRangeToUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
+                                        Object array, Element.DataType dt, int arrayLen) {
+        mRS.validate();
+        validate3DRange(xoff, yoff, zoff, w, h, d);
+        final int dataSize = mType.mElement.getBytesSize() * w * h * d;
+        // AutoPadding for Vec3 Element
+        boolean usePadding = false;
+        int sizeBytes = arrayLen * dt.mSize;
+        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+            if (dataSize / 4 * 3 > sizeBytes) {
+                throw new RSIllegalArgumentException("Array too small for allocation type.");
+            }
+            usePadding = true;
+            sizeBytes = dataSize;
+        } else {
+            if (dataSize > sizeBytes) {
+                throw new RSIllegalArgumentException("Array too small for allocation type.");
+            }
+        }
+        mRS.nAllocationRead3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
+                              array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
+    }
+    */
+
+    /**
+     * @hide
+     * Copy from a rectangular region in this Allocation into an array.
+     *
+     * @param xoff X offset of the region to copy in this Allocation
+     * @param yoff Y offset of the region to copy in this Allocation
+     * @param zoff Z offset of the region to copy in this Allocation
+     * @param w Width of the region to copy
+     * @param h Height of the region to copy
+     * @param d Depth of the region to copy
+     * @param array Dest Array to be copied into
+     */
+    /*
+    public void copy3DRangeTo(int xoff, int yoff, int zoff, int w, int h, int d, Object array) {
+        copy3DRangeToUnchecked(xoff, yoff, zoff, w, h, d, array,
+                                 validateObjectIsPrimitiveArray(array, true),
+                                 java.lang.reflect.Array.getLength(array));
+    }
+    */
+
     // creation
 
     static BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
@@ -1207,15 +1741,16 @@
      *              utilized
      */
     static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips, int usage) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker)rs;
-            return AllocationThunker.createTyped(rst, type, mips, usage);
-        }
         rs.validate();
         if (type.getID(rs) == 0) {
             throw new RSInvalidStateException("Bad Type");
         }
-        int id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
+
+        if(!rs.usingIO() && (usage & (USAGE_IO_INPUT | USAGE_IO_INPUT)) != 0) {
+            throw new RSRuntimeException("USAGE_IO not supported, Allocation creation failed.");
+        }
+
+        long id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
         if (id == 0) {
             throw new RSRuntimeException("Allocation creation failed.");
         }
@@ -1263,16 +1798,12 @@
      */
     static public Allocation createSized(RenderScript rs, Element e,
                                          int count, int usage) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker)rs;
-            return AllocationThunker.createSized(rs, e, count, usage);
-        }
         rs.validate();
         Type.Builder b = new Type.Builder(rs, e);
         b.setX(count);
         Type t = b.create();
 
-        int id = rs.nAllocationCreateTyped(t.getID(rs), MipmapControl.MIPMAP_NONE.mID, usage, 0);
+        long id = rs.nAllocationCreateTyped(t.getID(rs), MipmapControl.MIPMAP_NONE.mID, usage, 0);
         if (id == 0) {
             throw new RSRuntimeException("Allocation creation failed.");
         }
@@ -1335,10 +1866,6 @@
     static public Allocation createFromBitmap(RenderScript rs, Bitmap b,
                                               MipmapControl mips,
                                               int usage) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker)rs;
-            return AllocationThunker.createFromBitmap(rst, b, mips, usage);
-        }
         rs.validate();
 
         // WAR undocumented color formats
@@ -1358,7 +1885,7 @@
         if (mips == MipmapControl.MIPMAP_NONE &&
             t.getElement().isCompatible(Element.RGBA_8888(rs)) &&
             usage == (USAGE_SHARED | USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE)) {
-            int id = rs.nAllocationCreateBitmapBackedAllocation(t.getID(rs), mips.mID, b, usage);
+            long id = rs.nAllocationCreateBitmapBackedAllocation(t.getID(rs), mips.mID, b, usage);
             if (id == 0) {
                 throw new RSRuntimeException("Load failed.");
             }
@@ -1370,7 +1897,7 @@
         }
 
 
-        int id = rs.nAllocationCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
+        long id = rs.nAllocationCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
         if (id == 0) {
             throw new RSRuntimeException("Load failed.");
         }
@@ -1378,6 +1905,21 @@
     }
 
     /**
+     * Associate a {@link android.view.Surface} with this Allocation. This
+     * operation is only valid for Allocations with {@link #USAGE_IO_OUTPUT}.
+     *
+     * @param sur Surface to associate with allocation
+     */
+    public void setSurface(Surface sur) {
+        mRS.validate();
+        if ((mUsage & USAGE_IO_OUTPUT) == 0) {
+            throw new RSInvalidStateException("Allocation is not USAGE_IO_OUTPUT.");
+        }
+
+        mRS.nAllocationSetSurface(getID(mRS), sur);
+    }
+
+    /**
      * Creates an Allocation from a {@link android.graphics.Bitmap}.
      *
      * <p>This Allocation will be created with {@link #USAGE_SHARED}, and
@@ -1436,7 +1978,7 @@
         tb.setMipmaps(mips == MipmapControl.MIPMAP_FULL);
         Type t = tb.create();
 
-        int id = rs.nAllocationCubeCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
+        long id = rs.nAllocationCubeCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
         if(id == 0) {
             throw new RSRuntimeException("Load failed for bitmap " + b + " element " + e);
         }
@@ -1638,5 +2180,40 @@
             throw new RSRuntimeException("Could not convert string to utf-8.");
         }
     }
+
+    /**
+     * Frees any native resources associated with this object.  The
+     * primary use is to force immediate cleanup of resources when it is
+     * believed the GC will not respond quickly enough.
+     * For USAGE_IO_OUTPUT, destroy() implies setSurface(null).
+     */
+    @Override
+    public void destroy() {
+        if (mIncCompatAllocation != 0) {
+            boolean shouldDestroy = false;
+            synchronized(this) {
+                if (!mIncAllocDestroyed) {
+                    shouldDestroy = true;
+                    mIncAllocDestroyed = true;
+                }
+            }
+
+            if (shouldDestroy) {
+                // must include nObjDestroy in the critical section
+                ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock();
+                rlock.lock();
+                if(mRS.isAlive()) {
+                    mRS.nIncObjDestroy(mIncCompatAllocation);
+                }
+                rlock.unlock();
+                mIncCompatAllocation = 0;
+            }
+        }
+        if ((mUsage & (USAGE_IO_INPUT | USAGE_IO_OUTPUT)) != 0) {
+            setSurface(null);
+        }
+        super.destroy();
+    }
+
 }
 
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/AllocationThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/AllocationThunker.java
deleted file mode 100644
index 2ae7edf..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/AllocationThunker.java
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import java.io.IOException;
-import java.io.InputStream;
-import android.content.res.Resources;
-import android.content.res.AssetManager;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.view.Surface;
-import android.util.Log;
-import android.util.TypedValue;
-
-class AllocationThunker extends Allocation {
-    android.renderscript.Allocation mN;
-    //Allocation mAdaptedAllocation;
-
-    android.renderscript.Allocation getNObj() {
-        return mN;
-    }
-
-    static android.renderscript.Allocation.MipmapControl
-        convertMipmapControl(MipmapControl mc) {
-
-        switch(mc) {
-        case MIPMAP_NONE:
-            return android.renderscript.Allocation.MipmapControl.MIPMAP_NONE;
-        case MIPMAP_FULL:
-            return android.renderscript.Allocation.MipmapControl.MIPMAP_FULL;
-        case MIPMAP_ON_SYNC_TO_TEXTURE:
-            return android.renderscript.Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE;
-        }
-        return null;
-    }
-
-    public Type getType() {
-        return TypeThunker.find(mN.getType());
-    }
-
-    public Element getElement() {
-        return getType().getElement();
-    }
-
-    public int getUsage() {
-        try {
-            return mN.getUsage();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public int getBytesSize() {
-        try {
-            return mN.getBytesSize();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    AllocationThunker(RenderScript rs, Type t, int usage, android.renderscript.Allocation na) {
-        super(0, rs, t, usage);
-
-        mType = t;
-        mUsage = usage;
-        mN = na;
-    }
-
-    public void syncAll(int srcLocation) {
-        try {
-            mN.syncAll(srcLocation);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void ioSend() {
-        try {
-            mN.ioSend();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void ioReceive() {
-        try {
-            mN.ioReceive();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void copyFrom(BaseObj[] d) {
-        if (d == null) {
-            return;
-        }
-        android.renderscript.BaseObj[] dN = new android.renderscript.BaseObj[d.length];
-        for (int i = 0; i < d.length; i++) {
-            dN[i] = d[i].getNObj();
-        }
-        try {
-            mN.copyFrom(dN);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void copyFromUnchecked(int[] d) {
-        try {
-            mN.copyFromUnchecked(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyFromUnchecked(short[] d) {
-        try {
-            mN.copyFromUnchecked(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyFromUnchecked(byte[] d) {
-        try {
-            mN.copyFromUnchecked(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyFromUnchecked(float[] d) {
-        try {
-            mN.copyFromUnchecked(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void copyFrom(int[] d) {
-        try {
-            mN.copyFrom(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyFrom(short[] d) {
-        try {
-            mN.copyFrom(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyFrom(byte[] d) {
-        try {
-            mN.copyFrom(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyFrom(float[] d) {
-        try {
-            mN.copyFrom(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyFrom(Bitmap b) {
-        try {
-            mN.copyFrom(b);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyFrom(Allocation a) {
-        AllocationThunker at = (AllocationThunker)a;
-        try {
-            mN.copyFrom(at.mN);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-
-    public void setFromFieldPacker(int xoff, FieldPacker fp) {
-        try {
-            // Must construct actual FieldPacker from scratch, since we don't
-            // know how many bytes were actually used.
-            byte[] data = fp.getData();
-            int fp_length = fp.getPos();
-            android.renderscript.FieldPacker nfp =
-                new android.renderscript.FieldPacker(fp_length);
-            for (int i = 0; i < fp_length; i++) {
-                nfp.addI8(data[i]);
-            }
-            mN.setFromFieldPacker(xoff, nfp);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void setFromFieldPacker(int xoff, int component_number, FieldPacker fp) {
-        try {
-            // Must construct actual FieldPacker from scratch, since we don't
-            // know how many bytes were actually used.
-            byte[] data = fp.getData();
-            int fp_length = fp.getPos();
-            android.renderscript.FieldPacker nfp =
-                new android.renderscript.FieldPacker(fp_length);
-            for (int i = 0; i < fp_length; i++) {
-                nfp.addI8(data[i]);
-            }
-            mN.setFromFieldPacker(xoff, component_number, nfp);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void generateMipmaps() {
-        try {
-            mN.generateMipmaps();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void copy1DRangeFromUnchecked(int off, int count, int[] d) {
-        try {
-            mN.copy1DRangeFromUnchecked(off, count, d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copy1DRangeFromUnchecked(int off, int count, short[] d) {
-        try {
-            mN.copy1DRangeFromUnchecked(off, count, d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copy1DRangeFromUnchecked(int off, int count, byte[] d) {
-        try {
-            mN.copy1DRangeFromUnchecked(off, count, d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copy1DRangeFromUnchecked(int off, int count, float[] d) {
-        try {
-            mN.copy1DRangeFromUnchecked(off, count, d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void copy1DRangeFrom(int off, int count, int[] d) {
-        try {
-            mN.copy1DRangeFrom(off, count, d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copy1DRangeFrom(int off, int count, short[] d) {
-        try {
-            mN.copy1DRangeFrom(off, count, d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copy1DRangeFrom(int off, int count, byte[] d) {
-        try {
-            mN.copy1DRangeFrom(off, count, d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copy1DRangeFrom(int off, int count, float[] d) {
-        try {
-            mN.copy1DRangeFrom(off, count, d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void copy1DRangeFrom(int off, int count, Allocation data, int dataOff) {
-        try {
-            AllocationThunker at = (AllocationThunker)data;
-            mN.copy1DRangeFrom(off, count, at.mN, dataOff);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h, byte[] data) {
-        try {
-            mN.copy2DRangeFrom(xoff, yoff, w, h, data);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h, short[] data) {
-        try {
-            mN.copy2DRangeFrom(xoff, yoff, w, h, data);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h, int[] data) {
-        try {
-            mN.copy2DRangeFrom(xoff, yoff, w, h, data);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h, float[] data) {
-        try {
-            mN.copy2DRangeFrom(xoff, yoff, w, h, data);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h,
-                                Allocation data, int dataXoff, int dataYoff) {
-        try {
-            AllocationThunker at = (AllocationThunker)data;
-            mN.copy2DRangeFrom(xoff, yoff, w, h, at.mN, dataXoff, dataYoff);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copy2DRangeFrom(int xoff, int yoff, Bitmap data) {
-        try {
-            mN.copy2DRangeFrom(xoff, yoff, data);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-
-    public void copyTo(Bitmap b) {
-        try {
-            mN.copyTo(b);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyTo(byte[] d) {
-        try {
-            mN.copyTo(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyTo(short[] d) {
-        try {
-            mN.copyTo(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyTo(int[] d) {
-        try {
-            mN.copyTo(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    public void copyTo(float[] d) {
-        try {
-            mN.copyTo(d);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    // creation
-
-    static BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
-    static {
-        mBitmapOptions.inScaled = false;
-    }
-
-    static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips, int usage) {
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        TypeThunker tt = (TypeThunker)type;
-
-        try {
-            android.renderscript.Allocation a =
-                android.renderscript.Allocation.createTyped(rst.mN, tt.mN,
-                                                            convertMipmapControl(mips),
-                                                            usage);
-            return new AllocationThunker(rs, type, usage, a);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    static public Allocation createFromBitmap(RenderScript rs, Bitmap b,
-                                              MipmapControl mips,
-                                              int usage) {
-
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        try {
-            android.renderscript.Allocation a =
-                android.renderscript.Allocation.createFromBitmap(rst.mN, b,
-                                                                 convertMipmapControl(mips),
-                                                                 usage);
-            TypeThunker tt = new TypeThunker(rs, a.getType());
-            return new AllocationThunker(rs, tt, usage, a);
-
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    static public Allocation createCubemapFromBitmap(RenderScript rs, Bitmap b,
-                                                     MipmapControl mips,
-                                                     int usage) {
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        try {
-            android.renderscript.Allocation a =
-                    android.renderscript.Allocation.createCubemapFromBitmap(
-                    rst.mN, b, convertMipmapControl(mips), usage);
-            TypeThunker tt = new TypeThunker(rs, a.getType());
-            return new AllocationThunker(rs, tt, usage, a);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    static public Allocation createCubemapFromCubeFaces(RenderScript rs,
-                                                        Bitmap xpos,
-                                                        Bitmap xneg,
-                                                        Bitmap ypos,
-                                                        Bitmap yneg,
-                                                        Bitmap zpos,
-                                                        Bitmap zneg,
-                                                        MipmapControl mips,
-                                                        int usage) {
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        try {
-            android.renderscript.Allocation a =
-                    android.renderscript.Allocation.createCubemapFromCubeFaces(
-                    rst.mN, xpos, xneg, ypos, yneg, zpos, zneg,
-                    convertMipmapControl(mips), usage);
-            TypeThunker tt = new TypeThunker(rs, a.getType());
-            return new AllocationThunker(rs, tt, usage, a);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    static public Allocation createFromBitmapResource(RenderScript rs,
-                                                      Resources res,
-                                                      int id,
-                                                      MipmapControl mips,
-                                                      int usage) {
-
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        try {
-            android.renderscript.Allocation a =
-                    android.renderscript.Allocation.createFromBitmapResource(
-                    rst.mN, res, id, convertMipmapControl(mips), usage);
-            TypeThunker tt = new TypeThunker(rs, a.getType());
-            return new AllocationThunker(rs, tt, usage, a);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    static public Allocation createFromString(RenderScript rs,
-                                              String str,
-                                              int usage) {
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        try {
-            android.renderscript.Allocation a =
-                    android.renderscript.Allocation.createFromString(
-                    rst.mN, str, usage);
-            TypeThunker tt = new TypeThunker(rs, a.getType());
-            return new AllocationThunker(rs, tt, usage, a);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    static public Allocation createSized(RenderScript rs, Element e,
-                                         int count, int usage) {
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        ElementThunker et = (ElementThunker) e;
-        try {
-            android.renderscript.Allocation a =
-                android.renderscript.Allocation.createSized
-                (rst.mN, (android.renderscript.Element)e.getNObj(), count, usage);
-            TypeThunker tt = new TypeThunker(rs, a.getType());
-            return new AllocationThunker(rs, tt, usage, a);
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-    }
-
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/BaseObj.java b/v8/renderscript/java/src/android/support/v8/renderscript/BaseObj.java
index b9d3ef4..bb49600 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/BaseObj.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/BaseObj.java
@@ -26,14 +26,14 @@
  *
  **/
 public class BaseObj {
-    BaseObj(int id, RenderScript rs) {
+    BaseObj(long id, RenderScript rs) {
         rs.validate();
         mRS = rs;
         mID = id;
         mDestroyed = false;
     }
 
-    void setID(int id) {
+    void setID(long id) {
         if (mID != 0) {
             throw new RSRuntimeException("Internal Error, reset of object ID.");
         }
@@ -47,9 +47,9 @@
      * @param rs Context to verify against internal context for
      *           match.
      *
-     * @return int
+     * @return long
      */
-    int getID(RenderScript rs) {
+    long getID(RenderScript rs) {
         mRS.validate();
         if (mDestroyed) {
             throw new RSInvalidStateException("using a destroyed object.");
@@ -73,7 +73,7 @@
         }
     }
 
-    private int mID;
+    private long mID;
     private boolean mDestroyed;
     RenderScript mRS;
 
@@ -124,7 +124,7 @@
      */
     @Override
     public int hashCode() {
-        return mID;
+        return (int)((mID & 0xfffffff) ^ (mID >> 32));
     }
 
     /**
@@ -148,10 +148,6 @@
             return false;
         }
 
-        if (mRS.isNative) {
-            return ((RenderScriptThunker)mRS).equals((Object)this, obj);
-        }
-
         BaseObj b = (BaseObj) obj;
         return mID == b.mID;
     }
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Byte2.java b/v8/renderscript/java/src/android/support/v8/renderscript/Byte2.java
index ed1eb9a..2371077 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Byte2.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/Byte2.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2102 The Android Open Source Project
+ * Copyright (C) 2012 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.
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Element.java b/v8/renderscript/java/src/android/support/v8/renderscript/Element.java
index 6189773..135d854 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Element.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/Element.java
@@ -151,11 +151,11 @@
         MATRIX_3X3 (17, 36),
         MATRIX_2X2 (18, 16),
 
-        RS_ELEMENT (1000, 4),
-        RS_TYPE (1001, 4),
-        RS_ALLOCATION (1002, 4),
-        RS_SAMPLER (1003, 4),
-        RS_SCRIPT (1004, 4);
+        RS_ELEMENT (1000),
+        RS_TYPE (1001),
+        RS_ALLOCATION (1002),
+        RS_SAMPLER (1003),
+        RS_SCRIPT (1004);
 
         int mID;
         int mSize;
@@ -163,6 +163,14 @@
             mID = id;
             mSize = size;
         }
+
+        DataType(int id) {
+            mID = id;
+            mSize = 4;
+            if (RenderScript.sPointerSize == 8) {
+                mSize = 32;
+            }
+        }
     }
 
     /**
@@ -706,7 +714,7 @@
         return rs.mElement_MATRIX_2X2;
     }
 
-    Element(int id, RenderScript rs, Element[] e, String[] n, int[] as) {
+    Element(long id, RenderScript rs, Element[] e, String[] n, int[] as) {
         super(id, rs);
         mSize = 0;
         mVectorSize = 1;
@@ -723,7 +731,7 @@
         updateVisibleSubElements();
     }
 
-    Element(int id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
+    Element(long id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
         super(id, rs);
         if ((dt != DataType.UNSIGNED_5_6_5) &&
             (dt != DataType.UNSIGNED_4_4_4_4) &&
@@ -742,10 +750,17 @@
         mVectorSize = size;
     }
 
-    Element(int id, RenderScript rs) {
+    Element(long id, RenderScript rs) {
         super(id, rs);
     }
 
+    /*
+     * Get an identical dummy Element for Compat Context
+     *
+     */
+    public long getDummyElement(RenderScript mRS) {
+        return mRS.nIncElementCreate(mType.mID, mKind.mID, mNormalized, mVectorSize);
+    }
     /**
      * Create a custom Element of the specified DataType.  The DataKind will be
      * set to USER and the vector size to 1 indicating non-vector.
@@ -755,14 +770,10 @@
      * @return Element
      */
     static Element createUser(RenderScript rs, DataType dt) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker)rs;
-            return ElementThunker.create(rst, dt);
-        }
         DataKind dk = DataKind.USER;
         boolean norm = false;
         int vecSize = 1;
-        int id = rs.nElementCreate(dt.mID, dk.mID, norm, vecSize);
+        long id = rs.nElementCreate(dt.mID, dk.mID, norm, vecSize);
         return new Element(id, rs, dt, dk, norm, vecSize);
     }
 
@@ -780,10 +791,6 @@
      * @return Element
      */
     public static Element createVector(RenderScript rs, DataType dt, int size) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker)rs;
-            return ElementThunker.createVector(rst, dt, size);
-        }
         if (size < 2 || size > 4) {
             throw new RSIllegalArgumentException("Vector size out of range 2-4.");
         }
@@ -803,7 +810,7 @@
         case BOOLEAN: {
             DataKind dk = DataKind.USER;
             boolean norm = false;
-            int id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
+            long id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
             return new Element(id, rs, dt, dk, norm, size);
         }
 
@@ -827,11 +834,6 @@
      * @return Element
      */
     public static Element createPixel(RenderScript rs, DataType dt, DataKind dk) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker)rs;
-            return ElementThunker.createPixel(rst, dt, dk);
-        }
-
         if (!(dk == DataKind.PIXEL_L ||
               dk == DataKind.PIXEL_A ||
               dk == DataKind.PIXEL_LA ||
@@ -876,7 +878,7 @@
         }
 
         boolean norm = true;
-        int id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
+        long id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
         return new Element(id, rs, dt, dk, norm, size);
     }
 
@@ -914,7 +916,6 @@
      *
      */
     public static class Builder {
-        ElementThunker.BuilderThunker mT;
 
         RenderScript mRS;
         Element[] mElements;
@@ -929,10 +930,6 @@
          * @param rs
          */
         public Builder(RenderScript rs) {
-            if (rs.isNative) {
-                RenderScriptThunker rst = (RenderScriptThunker)rs;
-                mT = new ElementThunker.BuilderThunker(rs);
-            }
             mRS = rs;
             mCount = 0;
             mElements = new Element[8];
@@ -948,11 +945,6 @@
          * @param arraySize
          */
         public Builder add(Element element, String name, int arraySize) {
-            if (mT != null) {
-                mT.add(element, name, arraySize);
-                return this;
-            }
-
             if (arraySize < 1) {
                 throw new RSIllegalArgumentException("Array size cannot be less than 1.");
             }
@@ -1007,10 +999,6 @@
          * @return Element
          */
         public Element create() {
-            if (mT != null) {
-                return mT.create(mRS);
-            }
-
             mRS.validate();
             Element[] ein = new Element[mCount];
             String[] sin = new String[mCount];
@@ -1019,12 +1007,12 @@
             java.lang.System.arraycopy(mElementNames, 0, sin, 0, mCount);
             java.lang.System.arraycopy(mArraySizes, 0, asin, 0, mCount);
 
-            int[] ids = new int[ein.length];
+            long[] ids = new long[ein.length];
             for (int ct = 0; ct < ein.length; ct++ ) {
                 ids[ct] = ein[ct].getID(mRS);
             }
 
-            int id = mRS.nElementCreate2(ids, sin, asin);
+            long id = mRS.nElementCreate2(ids, sin, asin);
             return new Element(id, mRS, ein, sin, asin);
         }
     }
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ElementThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ElementThunker.java
deleted file mode 100644
index 9b820e2..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ElementThunker.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import java.lang.reflect.Field;
-
-import android.util.Log;
-
-class ElementThunker extends Element {
-    android.renderscript.Element mN;
-
-    android.renderscript.Element getNObj() {
-        return mN;
-    }
-
-    public int getBytesSize() {
-        try {
-            return mN.getBytesSize();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public int getVectorSize() {
-        try {
-            return mN.getVectorSize();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    static android.renderscript.Element.DataKind convertKind(DataKind cdk) {
-        switch(cdk) {
-        case USER:
-            return android.renderscript.Element.DataKind.USER;
-        case PIXEL_L:
-            return android.renderscript.Element.DataKind.PIXEL_L;
-        case PIXEL_A:
-            return android.renderscript.Element.DataKind.PIXEL_A;
-        case PIXEL_LA:
-            return android.renderscript.Element.DataKind.PIXEL_LA;
-        case PIXEL_RGB:
-            return android.renderscript.Element.DataKind.PIXEL_RGB;
-        case PIXEL_RGBA:
-            return android.renderscript.Element.DataKind.PIXEL_RGBA;
-        }
-        return null;
-    }
-
-    static android.renderscript.Element.DataType convertType(DataType cdt) {
-        switch(cdt) {
-        case NONE:
-            return android.renderscript.Element.DataType.NONE;
-            //case DataType.FLOAT_16:
-        case FLOAT_32:
-            return android.renderscript.Element.DataType.FLOAT_32;
-        case FLOAT_64:
-            return android.renderscript.Element.DataType.FLOAT_64;
-        case SIGNED_8:
-            return android.renderscript.Element.DataType.SIGNED_8;
-        case SIGNED_16:
-            return android.renderscript.Element.DataType.SIGNED_16;
-        case SIGNED_32:
-            return android.renderscript.Element.DataType.SIGNED_32;
-        case SIGNED_64:
-            return android.renderscript.Element.DataType.SIGNED_64;
-        case UNSIGNED_8:
-            return android.renderscript.Element.DataType.UNSIGNED_8;
-        case UNSIGNED_16:
-            return android.renderscript.Element.DataType.UNSIGNED_16;
-        case UNSIGNED_32:
-            return android.renderscript.Element.DataType.UNSIGNED_32;
-        case UNSIGNED_64:
-            return android.renderscript.Element.DataType.UNSIGNED_64;
-
-        case BOOLEAN:
-            return android.renderscript.Element.DataType.BOOLEAN;
-
-        case MATRIX_4X4:
-            return android.renderscript.Element.DataType.MATRIX_4X4;
-        case MATRIX_3X3:
-            return android.renderscript.Element.DataType.MATRIX_3X3;
-        case MATRIX_2X2:
-            return android.renderscript.Element.DataType.MATRIX_2X2;
-
-        case RS_ELEMENT:
-            return android.renderscript.Element.DataType.RS_ELEMENT;
-        case RS_TYPE:
-            return android.renderscript.Element.DataType.RS_TYPE;
-        case RS_ALLOCATION:
-            return android.renderscript.Element.DataType.RS_ALLOCATION;
-        case RS_SAMPLER:
-            return android.renderscript.Element.DataType.RS_SAMPLER;
-        case RS_SCRIPT:
-            return android.renderscript.Element.DataType.RS_SCRIPT;
-        }
-        return null;
-    }
-
-    public boolean isComplex() {
-        try {
-            return mN.isComplex();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public int getSubElementCount() {
-        try {
-            return mN.getSubElementCount();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Element getSubElement(int index) {
-        try {
-            return new ElementThunker(mRS, mN.getSubElement(index));
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public String getSubElementName(int index) {
-        try {
-            return mN.getSubElementName(index);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public int getSubElementArraySize(int index) {
-        try {
-            return mN.getSubElementArraySize(index);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public int getSubElementOffsetBytes(int index) {
-        try {
-            return mN.getSubElementOffsetBytes(index);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public DataType getDataType() {
-        return mType;
-    }
-
-    public DataKind getDataKind() {
-        return mKind;
-    }
-
-
-    ElementThunker(RenderScript rs, android.renderscript.Element e) {
-        super(0, rs);
-        mN = e;
-    }
-
-
-    static Element create(RenderScript rs, DataType dt) {
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        try {
-            android.renderscript.Element e = null;
-            switch(dt) {
-            case FLOAT_32:
-                e = android.renderscript.Element.F32(rst.mN);
-                break;
-            case FLOAT_64:
-                e = android.renderscript.Element.F64(rst.mN);
-                break;
-            case SIGNED_8:
-                e = android.renderscript.Element.I8(rst.mN);
-                break;
-            case SIGNED_16:
-                e = android.renderscript.Element.I16(rst.mN);
-                break;
-            case SIGNED_32:
-                e = android.renderscript.Element.I32(rst.mN);
-                break;
-            case SIGNED_64:
-                e = android.renderscript.Element.I64(rst.mN);
-                break;
-            case UNSIGNED_8:
-                e = android.renderscript.Element.U8(rst.mN);
-                break;
-            case UNSIGNED_16:
-                e = android.renderscript.Element.U16(rst.mN);
-                break;
-            case UNSIGNED_32:
-                e = android.renderscript.Element.U32(rst.mN);
-                break;
-            case UNSIGNED_64:
-                e = android.renderscript.Element.U64(rst.mN);
-                break;
-
-            case BOOLEAN:
-                e = android.renderscript.Element.BOOLEAN(rst.mN);
-                break;
-
-            case MATRIX_4X4:
-                e = android.renderscript.Element.MATRIX_4X4(rst.mN);
-                break;
-            case MATRIX_3X3:
-                e = android.renderscript.Element.MATRIX_3X3(rst.mN);
-                break;
-            case MATRIX_2X2:
-                e = android.renderscript.Element.MATRIX_2X2(rst.mN);
-                break;
-
-            case RS_ELEMENT:
-                e = android.renderscript.Element.ELEMENT(rst.mN);
-                break;
-            case RS_TYPE:
-                e = android.renderscript.Element.TYPE(rst.mN);
-                break;
-            case RS_ALLOCATION:
-                e = android.renderscript.Element.ALLOCATION(rst.mN);
-                break;
-            case RS_SAMPLER:
-                e = android.renderscript.Element.SAMPLER(rst.mN);
-                break;
-            case RS_SCRIPT:
-                e = android.renderscript.Element.SCRIPT(rst.mN);
-                break;
-            }
-
-            return new ElementThunker(rs, e);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public static Element createVector(RenderScript rs, DataType dt, int size) {
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        android.renderscript.Element e;
-        try {
-            e = android.renderscript.Element.createVector(rst.mN, convertType(dt), size);
-            return new ElementThunker(rs, e);
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-    }
-
-    public static Element createPixel(RenderScript rs, DataType dt, DataKind dk) {
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        android.renderscript.Element e;
-        try {
-            e = android.renderscript.Element.createPixel(rst.mN, convertType(dt), convertKind(dk));
-        return new ElementThunker(rs, e);
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-    }
-
-    public boolean isCompatible(Element e) {
-        ElementThunker et = (ElementThunker)e;
-        try {
-            return et.mN.isCompatible(mN);
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-    }
-
-    static class BuilderThunker {
-        android.renderscript.Element.Builder mN;
-
-        public BuilderThunker(RenderScript rs) {
-            RenderScriptThunker rst = (RenderScriptThunker)rs;
-            try {
-                mN = new android.renderscript.Element.Builder(rst.mN);
-            } catch (android.renderscript.RSRuntimeException e) {
-                throw ExceptionThunker.convertException(e);
-            }
-        }
-
-        public void add(Element e, String name, int arraySize) {
-            ElementThunker et = (ElementThunker)e;
-            try {
-                mN.add(et.mN, name, arraySize);
-            } catch (android.renderscript.RSRuntimeException exc) {
-                throw ExceptionThunker.convertException(exc);
-            }
-        }
-
-        public Element create(RenderScript rs) {
-            try {
-                android.renderscript.Element e = mN.create();
-                return new ElementThunker(rs, e);
-            } catch (android.renderscript.RSRuntimeException exc) {
-                throw ExceptionThunker.convertException(exc);
-            }
-        }
-    }
-}
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ExceptionThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ExceptionThunker.java
deleted file mode 100644
index 3e553b7..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ExceptionThunker.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import java.lang.Exception;
-
-class ExceptionThunker {
-    static RuntimeException convertException (RuntimeException e) {
-        if (e instanceof android.renderscript.RSIllegalArgumentException) {
-            return new android.support.v8.renderscript.RSIllegalArgumentException(e.getMessage());
-        } else if (e instanceof android.renderscript.RSInvalidStateException) {
-            return new android.support.v8.renderscript.RSInvalidStateException(e.getMessage());
-        } else if (e instanceof android.renderscript.RSDriverException) {
-            return new android.support.v8.renderscript.RSDriverException(e.getMessage());
-        } else if (e instanceof android.renderscript.RSRuntimeException) {
-            return new android.support.v8.renderscript.RSRuntimeException(e.getMessage());
-        }
-        return e;
-    }
-
-}
\ No newline at end of file
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/FieldPacker.java b/v8/renderscript/java/src/android/support/v8/renderscript/FieldPacker.java
index a5b5b41..ccb6d95 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/FieldPacker.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/FieldPacker.java
@@ -17,6 +17,7 @@
 package android.support.v8.renderscript;
 
 import android.support.v8.renderscript.RenderScript;
+import java.util.BitSet;
 
 /**
  * Utility class for packing arguments and structures from Android system objects to
@@ -28,54 +29,65 @@
  *
  **/
 public class FieldPacker {
-    private FieldPackerThunker mN;
-
     public FieldPacker(int len) {
         mPos = 0;
         mLen = len;
         mData = new byte[len];
-        if (RenderScript.shouldThunk()) {
-            mN = new FieldPackerThunker(len);
-        }
+        mAlignment = new BitSet();
+    }
+
+    public FieldPacker(byte[] data) {
+        // Advance mPos to the end of the buffer, since we are copying in the
+        // full data input.
+        mPos = data.length;
+        mLen = data.length;
+        mData = data;
+        mAlignment = new BitSet();
+        // TODO: We should either have an actual FieldPacker copy constructor
+        // or drop support for computing alignment like this. As it stands,
+        // subAlign() can never work correctly for copied FieldPacker objects.
     }
 
     public void align(int v) {
-        if (RenderScript.shouldThunk()) {
-            mN.align(v);
-            return;
-        }
         if ((v <= 0) || ((v & (v - 1)) != 0)) {
             throw new RSIllegalArgumentException("argument must be a non-negative non-zero power of 2: " + v);
         }
 
         while ((mPos & (v - 1)) != 0) {
+            mAlignment.flip(mPos);
             mData[mPos++] = 0;
         }
     }
 
-    public void reset() {
-        if (RenderScript.shouldThunk()) {
-            mN.reset();
-            return;
+    public void subalign(int v) {
+        if ((v & (v - 1)) != 0) {
+            throw new RSIllegalArgumentException("argument must be a non-negative non-zero power of 2: " + v);
         }
+
+        while ((mPos & (v - 1)) != 0) {
+            mPos--;
+        }
+
+        if (mPos > 0) {
+            while (mAlignment.get(mPos - 1) == true) {
+                mPos--;
+                mAlignment.flip(mPos);
+            }
+        }
+
+    }
+
+    public void reset() {
         mPos = 0;
     }
     public void reset(int i) {
-        if (RenderScript.shouldThunk()) {
-            mN.reset(i);
-            return;
-        }
-        if ((i < 0) || (i >= mLen)) {
+        if ((i < 0) || (i > mLen)) {
             throw new RSIllegalArgumentException("out of range argument: " + i);
         }
         mPos = i;
     }
 
     public void skip(int i) {
-        if (RenderScript.shouldThunk()) {
-            mN.skip(i);
-            return;
-        }
         int res = mPos + i;
         if ((res < 0) || (res > mLen)) {
             throw new RSIllegalArgumentException("out of range argument: " + i);
@@ -84,28 +96,30 @@
     }
 
     public void addI8(byte v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI8(v);
-            return;
-        }
         mData[mPos++] = v;
     }
 
+    public byte subI8() {
+        subalign(1);
+        return mData[--mPos];
+    }
+
     public void addI16(short v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI16(v);
-            return;
-        }
         align(2);
         mData[mPos++] = (byte)(v & 0xff);
         mData[mPos++] = (byte)(v >> 8);
     }
 
+    public short subI16() {
+        subalign(2);
+        short v = 0;
+        v = (short)((mData[--mPos] & 0xff) << 8);
+        v = (short)(v | (short)(mData[--mPos] & 0xff));
+        return v;
+    }
+
+
     public void addI32(int v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI32(v);
-            return;
-        }
         align(4);
         mData[mPos++] = (byte)(v & 0xff);
         mData[mPos++] = (byte)((v >> 8) & 0xff);
@@ -113,11 +127,18 @@
         mData[mPos++] = (byte)((v >> 24) & 0xff);
     }
 
+    public int subI32() {
+        subalign(4);
+        int v = 0;
+        v = ((mData[--mPos] & 0xff) << 24);
+        v = v | ((mData[--mPos] & 0xff) << 16);
+        v = v | ((mData[--mPos] & 0xff) << 8);
+        v = v | ((mData[--mPos] & 0xff));
+        return v;
+    }
+
+
     public void addI64(long v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI64(v);
-            return;
-        }
         align(8);
         mData[mPos++] = (byte)(v & 0xff);
         mData[mPos++] = (byte)((v >> 8) & 0xff);
@@ -129,22 +150,38 @@
         mData[mPos++] = (byte)((v >> 56) & 0xff);
     }
 
+    public long subI64() {
+        subalign(8);
+        long v = 0;
+        byte x = 0;
+        x = ((mData[--mPos]));
+        v = (long)(v | (((long)x) & 0xff) << 56l);
+        x = ((mData[--mPos]));
+        v = (long)(v | (((long)x) & 0xff) << 48l);
+        x = ((mData[--mPos]));
+        v = (long)(v | (((long)x) & 0xff) << 40l);
+        x = ((mData[--mPos]));
+        v = (long)(v | (((long)x) & 0xff) << 32l);
+        x = ((mData[--mPos]));
+        v = (long)(v | (((long)x) & 0xff) << 24l);
+        x = ((mData[--mPos]));
+        v = (long)(v | (((long)x) & 0xff) << 16l);
+        x = ((mData[--mPos]));
+        v = (long)(v | (((long)x) & 0xff) << 8l);
+        x = ((mData[--mPos]));
+        v = (long)(v | (((long)x) & 0xff));
+        return v;
+    }
+
     public void addU8(short v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU8(v);
-            return;
-        }
         if ((v < 0) || (v > 0xff)) {
+            android.util.Log.e("rs", "FieldPacker.addU8( " + v + " )");
             throw new IllegalArgumentException("Saving value out of range for type");
         }
         mData[mPos++] = (byte)v;
     }
 
     public void addU16(int v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU16(v);
-            return;
-        }
         if ((v < 0) || (v > 0xffff)) {
             android.util.Log.e("rs", "FieldPacker.addU16( " + v + " )");
             throw new IllegalArgumentException("Saving value out of range for type");
@@ -155,10 +192,6 @@
     }
 
     public void addU32(long v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU32(v);
-            return;
-        }
         if ((v < 0) || (v > 0xffffffffL)) {
             android.util.Log.e("rs", "FieldPacker.addU32( " + v + " )");
             throw new IllegalArgumentException("Saving value out of range for type");
@@ -171,10 +204,6 @@
     }
 
     public void addU64(long v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU64(v);
-            return;
-        }
         if (v < 0) {
             android.util.Log.e("rs", "FieldPacker.addU64( " + v + " )");
             throw new IllegalArgumentException("Saving value out of range for type");
@@ -191,55 +220,54 @@
     }
 
     public void addF32(float v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addF32(v);
-            return;
-        }
         addI32(Float.floatToRawIntBits(v));
     }
 
+    public float subF32() {
+        return Float.intBitsToFloat(subI32());
+    }
+
     public void addF64(double v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addF64(v);
-            return;
-        }
         addI64(Double.doubleToRawLongBits(v));
     }
 
+    public double subF64() {
+        return Double.longBitsToDouble(subI64());
+    }
+
     public void addObj(BaseObj obj) {
-        if (RenderScript.shouldThunk()) {
-            mN.addObj(obj);
-            return;
-        }
         if (obj != null) {
-            addI32(obj.getID(null));
+            if (RenderScript.sPointerSize == 8) {
+                addI64(obj.getID(null));
+                addI64(0);
+                addI64(0);
+                addI64(0);
+            }
+            else {
+                addI32((int)obj.getID(null));
+            }
         } else {
-            addI32(0);
+            if (RenderScript.sPointerSize == 8) {
+                addI64(0);
+                addI64(0);
+                addI64(0);
+                addI64(0);
+            } else {
+                addI32(0);
+            }
         }
     }
 
     public void addF32(Float2 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addF32(v);
-            return;
-        }
         addF32(v.x);
         addF32(v.y);
     }
     public void addF32(Float3 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addF32(v);
-            return;
-        }
         addF32(v.x);
         addF32(v.y);
         addF32(v.z);
     }
     public void addF32(Float4 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addF32(v);
-            return;
-        }
         addF32(v.x);
         addF32(v.y);
         addF32(v.z);
@@ -247,27 +275,15 @@
     }
 
     public void addF64(Double2 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addF64(v);
-            return;
-        }
         addF64(v.x);
         addF64(v.y);
     }
     public void addF64(Double3 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addF64(v);
-            return;
-        }
         addF64(v.x);
         addF64(v.y);
         addF64(v.z);
     }
     public void addF64(Double4 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addF64(v);
-            return;
-        }
         addF64(v.x);
         addF64(v.y);
         addF64(v.z);
@@ -275,27 +291,15 @@
     }
 
     public void addI8(Byte2 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI8(v);
-            return;
-        }
         addI8(v.x);
         addI8(v.y);
     }
     public void addI8(Byte3 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI8(v);
-            return;
-        }
         addI8(v.x);
         addI8(v.y);
         addI8(v.z);
     }
     public void addI8(Byte4 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI8(v);
-            return;
-        }
         addI8(v.x);
         addI8(v.y);
         addI8(v.z);
@@ -303,27 +307,15 @@
     }
 
     public void addU8(Short2 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU8(v);
-            return;
-        }
         addU8(v.x);
         addU8(v.y);
     }
     public void addU8(Short3 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU8(v);
-            return;
-        }
         addU8(v.x);
         addU8(v.y);
         addU8(v.z);
     }
     public void addU8(Short4 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU8(v);
-            return;
-        }
         addU8(v.x);
         addU8(v.y);
         addU8(v.z);
@@ -331,27 +323,15 @@
     }
 
     public void addI16(Short2 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI16(v);
-            return;
-        }
         addI16(v.x);
         addI16(v.y);
     }
     public void addI16(Short3 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI16(v);
-            return;
-        }
         addI16(v.x);
         addI16(v.y);
         addI16(v.z);
     }
     public void addI16(Short4 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI16(v);
-            return;
-        }
         addI16(v.x);
         addI16(v.y);
         addI16(v.z);
@@ -359,27 +339,15 @@
     }
 
     public void addU16(Int2 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU16(v);
-            return;
-        }
         addU16(v.x);
         addU16(v.y);
     }
     public void addU16(Int3 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU16(v);
-            return;
-        }
         addU16(v.x);
         addU16(v.y);
         addU16(v.z);
     }
     public void addU16(Int4 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU16(v);
-            return;
-        }
         addU16(v.x);
         addU16(v.y);
         addU16(v.z);
@@ -387,27 +355,15 @@
     }
 
     public void addI32(Int2 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI32(v);
-            return;
-        }
         addI32(v.x);
         addI32(v.y);
     }
     public void addI32(Int3 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI32(v);
-            return;
-        }
         addI32(v.x);
         addI32(v.y);
         addI32(v.z);
     }
     public void addI32(Int4 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI32(v);
-            return;
-        }
         addI32(v.x);
         addI32(v.y);
         addI32(v.z);
@@ -415,27 +371,15 @@
     }
 
     public void addU32(Long2 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU32(v);
-            return;
-        }
         addU32(v.x);
         addU32(v.y);
     }
     public void addU32(Long3 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU32(v);
-            return;
-        }
         addU32(v.x);
         addU32(v.y);
         addU32(v.z);
     }
     public void addU32(Long4 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU32(v);
-            return;
-        }
         addU32(v.x);
         addU32(v.y);
         addU32(v.z);
@@ -443,27 +387,15 @@
     }
 
     public void addI64(Long2 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI64(v);
-            return;
-        }
         addI64(v.x);
         addI64(v.y);
     }
     public void addI64(Long3 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI64(v);
-            return;
-        }
         addI64(v.x);
         addI64(v.y);
         addI64(v.z);
     }
     public void addI64(Long4 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addI64(v);
-            return;
-        }
         addI64(v.x);
         addI64(v.y);
         addI64(v.z);
@@ -471,88 +403,510 @@
     }
 
     public void addU64(Long2 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU64(v);
-            return;
-        }
         addU64(v.x);
         addU64(v.y);
     }
     public void addU64(Long3 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU64(v);
-            return;
-        }
         addU64(v.x);
         addU64(v.y);
         addU64(v.z);
     }
     public void addU64(Long4 v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addU64(v);
-            return;
-        }
         addU64(v.x);
         addU64(v.y);
         addU64(v.z);
         addU64(v.w);
     }
 
+
+    public Float2 subFloat2() {
+        Float2 v = new Float2();
+        v.y = subF32();
+        v.x = subF32();
+        return v;
+    }
+    public Float3 subFloat3() {
+        Float3 v = new Float3();
+        v.z = subF32();
+        v.y = subF32();
+        v.x = subF32();
+        return v;
+    }
+    public Float4 subFloat4() {
+        Float4 v = new Float4();
+        v.w = subF32();
+        v.z = subF32();
+        v.y = subF32();
+        v.x = subF32();
+        return v;
+    }
+
+    public Double2 subDouble2() {
+        Double2 v = new Double2();
+        v.y = subF64();
+        v.x = subF64();
+        return v;
+    }
+    public Double3 subDouble3() {
+        Double3 v = new Double3();
+        v.z = subF64();
+        v.y = subF64();
+        v.x = subF64();
+        return v;
+    }
+    public Double4 subDouble4() {
+        Double4 v = new Double4();
+        v.w = subF64();
+        v.z = subF64();
+        v.y = subF64();
+        v.x = subF64();
+        return v;
+    }
+
+    public Byte2 subByte2() {
+        Byte2 v = new Byte2();
+        v.y = subI8();
+        v.x = subI8();
+        return v;
+    }
+    public Byte3 subByte3() {
+        Byte3 v = new Byte3();
+        v.z = subI8();
+        v.y = subI8();
+        v.x = subI8();
+        return v;
+    }
+    public Byte4 subByte4() {
+        Byte4 v = new Byte4();
+        v.w = subI8();
+        v.z = subI8();
+        v.y = subI8();
+        v.x = subI8();
+        return v;
+    }
+
+    public Short2 subShort2() {
+        Short2 v = new Short2();
+        v.y = subI16();
+        v.x = subI16();
+        return v;
+    }
+    public Short3 subShort3() {
+        Short3 v = new Short3();
+        v.z = subI16();
+        v.y = subI16();
+        v.x = subI16();
+        return v;
+    }
+    public Short4 subShort4() {
+        Short4 v = new Short4();
+        v.w = subI16();
+        v.z = subI16();
+        v.y = subI16();
+        v.x = subI16();
+        return v;
+    }
+
+    public Int2 subInt2() {
+        Int2 v = new Int2();
+        v.y = subI32();
+        v.x = subI32();
+        return v;
+    }
+    public Int3 subInt3() {
+        Int3 v = new Int3();
+        v.z = subI32();
+        v.y = subI32();
+        v.x = subI32();
+        return v;
+    }
+    public Int4 subInt4() {
+        Int4 v = new Int4();
+        v.w = subI32();
+        v.z = subI32();
+        v.y = subI32();
+        v.x = subI32();
+        return v;
+    }
+
+    public Long2 subLong2() {
+        Long2 v = new Long2();
+        v.y = subI64();
+        v.x = subI64();
+        return v;
+    }
+    public Long3 subLong3() {
+        Long3 v = new Long3();
+        v.z = subI64();
+        v.y = subI64();
+        v.x = subI64();
+        return v;
+    }
+    public Long4 subLong4() {
+        Long4 v = new Long4();
+        v.w = subI64();
+        v.z = subI64();
+        v.y = subI64();
+        v.x = subI64();
+        return v;
+    }
+
+
+
     public void addMatrix(Matrix4f v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addMatrix(v);
-            return;
-        }
         for (int i=0; i < v.mMat.length; i++) {
             addF32(v.mMat[i]);
         }
     }
 
+    public Matrix4f subMatrix4f() {
+        Matrix4f v = new Matrix4f();
+        for (int i = v.mMat.length - 1; i >= 0; i--) {
+            v.mMat[i] = subF32();
+        }
+        return v;
+    }
+
     public void addMatrix(Matrix3f v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addMatrix(v);
-            return;
-        }
         for (int i=0; i < v.mMat.length; i++) {
             addF32(v.mMat[i]);
         }
     }
 
+    public Matrix3f subMatrix3f() {
+        Matrix3f v = new Matrix3f();
+        for (int i = v.mMat.length - 1; i >= 0; i--) {
+            v.mMat[i] = subF32();
+        }
+        return v;
+    }
+
     public void addMatrix(Matrix2f v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addMatrix(v);
-            return;
-        }
         for (int i=0; i < v.mMat.length; i++) {
             addF32(v.mMat[i]);
         }
     }
 
-    public void addBoolean(boolean v) {
-        if (RenderScript.shouldThunk()) {
-            mN.addBoolean(v);
-            return;
+    public Matrix2f subMatrix2f() {
+        Matrix2f v = new Matrix2f();
+        for (int i = v.mMat.length - 1; i >= 0; i--) {
+            v.mMat[i] = subF32();
         }
+        return v;
+    }
+
+    public void addBoolean(boolean v) {
         addI8((byte)(v ? 1 : 0));
     }
 
-    public final byte[] getData() {
-        if (RenderScript.shouldThunk()) {
-            return mN.getData();
+    public boolean subBoolean() {
+        byte v = subI8();
+        if (v == 1) {
+            return true;
         }
+        return false;
+    }
+
+    public final byte[] getData() {
         return mData;
     }
 
+    /**
+     * Get the actual length used for the FieldPacker.
+     *
+     * @hide
+     */
     public int getPos() {
-        if (RenderScript.shouldThunk()) {
-            return mN.getPos();
-        }
         return mPos;
     }
 
+    private static void addToPack(FieldPacker fp, Object obj) {
+        if (obj instanceof Boolean) {
+            fp.addBoolean(((Boolean)obj).booleanValue());
+            return;
+        }
+
+        if (obj instanceof Byte) {
+            fp.addI8(((Byte)obj).byteValue());
+            return;
+        }
+
+        if (obj instanceof Short) {
+            fp.addI16(((Short)obj).shortValue());
+            return;
+        }
+
+        if (obj instanceof Integer) {
+            fp.addI32(((Integer)obj).intValue());
+            return;
+        }
+
+        if (obj instanceof Long) {
+            fp.addI64(((Long)obj).longValue());
+            return;
+        }
+
+        if (obj instanceof Float) {
+            fp.addF32(((Float)obj).floatValue());
+            return;
+        }
+
+        if (obj instanceof Double) {
+            fp.addF64(((Double)obj).doubleValue());
+            return;
+        }
+
+        if (obj instanceof Byte2) {
+            fp.addI8((Byte2)obj);
+            return;
+        }
+
+        if (obj instanceof Byte3) {
+            fp.addI8((Byte3)obj);
+            return;
+        }
+
+        if (obj instanceof Byte4) {
+            fp.addI8((Byte4)obj);
+            return;
+        }
+
+        if (obj instanceof Short2) {
+            fp.addI16((Short2)obj);
+            return;
+        }
+
+        if (obj instanceof Short3) {
+            fp.addI16((Short3)obj);
+            return;
+        }
+
+        if (obj instanceof Short4) {
+            fp.addI16((Short4)obj);
+            return;
+        }
+
+        if (obj instanceof Int2) {
+            fp.addI32((Int2)obj);
+            return;
+        }
+
+        if (obj instanceof Int3) {
+            fp.addI32((Int3)obj);
+            return;
+        }
+
+        if (obj instanceof Int4) {
+            fp.addI32((Int4)obj);
+            return;
+        }
+
+        if (obj instanceof Long2) {
+            fp.addI64((Long2)obj);
+            return;
+        }
+
+        if (obj instanceof Long3) {
+            fp.addI64((Long3)obj);
+            return;
+        }
+
+        if (obj instanceof Long4) {
+            fp.addI64((Long4)obj);
+            return;
+        }
+
+        if (obj instanceof Float2) {
+            fp.addF32((Float2)obj);
+            return;
+        }
+
+        if (obj instanceof Float3) {
+            fp.addF32((Float3)obj);
+            return;
+        }
+
+        if (obj instanceof Float4) {
+            fp.addF32((Float4)obj);
+            return;
+        }
+
+        if (obj instanceof Double2) {
+            fp.addF64((Double2)obj);
+            return;
+        }
+
+        if (obj instanceof Double3) {
+            fp.addF64((Double3)obj);
+            return;
+        }
+
+        if (obj instanceof Double4) {
+            fp.addF64((Double4)obj);
+            return;
+        }
+
+        if (obj instanceof Matrix2f) {
+            fp.addMatrix((Matrix2f)obj);
+            return;
+        }
+
+        if (obj instanceof Matrix3f) {
+            fp.addMatrix((Matrix3f)obj);
+            return;
+        }
+
+        if (obj instanceof Matrix4f) {
+            fp.addMatrix((Matrix4f)obj);
+            return;
+        }
+
+        if (obj instanceof BaseObj) {
+            fp.addObj((BaseObj)obj);
+            return;
+        }
+    }
+
+    private static int getPackedSize(Object obj) {
+        if (obj instanceof Boolean) {
+            return 1;
+        }
+
+        if (obj instanceof Byte) {
+            return 1;
+        }
+
+        if (obj instanceof Short) {
+            return 2;
+        }
+
+        if (obj instanceof Integer) {
+            return 4;
+        }
+
+        if (obj instanceof Long) {
+            return 8;
+        }
+
+        if (obj instanceof Float) {
+            return 4;
+        }
+
+        if (obj instanceof Double) {
+            return 8;
+        }
+
+        if (obj instanceof Byte2) {
+            return 2;
+        }
+
+        if (obj instanceof Byte3) {
+            return 3;
+        }
+
+        if (obj instanceof Byte4) {
+            return 4;
+        }
+
+        if (obj instanceof Short2) {
+            return 4;
+        }
+
+        if (obj instanceof Short3) {
+            return 6;
+        }
+
+        if (obj instanceof Short4) {
+            return 8;
+        }
+
+        if (obj instanceof Int2) {
+            return 8;
+        }
+
+        if (obj instanceof Int3) {
+            return 12;
+        }
+
+        if (obj instanceof Int4) {
+            return 16;
+        }
+
+        if (obj instanceof Long2) {
+            return 16;
+        }
+
+        if (obj instanceof Long3) {
+            return 24;
+        }
+
+        if (obj instanceof Long4) {
+            return 32;
+        }
+
+        if (obj instanceof Float2) {
+            return 8;
+        }
+
+        if (obj instanceof Float3) {
+            return 12;
+        }
+
+        if (obj instanceof Float4) {
+            return 16;
+        }
+
+        if (obj instanceof Double2) {
+            return 16;
+        }
+
+        if (obj instanceof Double3) {
+            return 24;
+        }
+
+        if (obj instanceof Double4) {
+            return 32;
+        }
+
+        if (obj instanceof Matrix2f) {
+            return 16;
+        }
+
+        if (obj instanceof Matrix3f) {
+            return 36;
+        }
+
+        if (obj instanceof Matrix4f) {
+            return 64;
+        }
+
+        if (obj instanceof BaseObj) {
+            if (RenderScript.sPointerSize == 8) {
+                return 32;
+            } else {
+                return 4;
+            }
+        }
+
+        return 0;
+    }
+
+    static FieldPacker createFieldPack(Object[] args) {
+        int len = 0;
+        for (Object arg : args) {
+            len += getPackedSize(arg);
+        }
+        FieldPacker fp = new FieldPacker(len);
+        for (Object arg : args) {
+            addToPack(fp, arg);
+        }
+        return fp;
+    }
+
     private final byte mData[];
     private int mPos;
     private int mLen;
+    private BitSet mAlignment;
 
 }
 
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/FieldPackerThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/FieldPackerThunker.java
deleted file mode 100644
index b61e482..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/FieldPackerThunker.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2012 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.v8.renderscript;
-
-import android.support.v8.renderscript.RenderScript;
-
-/**
- * Utility class for packing arguments and structures from Android system objects to
- * RenderScript objects.
- *
- * This class is only intended to be used to support the
- * reflected code generated by the RS tool chain.  It should not
- * be called directly.
- *
- **/
-public class FieldPackerThunker {
-    private android.renderscript.FieldPacker mN;
-    private int mPos;
-
-    public FieldPackerThunker(int len) {
-        mN = new android.renderscript.FieldPacker(len);
-        mPos = 0;
-    }
-
-    void align(int v) {
-        mN.align(v);
-        while ((mPos & (v - 1)) != 0) {
-            mPos++;
-        }
-    }
-
-    void reset() {
-        mN.reset();
-        mPos = 0;
-    }
-
-    void reset(int i) {
-        mN.reset(i);
-        mPos = i;
-    }
-
-    public void skip(int i) {
-        mN.skip(i);
-        mPos += i;
-    }
-
-    public void addI8(byte v) {
-        mN.addI8(v);
-        mPos++;
-    }
-
-    public void addI16(short v) {
-        mN.addI16(v);
-        mPos += 2;
-    }
-
-    public void addI32(int v) {
-        mN.addI32(v);
-        mPos += 4;
-    }
-
-    public void addI64(long v) {
-        mN.addI64(v);
-        mPos += 8;
-    }
-
-    public void addU8(short v) {
-        mN.addU8(v);
-        mPos++;
-    }
-
-    public void addU16(int v) {
-        mN.addU16(v);
-        mPos += 2;
-    }
-
-    public void addU32(long v) {
-        mN.addU32(v);
-        mPos += 4;
-    }
-
-    public void addU64(long v) {
-        mN.addU64(v);
-        mPos += 8;
-    }
-
-    public void addF32(float v) {
-        mN.addF32(v);
-        mPos += 4;
-    }
-
-    public void addF64(double v) {
-        mN.addF64(v);
-        mPos += 8;
-    }
-
-    public void addObj(BaseObj obj) {
-        if (obj != null) {
-            mN.addObj(obj.getNObj());
-        } else {
-            mN.addObj(null);
-        }
-        mPos += 4;  // Compat lib only works in 32-bit mode, so objects are 4 bytes.
-    }
-
-    public void addF32(Float2 v) {
-        mN.addF32(new android.renderscript.Float2(v.x, v.y));
-        mPos += 8;
-    }
-    public void addF32(Float3 v) {
-        mN.addF32(new android.renderscript.Float3(v.x, v.y, v.z));
-        mPos += 12;
-    }
-    public void addF32(Float4 v) {
-        mN.addF32(new android.renderscript.Float4(v.x, v.y, v.z, v.w));
-        mPos += 16;
-    }
-
-    public void addF64(Double2 v) {
-        mN.addF64(new android.renderscript.Double2(v.x, v.y));
-        mPos += 16;
-    }
-    public void addF64(Double3 v) {
-        mN.addF64(new android.renderscript.Double3(v.x, v.y, v.z));
-        mPos += 24;
-    }
-    public void addF64(Double4 v) {
-        mN.addF64(new android.renderscript.Double4(v.x, v.y, v.z, v.w));
-        mPos += 32;
-    }
-
-    public void addI8(Byte2 v) {
-        mN.addI8(new android.renderscript.Byte2(v.x, v.y));
-        mPos += 2;
-    }
-    public void addI8(Byte3 v) {
-        mN.addI8(new android.renderscript.Byte3(v.x, v.y, v.z));
-        mPos += 3;
-    }
-    public void addI8(Byte4 v) {
-        mN.addI8(new android.renderscript.Byte4(v.x, v.y, v.z, v.w));
-        mPos += 4;
-    }
-
-    public void addU8(Short2 v) {
-        mN.addU8(new android.renderscript.Short2(v.x, v.y));
-        mPos += 2;
-    }
-    public void addU8(Short3 v) {
-        mN.addU8(new android.renderscript.Short3(v.x, v.y, v.z));
-        mPos += 3;
-    }
-    public void addU8(Short4 v) {
-        mN.addU8(new android.renderscript.Short4(v.x, v.y, v.z, v.w));
-        mPos += 4;
-    }
-
-    public void addI16(Short2 v) {
-        mN.addI16(new android.renderscript.Short2(v.x, v.y));
-        mPos += 4;
-    }
-    public void addI16(Short3 v) {
-        mN.addI16(new android.renderscript.Short3(v.x, v.y, v.z));
-        mPos += 6;
-    }
-    public void addI16(Short4 v) {
-        mN.addI16(new android.renderscript.Short4(v.x, v.y, v.z, v.w));
-        mPos += 8;
-    }
-
-    public void addU16(Int2 v) {
-        mN.addU16(new android.renderscript.Int2(v.x, v.y));
-        mPos += 4;
-    }
-    public void addU16(Int3 v) {
-        mN.addU16(new android.renderscript.Int3(v.x, v.y, v.z));
-        mPos += 6;
-    }
-    public void addU16(Int4 v) {
-        mN.addU16(new android.renderscript.Int4(v.x, v.y, v.z, v.w));
-        mPos += 8;
-    }
-
-    public void addI32(Int2 v) {
-        mN.addI32(new android.renderscript.Int2(v.x, v.y));
-        mPos += 8;
-    }
-    public void addI32(Int3 v) {
-        mN.addI32(new android.renderscript.Int3(v.x, v.y, v.z));
-        mPos += 12;
-    }
-    public void addI32(Int4 v) {
-        mN.addI32(new android.renderscript.Int4(v.x, v.y, v.z, v.w));
-        mPos += 16;
-    }
-
-    public void addU32(Long2 v) {
-        mN.addU32(new android.renderscript.Long2(v.x, v.y));
-        mPos += 8;
-    }
-    public void addU32(Long3 v) {
-        mN.addU32(new android.renderscript.Long3(v.x, v.y, v.z));
-        mPos += 12;
-    }
-    public void addU32(Long4 v) {
-        mN.addU32(new android.renderscript.Long4(v.x, v.y, v.z, v.w));
-        mPos += 16;
-    }
-
-    public void addI64(Long2 v) {
-        mN.addI64(new android.renderscript.Long2(v.x, v.y));
-        mPos += 16;
-    }
-    public void addI64(Long3 v) {
-        mN.addI64(new android.renderscript.Long3(v.x, v.y, v.z));
-        mPos += 24;
-    }
-    public void addI64(Long4 v) {
-        mN.addI64(new android.renderscript.Long4(v.x, v.y, v.z, v.w));
-        mPos += 32;
-    }
-
-    public void addU64(Long2 v) {
-        mN.addU64(new android.renderscript.Long2(v.x, v.y));
-        mPos += 16;
-    }
-    public void addU64(Long3 v) {
-        mN.addU64(new android.renderscript.Long3(v.x, v.y, v.z));
-        mPos += 24;
-    }
-    public void addU64(Long4 v) {
-        mN.addU64(new android.renderscript.Long4(v.x, v.y, v.z, v.w));
-        mPos += 32;
-    }
-
-    public void addMatrix(Matrix4f v) {
-        mN.addMatrix(new android.renderscript.Matrix4f(v.getArray()));
-        mPos += (4 * 4 * 4);
-    }
-
-    public void addMatrix(Matrix3f v) {
-        mN.addMatrix(new android.renderscript.Matrix3f(v.getArray()));
-        mPos += (3 * 3 * 4);
-    }
-
-    public void addMatrix(Matrix2f v) {
-        mN.addMatrix(new android.renderscript.Matrix2f(v.getArray()));
-        mPos += (2 * 2 * 4);
-    }
-
-    public void addBoolean(boolean v) {
-        mN.addBoolean(v);
-        mPos++;
-    }
-
-    public final byte[] getData() {
-        return mN.getData();
-    }
-
-    // We must compute our own mPos, since this API is not available in older target APIs.
-    public int getPos() {
-        return mPos;
-    }
-}
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
index 762c715..8f681d8 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
@@ -49,6 +49,7 @@
     static final boolean LOG_ENABLED = false;
 
     private Context mApplicationContext;
+    private String mNativeLibDir;
 
     /*
      * We use a class initializer to allow the native code to cache some
@@ -64,35 +65,37 @@
     static Object lock = new Object();
 
     // Non-threadsafe functions.
-    native int  nDeviceCreate();
-    native void nDeviceDestroy(int dev);
-    native void nDeviceSetConfig(int dev, int param, int value);
-    native int nContextGetUserMessage(int con, int[] data);
-    native String nContextGetErrorMessage(int con);
-    native int  nContextPeekMessage(int con, int[] subID);
-    native void nContextInitToClient(int con);
-    native void nContextDeinitToClient(int con);
+    native boolean nLoadSO(boolean useNative);
+    native boolean nLoadIOSO();
+    native long nDeviceCreate();
+    native void nDeviceDestroy(long dev);
+    native void nDeviceSetConfig(long dev, int param, int value);
+    native int nContextGetUserMessage(long con, int[] data);
+    native String nContextGetErrorMessage(long con);
+    native int  nContextPeekMessage(long con, int[] subID);
+    native void nContextInitToClient(long con);
+    native void nContextDeinitToClient(long con);
 
-    static boolean isNative = false;
-
-    static private int sThunk = -1;
+    static private int sNative = -1;
     static private int sSdkVersion = -1;
+    static private boolean useIOlib = false;
+    static private boolean useNative;
 
-    static boolean shouldThunk() {
-        if (sThunk == -1) {
-            throw new RSRuntimeException("Can't use RS classes before setting up a RenderScript context");
-        } else if (sThunk == 1) {
-            return true;
-        }
-        return false;
+    boolean isUseNative() {
+        return useNative;
     }
+    /*
+     * Detect the bitness of the VM to allow FieldPacker to do the right thing.
+     */
+    static native int rsnSystemGetPointerSize();
+    static int sPointerSize;
 
     /**
      * Determines whether or not we should be thunking into the native
      * RenderScript layer or actually using the compatibility library.
      */
-    static private boolean setupThunk(int sdkVersion, Context ctx) {
-        if (sThunk == -1) {
+    static private boolean setupNative(int sdkVersion, Context ctx) {
+        if (sNative == -1) {
 
             // get the value of the debug.rs.forcecompat property
             int forcecompat = 0;
@@ -106,19 +109,15 @@
 
             }
 
-            // use compat on Jelly Bean MR2 if we're requesting SDK 19+
-            if (android.os.Build.VERSION.SDK_INT == 18 && sdkVersion >= 19) {
-                sThunk = 0;
-            }
-            else if ((android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2)
+            if ((android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT)
                      && forcecompat == 0) {
-                sThunk = 1;
+                sNative = 1;
             } else {
-                sThunk = 0;
+                sNative = 0;
             }
 
 
-            if (sThunk == 1) {
+            if (sNative == 1) {
                 // Workarounds that may disable thunking go here
                 ApplicationInfo info;
                 try {
@@ -143,7 +142,7 @@
                     // asynchronous teardown: minor version 1+
                     if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableAsyncTeardown") == true) {
                         if (minorVersion == 0) {
-                            sThunk = 0;
+                            sNative = 0;
                         }
                     }
 
@@ -151,7 +150,7 @@
                     if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableBlurWorkaround") == true) {
                         if (android.os.Build.VERSION.SDK_INT <= 19) {
                             //android.util.Log.e("rs", "war on");
-                            sThunk = 0;
+                            sNative = 0;
                         }
                     }
                 }
@@ -159,7 +158,7 @@
             }
         }
 
-        if (sThunk == 1) {
+        if (sNative == 1) {
             return true;
         }
         return false;
@@ -217,11 +216,12 @@
 
     // Methods below are wrapped to protect the non-threadsafe
     // lockless fifo.
-    native int  rsnContextCreate(int dev, int ver, int sdkVer, int contextType);
-    synchronized int nContextCreate(int dev, int ver, int sdkVer, int contextType) {
-        return rsnContextCreate(dev, ver, sdkVer, contextType);
+
+    native long  rsnContextCreate(long dev, int ver, int sdkVer, int contextType, String nativeLibDir);
+    synchronized long nContextCreate(long dev, int ver, int sdkVer, int contextType, String nativeLibDir) {
+        return rsnContextCreate(dev, ver, sdkVer, contextType, nativeLibDir);
     }
-    native void rsnContextDestroy(int con);
+    native void rsnContextDestroy(long con);
     synchronized void nContextDestroy() {
         validate();
 
@@ -230,38 +230,38 @@
         ReentrantReadWriteLock.WriteLock wlock = mRWLock.writeLock();
         wlock.lock();
 
-        int curCon = mContext;
+        long curCon = mContext;
         // context is considered dead as of this point
         mContext = 0;
 
         wlock.unlock();
         rsnContextDestroy(curCon);
     }
-    native void rsnContextSetPriority(int con, int p);
+    native void rsnContextSetPriority(long con, int p);
     synchronized void nContextSetPriority(int p) {
         validate();
         rsnContextSetPriority(mContext, p);
     }
-    native void rsnContextDump(int con, int bits);
+    native void rsnContextDump(long con, int bits);
     synchronized void nContextDump(int bits) {
         validate();
         rsnContextDump(mContext, bits);
     }
-    native void rsnContextFinish(int con);
+    native void rsnContextFinish(long con);
     synchronized void nContextFinish() {
         validate();
         rsnContextFinish(mContext);
     }
 
-    native void rsnContextSendMessage(int con, int id, int[] data);
+    native void rsnContextSendMessage(long con, int id, int[] data);
     synchronized void nContextSendMessage(int id, int[] data) {
         validate();
         rsnContextSendMessage(mContext, id, data);
     }
 
     // nObjDestroy is explicitly _not_ synchronous to prevent crashes in finalizers
-    native void rsnObjDestroy(int con, int id);
-    void nObjDestroy(int id) {
+    native void rsnObjDestroy(long con, long id);
+    void nObjDestroy(long id) {
         // There is a race condition here.  The calling code may be run
         // by the gc while teardown is occuring.  This protects againts
         // deleting dead objects.
@@ -270,146 +270,148 @@
         }
     }
 
-    native int  rsnElementCreate(int con, int type, int kind, boolean norm, int vecSize);
-    synchronized int nElementCreate(int type, int kind, boolean norm, int vecSize) {
+    native long  rsnElementCreate(long con, long type, int kind, boolean norm, int vecSize);
+    synchronized long nElementCreate(long type, int kind, boolean norm, int vecSize) {
         validate();
         return rsnElementCreate(mContext, type, kind, norm, vecSize);
     }
-    native int  rsnElementCreate2(int con, int[] elements, String[] names, int[] arraySizes);
-    synchronized int nElementCreate2(int[] elements, String[] names, int[] arraySizes) {
+    native long  rsnElementCreate2(long con, long[] elements, String[] names, int[] arraySizes);
+    synchronized long nElementCreate2(long[] elements, String[] names, int[] arraySizes) {
         validate();
         return rsnElementCreate2(mContext, elements, names, arraySizes);
     }
-    native void rsnElementGetNativeData(int con, int id, int[] elementData);
-    synchronized void nElementGetNativeData(int id, int[] elementData) {
+    native void rsnElementGetNativeData(long con, long id, int[] elementData);
+    synchronized void nElementGetNativeData(long id, int[] elementData) {
         validate();
         rsnElementGetNativeData(mContext, id, elementData);
     }
-    native void rsnElementGetSubElements(int con, int id,
-                                         int[] IDs, String[] names, int[] arraySizes);
-    synchronized void nElementGetSubElements(int id, int[] IDs, String[] names, int[] arraySizes) {
+    native void rsnElementGetSubElements(long con, long id,
+                                         long[] IDs, String[] names, int[] arraySizes);
+    synchronized void nElementGetSubElements(long id, long[] IDs, String[] names, int[] arraySizes) {
         validate();
         rsnElementGetSubElements(mContext, id, IDs, names, arraySizes);
     }
 
-    native int rsnTypeCreate(int con, int eid, int x, int y, int z, boolean mips, boolean faces, int yuv);
-    synchronized int nTypeCreate(int eid, int x, int y, int z, boolean mips, boolean faces, int yuv) {
+    native long rsnTypeCreate(long con, long eid, int x, int y, int z, boolean mips, boolean faces, int yuv);
+    synchronized long nTypeCreate(long eid, int x, int y, int z, boolean mips, boolean faces, int yuv) {
         validate();
         return rsnTypeCreate(mContext, eid, x, y, z, mips, faces, yuv);
     }
-    native void rsnTypeGetNativeData(int con, int id, int[] typeData);
-    synchronized void nTypeGetNativeData(int id, int[] typeData) {
+
+    native void rsnTypeGetNativeData(long con, long id, long[] typeData);
+    synchronized void nTypeGetNativeData(long id, long[] typeData) {
         validate();
         rsnTypeGetNativeData(mContext, id, typeData);
     }
 
-    native int  rsnAllocationCreateTyped(int con, int type, int mip, int usage, int pointer);
-    synchronized int nAllocationCreateTyped(int type, int mip, int usage, int pointer) {
+    native long  rsnAllocationCreateTyped(long con, long type, int mip, int usage, long pointer);
+    synchronized long nAllocationCreateTyped(long type, int mip, int usage, long pointer) {
         validate();
         return rsnAllocationCreateTyped(mContext, type, mip, usage, pointer);
     }
-    native int  rsnAllocationCreateFromBitmap(int con, int type, int mip, Bitmap bmp, int usage);
-    synchronized int nAllocationCreateFromBitmap(int type, int mip, Bitmap bmp, int usage) {
+    native long  rsnAllocationCreateFromBitmap(long con, long type, int mip, Bitmap bmp, int usage);
+    synchronized long nAllocationCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) {
         validate();
         return rsnAllocationCreateFromBitmap(mContext, type, mip, bmp, usage);
     }
 
-    native int  rsnAllocationCreateBitmapBackedAllocation(int con, int type, int mip, Bitmap bmp, int usage);
-    synchronized int nAllocationCreateBitmapBackedAllocation(int type, int mip, Bitmap bmp, int usage) {
+    native long  rsnAllocationCreateBitmapBackedAllocation(long con, long type, int mip, Bitmap bmp, int usage);
+    synchronized long nAllocationCreateBitmapBackedAllocation(long type, int mip, Bitmap bmp, int usage) {
         validate();
         return rsnAllocationCreateBitmapBackedAllocation(mContext, type, mip, bmp, usage);
     }
 
 
-    native int  rsnAllocationCubeCreateFromBitmap(int con, int type, int mip, Bitmap bmp, int usage);
-    synchronized int nAllocationCubeCreateFromBitmap(int type, int mip, Bitmap bmp, int usage) {
+    native long  rsnAllocationCubeCreateFromBitmap(long con, long type, int mip, Bitmap bmp, int usage);
+    synchronized long nAllocationCubeCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) {
         validate();
         return rsnAllocationCubeCreateFromBitmap(mContext, type, mip, bmp, usage);
     }
-    native int  rsnAllocationCreateBitmapRef(int con, int type, Bitmap bmp);
-    synchronized int nAllocationCreateBitmapRef(int type, Bitmap bmp) {
+    native long  rsnAllocationCreateBitmapRef(long con, long type, Bitmap bmp);
+    synchronized long nAllocationCreateBitmapRef(long type, Bitmap bmp) {
         validate();
         return rsnAllocationCreateBitmapRef(mContext, type, bmp);
     }
-    native int  rsnAllocationCreateFromAssetStream(int con, int mips, int assetStream, int usage);
-    synchronized int nAllocationCreateFromAssetStream(int mips, int assetStream, int usage) {
+    native long  rsnAllocationCreateFromAssetStream(long con, int mips, int assetStream, int usage);
+    synchronized long nAllocationCreateFromAssetStream(int mips, int assetStream, int usage) {
         validate();
         return rsnAllocationCreateFromAssetStream(mContext, mips, assetStream, usage);
     }
 
-    native void  rsnAllocationCopyToBitmap(int con, int alloc, Bitmap bmp);
-    synchronized void nAllocationCopyToBitmap(int alloc, Bitmap bmp) {
+    native void  rsnAllocationCopyToBitmap(long con, long alloc, Bitmap bmp);
+    synchronized void nAllocationCopyToBitmap(long alloc, Bitmap bmp) {
         validate();
         rsnAllocationCopyToBitmap(mContext, alloc, bmp);
     }
 
 
-    native void rsnAllocationSyncAll(int con, int alloc, int src);
-    synchronized void nAllocationSyncAll(int alloc, int src) {
+    native void rsnAllocationSyncAll(long con, long alloc, int src);
+    synchronized void nAllocationSyncAll(long alloc, int src) {
         validate();
         rsnAllocationSyncAll(mContext, alloc, src);
     }
-    native void rsnAllocationIoSend(int con, int alloc);
-    synchronized void nAllocationIoSend(int alloc) {
+
+    native void rsnAllocationSetSurface(long con, long alloc, Surface sur);
+    synchronized void nAllocationSetSurface(long alloc, Surface sur) {
+        validate();
+        rsnAllocationSetSurface(mContext, alloc, sur);
+    }
+
+    native void rsnAllocationIoSend(long con, long alloc);
+    synchronized void nAllocationIoSend(long alloc) {
         validate();
         rsnAllocationIoSend(mContext, alloc);
     }
-    native void rsnAllocationIoReceive(int con, int alloc);
-    synchronized void nAllocationIoReceive(int alloc) {
+    native void rsnAllocationIoReceive(long con, long alloc);
+    synchronized void nAllocationIoReceive(long alloc) {
         validate();
         rsnAllocationIoReceive(mContext, alloc);
     }
 
 
-    native void rsnAllocationGenerateMipmaps(int con, int alloc);
-    synchronized void nAllocationGenerateMipmaps(int alloc) {
+    native void rsnAllocationGenerateMipmaps(long con, long alloc);
+    synchronized void nAllocationGenerateMipmaps(long alloc) {
         validate();
         rsnAllocationGenerateMipmaps(mContext, alloc);
     }
-    native void  rsnAllocationCopyFromBitmap(int con, int alloc, Bitmap bmp);
-    synchronized void nAllocationCopyFromBitmap(int alloc, Bitmap bmp) {
+    native void  rsnAllocationCopyFromBitmap(long con, long alloc, Bitmap bmp);
+    synchronized void nAllocationCopyFromBitmap(long alloc, Bitmap bmp) {
         validate();
         rsnAllocationCopyFromBitmap(mContext, alloc, bmp);
     }
 
 
-    native void rsnAllocationData1D(int con, int id, int off, int mip, int count, int[] d, int sizeBytes);
-    synchronized void nAllocationData1D(int id, int off, int mip, int count, int[] d, int sizeBytes) {
+    native void rsnAllocationData1D(long con, long id, int off, int mip, int count, Object d, int sizeBytes, int dt,
+                                    int mSize, boolean usePadding);
+    synchronized void nAllocationData1D(long id, int off, int mip, int count, Object d, int sizeBytes, Element.DataType dt,
+                                        int mSize, boolean usePadding) {
         validate();
-        rsnAllocationData1D(mContext, id, off, mip, count, d, sizeBytes);
-    }
-    native void rsnAllocationData1D(int con, int id, int off, int mip, int count, short[] d, int sizeBytes);
-    synchronized void nAllocationData1D(int id, int off, int mip, int count, short[] d, int sizeBytes) {
-        validate();
-        rsnAllocationData1D(mContext, id, off, mip, count, d, sizeBytes);
-    }
-    native void rsnAllocationData1D(int con, int id, int off, int mip, int count, byte[] d, int sizeBytes);
-    synchronized void nAllocationData1D(int id, int off, int mip, int count, byte[] d, int sizeBytes) {
-        validate();
-        rsnAllocationData1D(mContext, id, off, mip, count, d, sizeBytes);
-    }
-    native void rsnAllocationData1D(int con, int id, int off, int mip, int count, float[] d, int sizeBytes);
-    synchronized void nAllocationData1D(int id, int off, int mip, int count, float[] d, int sizeBytes) {
-        validate();
-        rsnAllocationData1D(mContext, id, off, mip, count, d, sizeBytes);
+        rsnAllocationData1D(mContext, id, off, mip, count, d, sizeBytes, dt.mID, mSize, usePadding);
     }
 
-    native void rsnAllocationElementData1D(int con, int id, int xoff, int mip, int compIdx, byte[] d, int sizeBytes);
-    synchronized void nAllocationElementData1D(int id, int xoff, int mip, int compIdx, byte[] d, int sizeBytes) {
+    native void rsnAllocationElementData1D(long con,long id, int xoff, int mip, int compIdx, byte[] d, int sizeBytes);
+    synchronized void nAllocationElementData1D(long id, int xoff, int mip, int compIdx, byte[] d, int sizeBytes) {
         validate();
         rsnAllocationElementData1D(mContext, id, xoff, mip, compIdx, d, sizeBytes);
     }
+    /*
+    native void rsnAllocationElementData(long con,long id, int xoff, int yoff, int zoff, int mip, int compIdx, byte[] d, int sizeBytes);
+    synchronized void nAllocationElementData(long id, int xoff, int yoff, int zoff, int mip, int compIdx, byte[] d, int sizeBytes) {
+        validate();
+        rsnAllocationElementData(mContext, id, xoff, yoff, zoff, mip, compIdx, d, sizeBytes);
+    }
+    */
 
-    native void rsnAllocationData2D(int con,
-                                    int dstAlloc, int dstXoff, int dstYoff,
+    native void rsnAllocationData2D(long con,
+                                    long dstAlloc, int dstXoff, int dstYoff,
                                     int dstMip, int dstFace,
                                     int width, int height,
-                                    int srcAlloc, int srcXoff, int srcYoff,
+                                    long srcAlloc, int srcXoff, int srcYoff,
                                     int srcMip, int srcFace);
-    synchronized void nAllocationData2D(int dstAlloc, int dstXoff, int dstYoff,
+    synchronized void nAllocationData2D(long dstAlloc, int dstXoff, int dstYoff,
                                         int dstMip, int dstFace,
                                         int width, int height,
-                                        int srcAlloc, int srcXoff, int srcYoff,
+                                        long srcAlloc, int srcXoff, int srcYoff,
                                         int srcMip, int srcFace) {
         validate();
         rsnAllocationData2D(mContext,
@@ -420,42 +422,32 @@
                             srcMip, srcFace);
     }
 
-    native void rsnAllocationData2D(int con, int id, int xoff, int yoff, int mip, int face, int w, int h, byte[] d, int sizeBytes);
-    synchronized void nAllocationData2D(int id, int xoff, int yoff, int mip, int face, int w, int h, byte[] d, int sizeBytes) {
+    native void rsnAllocationData2D(long con, long id, int xoff, int yoff, int mip, int face,
+                                    int w, int h, Object d, int sizeBytes, int dt,
+                                    int mSize, boolean usePadding);
+    synchronized void nAllocationData2D(long id, int xoff, int yoff, int mip, int face,
+                                        int w, int h, Object d, int sizeBytes, Element.DataType dt,
+                                        int mSize, boolean usePadding) {
         validate();
-        rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, w, h, d, sizeBytes);
+        rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, w, h, d, sizeBytes, dt.mID, mSize, usePadding);
     }
-    native void rsnAllocationData2D(int con, int id, int xoff, int yoff, int mip, int face, int w, int h, short[] d, int sizeBytes);
-    synchronized void nAllocationData2D(int id, int xoff, int yoff, int mip, int face, int w, int h, short[] d, int sizeBytes) {
-        validate();
-        rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, w, h, d, sizeBytes);
-    }
-    native void rsnAllocationData2D(int con, int id, int xoff, int yoff, int mip, int face, int w, int h, int[] d, int sizeBytes);
-    synchronized void nAllocationData2D(int id, int xoff, int yoff, int mip, int face, int w, int h, int[] d, int sizeBytes) {
-        validate();
-        rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, w, h, d, sizeBytes);
-    }
-    native void rsnAllocationData2D(int con, int id, int xoff, int yoff, int mip, int face, int w, int h, float[] d, int sizeBytes);
-    synchronized void nAllocationData2D(int id, int xoff, int yoff, int mip, int face, int w, int h, float[] d, int sizeBytes) {
-        validate();
-        rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, w, h, d, sizeBytes);
-    }
-    native void rsnAllocationData2D(int con, int id, int xoff, int yoff, int mip, int face, Bitmap b);
-    synchronized void nAllocationData2D(int id, int xoff, int yoff, int mip, int face, Bitmap b) {
+
+    native void rsnAllocationData2D(long con, long id, int xoff, int yoff, int mip, int face, Bitmap b);
+    synchronized void nAllocationData2D(long id, int xoff, int yoff, int mip, int face, Bitmap b) {
         validate();
         rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, b);
     }
 
-    native void rsnAllocationData3D(int con,
-                                    int dstAlloc, int dstXoff, int dstYoff, int dstZoff,
+    native void rsnAllocationData3D(long con,
+                                    long dstAlloc, int dstXoff, int dstYoff, int dstZoff,
                                     int dstMip,
                                     int width, int height, int depth,
-                                    int srcAlloc, int srcXoff, int srcYoff, int srcZoff,
+                                    long srcAlloc, int srcXoff, int srcYoff, int srcZoff,
                                     int srcMip);
-    synchronized void nAllocationData3D(int dstAlloc, int dstXoff, int dstYoff, int dstZoff,
+    synchronized void nAllocationData3D(long dstAlloc, int dstXoff, int dstYoff, int dstZoff,
                                         int dstMip,
                                         int width, int height, int depth,
-                                        int srcAlloc, int srcXoff, int srcYoff, int srcZoff,
+                                        long srcAlloc, int srcXoff, int srcYoff, int srcZoff,
                                         int srcMip) {
         validate();
         rsnAllocationData3D(mContext,
@@ -464,210 +456,368 @@
                             srcAlloc, srcXoff, srcYoff, srcZoff, srcMip);
     }
 
-    native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, byte[] d, int sizeBytes);
-    synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, byte[] d, int sizeBytes) {
+
+    native void rsnAllocationData3D(long con, long id, int xoff, int yoff, int zoff, int mip,
+                                    int w, int h, int depth, Object d, int sizeBytes, int dt,
+                                    int mSize, boolean usePadding);
+    synchronized void nAllocationData3D(long id, int xoff, int yoff, int zoff, int mip,
+                                        int w, int h, int depth, Object d, int sizeBytes, Element.DataType dt,
+                                        int mSize, boolean usePadding) {
         validate();
-        rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
-    }
-    native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, short[] d, int sizeBytes);
-    synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, short[] d, int sizeBytes) {
-        validate();
-        rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
-    }
-    native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, int[] d, int sizeBytes);
-    synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, int[] d, int sizeBytes) {
-        validate();
-        rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
-    }
-    native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, float[] d, int sizeBytes);
-    synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, float[] d, int sizeBytes) {
-        validate();
-        rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+        rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes,
+                            dt.mID, mSize, usePadding);
     }
 
+    native void rsnAllocationRead(long con, long id, Object d, int dt, int mSize, boolean usePadding);
+    synchronized void nAllocationRead(long id, Object d, Element.DataType dt, int mSize, boolean usePadding) {
+        validate();
+        rsnAllocationRead(mContext, id, d, dt.mID, mSize, usePadding);
+    }
 
-    native void rsnAllocationRead(int con, int id, byte[] d);
-    synchronized void nAllocationRead(int id, byte[] d) {
+    native void rsnAllocationRead1D(long con, long id, int off, int mip, int count, Object d,
+                                    int sizeBytes, int dt, int mSize, boolean usePadding);
+    synchronized void nAllocationRead1D(long id, int off, int mip, int count, Object d,
+                                        int sizeBytes, Element.DataType dt, int mSize, boolean usePadding) {
         validate();
-        rsnAllocationRead(mContext, id, d);
+        rsnAllocationRead1D(mContext, id, off, mip, count, d, sizeBytes, dt.mID, mSize, usePadding);
     }
-    native void rsnAllocationRead(int con, int id, short[] d);
-    synchronized void nAllocationRead(int id, short[] d) {
+    
+    /*
+    native void rsnAllocationElementRead(long con,long id, int xoff, int yoff, int zoff,
+                                         int mip, int compIdx, byte[] d, int sizeBytes);
+    synchronized void nAllocationElementRead(long id, int xoff, int yoff, int zoff,
+                                             int mip, int compIdx, byte[] d, int sizeBytes) {
         validate();
-        rsnAllocationRead(mContext, id, d);
+        rsnAllocationElementRead(mContext, id, xoff, yoff, zoff, mip, compIdx, d, sizeBytes);
     }
-    native void rsnAllocationRead(int con, int id, int[] d);
-    synchronized void nAllocationRead(int id, int[] d) {
+    */
+
+    native void rsnAllocationRead2D(long con, long id, int xoff, int yoff, int mip, int face,
+                                    int w, int h, Object d, int sizeBytes, int dt,
+                                    int mSize, boolean usePadding);
+    synchronized void nAllocationRead2D(long id, int xoff, int yoff, int mip, int face,
+                                        int w, int h, Object d, int sizeBytes, Element.DataType dt,
+                                        int mSize, boolean usePadding) {
         validate();
-        rsnAllocationRead(mContext, id, d);
+        rsnAllocationRead2D(mContext, id, xoff, yoff, mip, face, w, h, d, sizeBytes, dt.mID, mSize, usePadding);
     }
-    native void rsnAllocationRead(int con, int id, float[] d);
-    synchronized void nAllocationRead(int id, float[] d) {
+
+    /*
+    native void rsnAllocationRead3D(long con, long id, int xoff, int yoff, int zoff, int mip,
+                                    int w, int h, int depth, Object d, int sizeBytes, int dt,
+                                    int mSize, boolean usePadding);
+    synchronized void nAllocationRead3D(long id, int xoff, int yoff, int zoff, int mip,
+                                        int w, int h, int depth, Object d, int sizeBytes, Element.DataType dt,
+                                        int mSize, boolean usePadding) {
         validate();
-        rsnAllocationRead(mContext, id, d);
+        rsnAllocationRead3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes, dt.mID, mSize, usePadding);
     }
-    native int  rsnAllocationGetType(int con, int id);
-    synchronized int nAllocationGetType(int id) {
+    */
+
+    native long  rsnAllocationGetType(long con, long id);
+    synchronized long nAllocationGetType(long id) {
         validate();
         return rsnAllocationGetType(mContext, id);
     }
 
-    native void rsnAllocationResize1D(int con, int id, int dimX);
-    synchronized void nAllocationResize1D(int id, int dimX) {
+    native void rsnAllocationResize1D(long con, long id, int dimX);
+    synchronized void nAllocationResize1D(long id, int dimX) {
         validate();
         rsnAllocationResize1D(mContext, id, dimX);
     }
-    native void rsnAllocationResize2D(int con, int id, int dimX, int dimY);
-    synchronized void nAllocationResize2D(int id, int dimX, int dimY) {
+    native void rsnAllocationResize2D(long con, long id, int dimX, int dimY);
+    synchronized void nAllocationResize2D(long id, int dimX, int dimY) {
         validate();
         rsnAllocationResize2D(mContext, id, dimX, dimY);
     }
 
-    native void rsnScriptBindAllocation(int con, int script, int alloc, int slot);
-    synchronized void nScriptBindAllocation(int script, int alloc, int slot) {
+    native void rsnScriptBindAllocation(long con, long script, long alloc, int slot, boolean mUseInc);
+    synchronized void nScriptBindAllocation(long script, long alloc, int slot, boolean mUseInc) {
         validate();
-        rsnScriptBindAllocation(mContext, script, alloc, slot);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptBindAllocation(curCon, script, alloc, slot, mUseInc);
     }
-    native void rsnScriptSetTimeZone(int con, int script, byte[] timeZone);
-    synchronized void nScriptSetTimeZone(int script, byte[] timeZone) {
+    native void rsnScriptSetTimeZone(long con, long script, byte[] timeZone, boolean mUseInc);
+    synchronized void nScriptSetTimeZone(long script, byte[] timeZone, boolean mUseInc) {
         validate();
-        rsnScriptSetTimeZone(mContext, script, timeZone);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptSetTimeZone(curCon, script, timeZone, mUseInc);
     }
-    native void rsnScriptInvoke(int con, int id, int slot);
-    synchronized void nScriptInvoke(int id, int slot) {
+    native void rsnScriptInvoke(long con, long id, int slot, boolean mUseInc);
+    synchronized void nScriptInvoke(long id, int slot, boolean mUseInc) {
         validate();
-        rsnScriptInvoke(mContext, id, slot);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptInvoke(curCon, id, slot, mUseInc);
     }
-    native void rsnScriptForEach(int con, int id, int slot, int ain, int aout, byte[] params);
-    native void rsnScriptForEach(int con, int id, int slot, int ain, int aout);
-    native void rsnScriptForEachClipped(int con, int id, int slot, int ain, int aout, byte[] params,
-                                        int xstart, int xend, int ystart, int yend, int zstart, int zend);
-    native void rsnScriptForEachClipped(int con, int id, int slot, int ain, int aout,
-                                        int xstart, int xend, int ystart, int yend, int zstart, int zend);
-    synchronized void nScriptForEach(int id, int slot, int ain, int aout, byte[] params) {
+    native void rsnScriptForEach(long con, long incCon, long id, int slot, long ain, long aout, byte[] params, boolean mUseInc);
+    native void rsnScriptForEach(long con, long incCon, long id, int slot, long ain, long aout, boolean mUseInc);
+    native void rsnScriptForEachClipped(long con, long incCon, long id, int slot, long ain, long aout, byte[] params,
+                                        int xstart, int xend, int ystart, int yend, int zstart, int zend, boolean mUseInc);
+    native void rsnScriptForEachClipped(long con, long incCon, long id, int slot, long ain, long aout,
+                                        int xstart, int xend, int ystart, int yend, int zstart, int zend, boolean mUseInc);
+    synchronized void nScriptForEach(long id, int slot, long ain, long aout, byte[] params, boolean mUseInc) {
         validate();
         if (params == null) {
-            rsnScriptForEach(mContext, id, slot, ain, aout);
+            rsnScriptForEach(mContext, mIncCon, id, slot, ain, aout, mUseInc);
         } else {
-            rsnScriptForEach(mContext, id, slot, ain, aout, params);
+            rsnScriptForEach(mContext, mIncCon, id, slot, ain, aout, params, mUseInc);
         }
     }
 
-    synchronized void nScriptForEachClipped(int id, int slot, int ain, int aout, byte[] params,
-                                            int xstart, int xend, int ystart, int yend, int zstart, int zend) {
+    synchronized void nScriptForEachClipped(long id, int slot, long ain, long aout, byte[] params,
+                                            int xstart, int xend, int ystart, int yend, int zstart, int zend, boolean mUseInc) {
         validate();
         if (params == null) {
-            rsnScriptForEachClipped(mContext, id, slot, ain, aout, xstart, xend, ystart, yend, zstart, zend);
+            rsnScriptForEachClipped(mContext, mIncCon, id, slot, ain, aout, xstart, xend, ystart, yend, zstart, zend, mUseInc);
         } else {
-            rsnScriptForEachClipped(mContext, id, slot, ain, aout, params, xstart, xend, ystart, yend, zstart, zend);
+            rsnScriptForEachClipped(mContext, mIncCon, id, slot, ain, aout, params, xstart, xend, ystart, yend, zstart, zend, mUseInc);
         }
     }
 
-    native void rsnScriptInvokeV(int con, int id, int slot, byte[] params);
-    synchronized void nScriptInvokeV(int id, int slot, byte[] params) {
+    native void rsnScriptInvokeV(long con, long id, int slot, byte[] params, boolean mUseInc);
+    synchronized void nScriptInvokeV(long id, int slot, byte[] params, boolean mUseInc) {
         validate();
-        rsnScriptInvokeV(mContext, id, slot, params);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptInvokeV(curCon, id, slot, params, mUseInc);
     }
-    native void rsnScriptSetVarI(int con, int id, int slot, int val);
-    synchronized void nScriptSetVarI(int id, int slot, int val) {
+    native void rsnScriptSetVarI(long con, long id, int slot, int val, boolean mUseInc);
+    synchronized void nScriptSetVarI(long id, int slot, int val, boolean mUseInc) {
         validate();
-        rsnScriptSetVarI(mContext, id, slot, val);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptSetVarI(curCon, id, slot, val, mUseInc);
     }
-    native void rsnScriptSetVarJ(int con, int id, int slot, long val);
-    synchronized void nScriptSetVarJ(int id, int slot, long val) {
+    native void rsnScriptSetVarJ(long con, long id, int slot, long val, boolean mUseInc);
+    synchronized void nScriptSetVarJ(long id, int slot, long val, boolean mUseInc) {
         validate();
-        rsnScriptSetVarJ(mContext, id, slot, val);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptSetVarJ(curCon, id, slot, val, mUseInc);
     }
-    native void rsnScriptSetVarF(int con, int id, int slot, float val);
-    synchronized void nScriptSetVarF(int id, int slot, float val) {
+    native void rsnScriptSetVarF(long con, long id, int slot, float val, boolean mUseInc);
+    synchronized void nScriptSetVarF(long id, int slot, float val, boolean mUseInc) {
         validate();
-        rsnScriptSetVarF(mContext, id, slot, val);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptSetVarF(curCon, id, slot, val, mUseInc);
     }
-    native void rsnScriptSetVarD(int con, int id, int slot, double val);
-    synchronized void nScriptSetVarD(int id, int slot, double val) {
+    native void rsnScriptSetVarD(long con, long id, int slot, double val, boolean mUseInc);
+    synchronized void nScriptSetVarD(long id, int slot, double val, boolean mUseInc) {
         validate();
-        rsnScriptSetVarD(mContext, id, slot, val);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptSetVarD(curCon, id, slot, val, mUseInc);
     }
-    native void rsnScriptSetVarV(int con, int id, int slot, byte[] val);
-    synchronized void nScriptSetVarV(int id, int slot, byte[] val) {
+    native void rsnScriptSetVarV(long con, long id, int slot, byte[] val, boolean mUseInc);
+    synchronized void nScriptSetVarV(long id, int slot, byte[] val, boolean mUseInc) {
         validate();
-        rsnScriptSetVarV(mContext, id, slot, val);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptSetVarV(curCon, id, slot, val, mUseInc);
     }
-    native void rsnScriptSetVarVE(int con, int id, int slot, byte[] val,
-                                  int e, int[] dims);
-    synchronized void nScriptSetVarVE(int id, int slot, byte[] val,
-                                      int e, int[] dims) {
+    native void rsnScriptSetVarVE(long con, long id, int slot, byte[] val,
+                                  long e, int[] dims, boolean mUseInc);
+    synchronized void nScriptSetVarVE(long id, int slot, byte[] val,
+                                      long e, int[] dims, boolean mUseInc) {
         validate();
-        rsnScriptSetVarVE(mContext, id, slot, val, e, dims);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptSetVarVE(curCon, id, slot, val, e, dims, mUseInc);
     }
-    native void rsnScriptSetVarObj(int con, int id, int slot, int val);
-    synchronized void nScriptSetVarObj(int id, int slot, int val) {
+    native void rsnScriptSetVarObj(long con, long id, int slot, long val, boolean mUseInc);
+    synchronized void nScriptSetVarObj(long id, int slot, long val, boolean mUseInc) {
         validate();
-        rsnScriptSetVarObj(mContext, id, slot, val);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        rsnScriptSetVarObj(curCon, id, slot, val, mUseInc);
     }
 
-    native int  rsnScriptCCreate(int con, String resName, String cacheDir,
+    native long  rsnScriptCCreate(long con, String resName, String cacheDir,
                                  byte[] script, int length);
-    synchronized int nScriptCCreate(String resName, String cacheDir, byte[] script, int length) {
+    synchronized long nScriptCCreate(String resName, String cacheDir, byte[] script, int length) {
         validate();
         return rsnScriptCCreate(mContext, resName, cacheDir, script, length);
     }
 
-    native int  rsnScriptIntrinsicCreate(int con, int id, int eid);
-    synchronized int nScriptIntrinsicCreate(int id, int eid) {
+    native long  rsnScriptIntrinsicCreate(long con, int id, long eid, boolean mUseInc);
+    synchronized long nScriptIntrinsicCreate(int id, long eid, boolean mUseInc) {
         validate();
-        return rsnScriptIntrinsicCreate(mContext, id, eid);
+        if (mUseInc) {
+            if (!mIncLoaded) {
+                try {
+                    System.loadLibrary("RSSupport");
+                } catch (UnsatisfiedLinkError e) {
+                    Log.e(LOG_TAG, "Error loading RS Compat library for Incremental Intrinsic Support: " + e);
+                    throw new RSRuntimeException("Error loading RS Compat library for Incremental Intrinsic Support: " + e);
+                }
+                if (!nIncLoadSO()) {
+                    throw new RSRuntimeException("Error loading libRSSupport library for Incremental Intrinsic Support");
+                }
+                mIncLoaded = true;
+            }
+            if (mIncDev == 0) {
+                mIncDev = nIncDeviceCreate();
+            }
+            if (mIncCon == 0) {
+                //Create a dummy compat context (synchronous).
+                mIncCon = nIncContextCreate(mIncDev, 0, 0, 0);
+            }
+            return rsnScriptIntrinsicCreate(mIncCon, id, eid, mUseInc);
+        } else {
+            return rsnScriptIntrinsicCreate(mContext, id, eid, mUseInc);
+        }
     }
 
-    native int  rsnScriptKernelIDCreate(int con, int sid, int slot, int sig);
-    synchronized int nScriptKernelIDCreate(int sid, int slot, int sig) {
+    native long  rsnScriptKernelIDCreate(long con, long sid, int slot, int sig, boolean mUseInc);
+    synchronized long nScriptKernelIDCreate(long sid, int slot, int sig, boolean mUseInc) {
         validate();
-        return rsnScriptKernelIDCreate(mContext, sid, slot, sig);
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        return rsnScriptKernelIDCreate(curCon, sid, slot, sig, mUseInc);
     }
 
-    native int  rsnScriptFieldIDCreate(int con, int sid, int slot);
-    synchronized int nScriptFieldIDCreate(int sid, int slot) {
+    native long  rsnScriptInvokeIDCreate(long con, long sid, int slot);
+    synchronized long nScriptInvokeIDCreate(long sid, int slot) {
         validate();
-        return rsnScriptFieldIDCreate(mContext, sid, slot);
+        return rsnScriptInvokeIDCreate(mContext, sid, slot);
     }
 
-    native int  rsnScriptGroupCreate(int con, int[] kernels, int[] src, int[] dstk, int[] dstf, int[] types);
-    synchronized int nScriptGroupCreate(int[] kernels, int[] src, int[] dstk, int[] dstf, int[] types) {
+    native long  rsnScriptFieldIDCreate(long con, long sid, int slot, boolean mUseInc);
+    synchronized long nScriptFieldIDCreate(long sid, int slot, boolean mUseInc) {
+        validate();
+        long curCon = mContext;
+        if (mUseInc) {
+            curCon = mIncCon;
+        }
+        return rsnScriptFieldIDCreate(curCon, sid, slot, mUseInc);
+    }
+
+    native long  rsnScriptGroupCreate(long con, long[] kernels, long[] src, long[] dstk, long[] dstf, long[] types);
+    synchronized long nScriptGroupCreate(long[] kernels, long[] src, long[] dstk, long[] dstf, long[] types) {
         validate();
         return rsnScriptGroupCreate(mContext, kernels, src, dstk, dstf, types);
     }
 
-    native void rsnScriptGroupSetInput(int con, int group, int kernel, int alloc);
-    synchronized void nScriptGroupSetInput(int group, int kernel, int alloc) {
+    native void rsnScriptGroupSetInput(long con, long group, long kernel, long alloc);
+    synchronized void nScriptGroupSetInput(long group, long kernel, long alloc) {
         validate();
         rsnScriptGroupSetInput(mContext, group, kernel, alloc);
     }
 
-    native void rsnScriptGroupSetOutput(int con, int group, int kernel, int alloc);
-    synchronized void nScriptGroupSetOutput(int group, int kernel, int alloc) {
+    native void rsnScriptGroupSetOutput(long con, long group, long kernel, long alloc);
+    synchronized void nScriptGroupSetOutput(long group, long kernel, long alloc) {
         validate();
         rsnScriptGroupSetOutput(mContext, group, kernel, alloc);
     }
 
-    native void rsnScriptGroupExecute(int con, int group);
-    synchronized void nScriptGroupExecute(int group) {
+    native void rsnScriptGroupExecute(long con, long group);
+    synchronized void nScriptGroupExecute(long group) {
         validate();
         rsnScriptGroupExecute(mContext, group);
     }
 
-    native int  rsnSamplerCreate(int con, int magFilter, int minFilter,
+    native long  rsnSamplerCreate(long con, int magFilter, int minFilter,
                                  int wrapS, int wrapT, int wrapR, float aniso);
-    synchronized int nSamplerCreate(int magFilter, int minFilter,
+    synchronized long nSamplerCreate(int magFilter, int minFilter,
                                  int wrapS, int wrapT, int wrapR, float aniso) {
         validate();
         return rsnSamplerCreate(mContext, magFilter, minFilter, wrapS, wrapT, wrapR, aniso);
     }
 
+// Additional Entry points For inc libRSSupport
 
+    native boolean nIncLoadSO();
+    native long nIncDeviceCreate();
+    native void nIncDeviceDestroy(long dev);
+    // Methods below are wrapped to protect the non-threadsafe
+    // lockless fifo.
+    native long  rsnIncContextCreate(long dev, int ver, int sdkVer, int contextType);
+    synchronized long nIncContextCreate(long dev, int ver, int sdkVer, int contextType) {
+        return rsnIncContextCreate(dev, ver, sdkVer, contextType);
+    }
+    native void rsnIncContextDestroy(long con);
+    synchronized void nIncContextDestroy() {
+        validate();
 
+        // take teardown lock
+        // teardown lock can only be taken when no objects are being destroyed
+        ReentrantReadWriteLock.WriteLock wlock = mRWLock.writeLock();
+        wlock.lock();
 
-    int     mDev;
-    int     mContext;
+        long curCon = mIncCon;
+        // context is considered dead as of this point
+        mIncCon = 0;
+
+        wlock.unlock();
+        rsnIncContextDestroy(curCon);
+    }
+
+    native void rsnIncContextFinish(long con);
+    synchronized void nIncContextFinish() {
+        validate();
+        rsnIncContextFinish(mIncCon);
+    }
+
+    native void rsnIncObjDestroy(long con, long id);
+    void nIncObjDestroy(long id) {
+        // There is a race condition here.  The calling code may be run
+        // by the gc while teardown is occuring.  This protects againts
+        // deleting dead objects.
+        if (mIncCon != 0) {
+            rsnIncObjDestroy(mIncCon, id);
+        }
+    }
+    native long  rsnIncElementCreate(long con, long type, int kind, boolean norm, int vecSize);
+    synchronized long nIncElementCreate(long type, int kind, boolean norm, int vecSize) {
+        validate();
+        return rsnIncElementCreate(mIncCon, type, kind, norm, vecSize);
+    }
+    native long rsnIncTypeCreate(long con, long eid, int x, int y, int z, boolean mips, boolean faces, int yuv);
+    synchronized long nIncTypeCreate(long eid, int x, int y, int z, boolean mips, boolean faces, int yuv) {
+        validate();
+        return rsnIncTypeCreate(mIncCon, eid, x, y, z, mips, faces, yuv);
+    }
+    native long  rsnIncAllocationCreateTyped(long con, long incCon, long alloc, long type);
+    synchronized long nIncAllocationCreateTyped(long alloc, long type) {
+        validate();
+        return rsnIncAllocationCreateTyped(mContext, mIncCon, alloc, type);
+    }
+
+    long     mDev;
+    long     mContext;
+    //Dummy device & context for Inc Support Lib
+    long     mIncDev;
+    long     mIncCon;
+    //indicator of whether inc support lib has been loaded or not.
+    boolean  mIncLoaded;
     ReentrantReadWriteLock mRWLock;
     @SuppressWarnings({"FieldCanBeLocal"})
     MessageThread mMessageThread;
@@ -784,10 +934,6 @@
 
     public void setMessageHandler(RSMessageHandler msg) {
         mMessageCallback = msg;
-        if (isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker) this;
-            rst.setMessageHandler(msg);
-        }
     }
     public RSMessageHandler getMessageHandler() {
         return mMessageCallback;
@@ -830,10 +976,6 @@
 
     public void setErrorHandler(RSErrorHandler msg) {
         mErrorCallback = msg;
-        if (isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker) this;
-            rst.setErrorHandler(msg);
-        }
     }
     public RSErrorHandler getErrorHandler() {
         return mErrorCallback;
@@ -860,7 +1002,12 @@
         }
     }
 
-
+    /**
+     * check if IO support lib is available.
+     */
+    boolean usingIO() {
+        return useIOlib;
+    }
     /**
      * Change the priority of the worker threads for this context.
      *
@@ -880,8 +1027,8 @@
         static final int RS_MESSAGE_TO_CLIENT_EXCEPTION = 1;
         static final int RS_MESSAGE_TO_CLIENT_RESIZE = 2;
         static final int RS_MESSAGE_TO_CLIENT_ERROR = 3;
-        static final int RS_MESSAGE_TO_CLIENT_USER = 4;
 
+        static final int RS_MESSAGE_TO_CLIENT_USER = 4;
         static final int RS_ERROR_FATAL_UNKNOWN = 0x1000;
 
         MessageThread(RenderScript rs) {
@@ -956,7 +1103,11 @@
     RenderScript(Context ctx) {
         if (ctx != null) {
             mApplicationContext = ctx.getApplicationContext();
+            mNativeLibDir = mApplicationContext.getApplicationInfo().nativeLibraryDir;
         }
+        mIncDev = 0;
+        mIncCon = 0;
+        mIncLoaded = false;
         mRWLock = new ReentrantReadWriteLock();
     }
 
@@ -991,11 +1142,7 @@
         } else if (sSdkVersion != sdkVersion) {
             throw new RSRuntimeException("Can't have two contexts with different SDK versions in support lib");
         }
-
-        if (setupThunk(sSdkVersion, ctx)) {
-            android.util.Log.v(LOG_TAG, "RS native mode");
-            return RenderScriptThunker.create(ctx, sSdkVersion);
-        }
+        useNative = setupNative(sSdkVersion, ctx);
         synchronized(lock) {
             if (sInitialized == false) {
                 try {
@@ -1010,9 +1157,9 @@
                     sUseGCHooks = false;
                 }
                 try {
-                    System.loadLibrary("RSSupport");
                     System.loadLibrary("rsjni");
                     sInitialized = true;
+                    sPointerSize = rsnSystemGetPointerSize();
                 } catch (UnsatisfiedLinkError e) {
                     Log.e(LOG_TAG, "Error loading RS jni library: " + e);
                     throw new RSRuntimeException("Error loading RS jni library: " + e);
@@ -1020,9 +1167,45 @@
             }
         }
 
-        android.util.Log.v(LOG_TAG, "RS compat mode");
+        if (useNative) {
+            android.util.Log.v(LOG_TAG, "RS native mode");
+        } else {
+            android.util.Log.v(LOG_TAG, "RS compat mode");
+        }
+
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            useIOlib = true;
+        }
+        if (!rs.nLoadSO(useNative)) {
+            if (useNative) {
+                android.util.Log.v(LOG_TAG, "Unable to load libRS.so, falling back to compat mode");
+                useNative = false;
+            }
+            try {
+                System.loadLibrary("RSSupport");
+            } catch (UnsatisfiedLinkError e) {
+                Log.e(LOG_TAG, "Error loading RS Compat library: " + e);
+                throw new RSRuntimeException("Error loading RS Compat library: " + e);
+            }
+            if (!rs.nLoadSO(false)) {
+                throw new RSRuntimeException("Error loading libRSSupport library");
+            }
+        }
+
+        if (useIOlib) {
+            try {
+                System.loadLibrary("RSSupportIO");
+            } catch (UnsatisfiedLinkError e) {
+                useIOlib = false;
+            }
+            if (!useIOlib || !rs.nLoadIOSO()) {
+                android.util.Log.v(LOG_TAG, "Unable to load libRSSupportIO.so, USAGE_IO not supported");
+                useIOlib = false;
+            }
+        }
+
         rs.mDev = rs.nDeviceCreate();
-        rs.mContext = rs.nContextCreate(rs.mDev, 0, sdkVersion, ct.mID);
+        rs.mContext = rs.nContextCreate(rs.mDev, 0, sdkVersion, ct.mID, rs.mNativeLibDir);
         if (rs.mContext == 0) {
             throw new RSDriverException("Failed to create RS context.");
         }
@@ -1083,6 +1266,11 @@
     public void destroy() {
         validate();
         nContextFinish();
+        if (mIncCon != 0) {
+            nIncContextFinish();
+            nIncContextDestroy();
+            mIncCon = 0;
+        }
         nContextDeinitToClient(mContext);
         mMessageThread.mRun = false;
         try {
@@ -1092,6 +1280,10 @@
 
         nContextDestroy();
         nDeviceDestroy(mDev);
+        if (mIncDev != 0) {
+            nIncDeviceDestroy(mIncDev);
+            mIncDev = 0;
+        }
         mDev = 0;
     }
 
@@ -1099,7 +1291,7 @@
         return mContext != 0;
     }
 
-    int safeID(BaseObj o) {
+    long safeID(BaseObj o) {
         if(o != null) {
             return o.getID(this);
         }
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScriptThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScriptThunker.java
deleted file mode 100644
index bb6bf73..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScriptThunker.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.AssetManager;
-import android.os.Process;
-import android.util.Log;
-import android.view.Surface;
-
-
-
-class RenderScriptThunker extends RenderScript {
-    android.renderscript.RenderScript mN;
-
-    void validate() {
-        if (mN == null) {
-            throw new RSInvalidStateException("Calling RS with no Context active.");
-        }
-    }
-
-    public void setPriority(Priority p) {
-        try {
-            if (p == Priority.LOW) mN.setPriority(android.renderscript.RenderScript.Priority.LOW);
-            if (p == Priority.NORMAL) mN.setPriority(android.renderscript.RenderScript.Priority.NORMAL);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    RenderScriptThunker(Context ctx) {
-        super(ctx);
-        isNative = true;
-    }
-
-    public static RenderScript create(Context ctx, int sdkVersion) {
-        try {
-            RenderScriptThunker rs = new RenderScriptThunker(ctx);
-            Class<?> javaRS = Class.forName("android.renderscript.RenderScript");
-            Class[] signature = {Context.class, Integer.TYPE};
-            Object[] args = {ctx, new Integer(sdkVersion)};
-            Method create = javaRS.getDeclaredMethod("create", signature);
-            rs.mN = (android.renderscript.RenderScript)create.invoke(null, args);
-            return rs;
-        }
-        catch(android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        } catch (Exception e) {
-            throw new RSRuntimeException("Failure to create platform RenderScript context");
-        }
-    }
-
-    public void contextDump() {
-        try {
-            mN.contextDump();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void finish() {
-        try {
-            mN.finish();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void destroy() {
-        try {
-            mN.destroy();
-            mN = null;
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-
-    }
-
-    public void setMessageHandler(RSMessageHandler msg) {
-        mMessageCallback = msg;
-        try {
-            android.renderscript.RenderScript.RSMessageHandler handler =
-                new android.renderscript.RenderScript.RSMessageHandler() {
-                    public void run() {
-                        mMessageCallback.mData = mData;
-                        mMessageCallback.mID = mID;
-                        mMessageCallback.mLength = mLength;
-                        mMessageCallback.run();
-                    }
-                };
-            mN.setMessageHandler(handler);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setErrorHandler(RSErrorHandler msg) {
-        mErrorCallback = msg;
-        try {
-            android.renderscript.RenderScript.RSErrorHandler handler =
-                new android.renderscript.RenderScript.RSErrorHandler() {
-                    public void run() {
-                        mErrorCallback.mErrorMessage = mErrorMessage;
-                        mErrorCallback.mErrorNum = mErrorNum;
-                        mErrorCallback.run();
-                    }
-                };
-            mN.setErrorHandler(handler);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-
-    boolean equals(Object obj1, Object obj2) {
-        if (obj2 instanceof android.support.v8.renderscript.BaseObj) {
-            return ((android.renderscript.BaseObj)obj1).equals(((android.support.v8.renderscript.BaseObj)obj2).getNObj());
-        }
-        return false;
-    }
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Sampler.java b/v8/renderscript/java/src/android/support/v8/renderscript/Sampler.java
index 7234c12..7119e8c 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Sampler.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/Sampler.java
@@ -62,7 +62,7 @@
     Value mWrapR;
     float mAniso;
 
-    Sampler(int id, RenderScript rs) {
+    Sampler(long id, RenderScript rs) {
         super(id, rs);
     }
 
@@ -328,18 +328,8 @@
         }
 
         public Sampler create() {
-            if (mRS.isNative) {
-                RenderScriptThunker rst = (RenderScriptThunker)mRS;
-                SamplerThunker.Builder b = new SamplerThunker.Builder(rst);
-                b.setMinification(mMin);
-                b.setMagnification(mMag);
-                b.setWrapS(mWrapS);
-                b.setWrapT(mWrapT);
-                b.setAnisotropy(mAniso);
-                return b.create();
-            }
             mRS.validate();
-            int id = mRS.nSamplerCreate(mMag.mID, mMin.mID,
+            long id = mRS.nSamplerCreate(mMag.mID, mMin.mID,
                                         mWrapS.mID, mWrapT.mID, mWrapR.mID, mAniso);
             Sampler sampler = new Sampler(id, mRS);
             sampler.mMin = mMin;
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/SamplerThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/SamplerThunker.java
deleted file mode 100644
index eb14c7f..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/SamplerThunker.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-import android.os.Bundle;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-/**
- *
- **/
-class SamplerThunker extends Sampler {
-    android.renderscript.Sampler mN;
-
-    protected SamplerThunker(int id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    android.renderscript.BaseObj getNObj() {
-        return mN;
-    }
-
-    static android.renderscript.Sampler.Value convertValue (Value v) {
-        switch (v) {
-        case NEAREST:
-            return android.renderscript.Sampler.Value.NEAREST;
-        case LINEAR:
-            return android.renderscript.Sampler.Value.LINEAR;
-        case LINEAR_MIP_LINEAR:
-            return android.renderscript.Sampler.Value.LINEAR_MIP_LINEAR;
-        case LINEAR_MIP_NEAREST:
-            return android.renderscript.Sampler.Value.LINEAR_MIP_NEAREST;
-        case WRAP:
-            return android.renderscript.Sampler.Value.WRAP;
-        case CLAMP:
-            return android.renderscript.Sampler.Value.CLAMP;
-        case MIRRORED_REPEAT:
-            return android.renderscript.Sampler.Value.MIRRORED_REPEAT;
-        }
-        return null;
-    }
-
-    /**
-     * Builder for creating non-standard samplers.  Useful if mix and match of
-     * wrap modes is necesary or if anisotropic filtering is desired.
-     *
-     */
-    public static class Builder {
-        RenderScriptThunker mRS;
-        Value mMin;
-        Value mMag;
-        Value mWrapS;
-        Value mWrapT;
-        Value mWrapR;
-        float mAniso;
-
-        public Builder(RenderScriptThunker rs) {
-            mRS = rs;
-            mMin = Value.NEAREST;
-            mMag = Value.NEAREST;
-            mWrapS = Value.WRAP;
-            mWrapT = Value.WRAP;
-            mWrapR = Value.WRAP;
-        }
-
-        public void setMinification(Value v) {
-            if (v == Value.NEAREST ||
-                v == Value.LINEAR ||
-                v == Value.LINEAR_MIP_LINEAR ||
-                v == Value.LINEAR_MIP_NEAREST) {
-                mMin = v;
-            } else {
-                throw new IllegalArgumentException("Invalid value");
-            }
-        }
-
-        public void setMagnification(Value v) {
-            if (v == Value.NEAREST || v == Value.LINEAR) {
-                mMag = v;
-            } else {
-                throw new IllegalArgumentException("Invalid value");
-            }
-        }
-
-        public void setWrapS(Value v) {
-            if (v == Value.WRAP || v == Value.CLAMP || v == Value.MIRRORED_REPEAT) {
-                mWrapS = v;
-            } else {
-                throw new IllegalArgumentException("Invalid value");
-            }
-        }
-
-        public void setWrapT(Value v) {
-            if (v == Value.WRAP || v == Value.CLAMP || v == Value.MIRRORED_REPEAT) {
-                mWrapT = v;
-            } else {
-                throw new IllegalArgumentException("Invalid value");
-            }
-        }
-
-        public void setAnisotropy(float v) {
-            if(v >= 0.0f) {
-                mAniso = v;
-            } else {
-                throw new IllegalArgumentException("Invalid value");
-            }
-        }
-
-        public Sampler create() {
-            mRS.validate();
-            try {
-                android.renderscript.Sampler.Builder b = new android.renderscript.Sampler.Builder(mRS.mN);
-                b.setMinification(convertValue(mMin));
-                b.setMagnification(convertValue(mMag));
-                b.setWrapS(convertValue(mWrapS));
-                b.setWrapT(convertValue(mWrapT));
-                b.setAnisotropy(mAniso);
-                android.renderscript.Sampler s = b.create();
-
-                SamplerThunker sampler = new SamplerThunker(0, mRS);
-                sampler.mMin = mMin;
-                sampler.mMag = mMag;
-                sampler.mWrapS = mWrapS;
-                sampler.mWrapT = mWrapT;
-                sampler.mWrapR = mWrapR;
-                sampler.mAniso = mAniso;
-                sampler.mN = s;
-
-                return sampler;
-            } catch (android.renderscript.RSRuntimeException e) {
-                throw ExceptionThunker.convertException(e);
-            }
-        }
-    }
-
-
-}
\ No newline at end of file
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Script.java b/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
index 8ac9171..fcb4fd9 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
@@ -23,13 +23,35 @@
  * applications.
  **/
 public class Script extends BaseObj {
-    ScriptCThunker mT;
-
-    android.renderscript.Script getNObj() {
-        return mT;
+    /**
+     * Determine if Incremental Intrinsic Support is needed
+     *
+     */
+    private boolean mUseIncSupp;
+    protected void setIncSupp(boolean useInc) {
+        mUseIncSupp = useInc;
     }
+    protected boolean isIncSupp() {
+        return mUseIncSupp;
+    }
+    /**
+     * An allocation for the compat context will be created when needed
+     * e.g. foreach(ain, aout), setVar(ain);
+     *
+     */
+    private long getDummyAlloc(Allocation ain) {
+        long dInElement = 0;
+        long dInType = 0;
+        long dummyAlloc = 0;
+        if (ain != null) {
+            dInElement = ain.getType().getElement().getDummyElement(mRS);
+            dInType = ain.getType().getDummyType(mRS, dInElement);
+            dummyAlloc = mRS.nIncAllocationCreateTyped(ain.getID(mRS), dInType);
+            ain.setIncAllocID(dummyAlloc);
+        }
 
-
+        return dummyAlloc;
+    }
     /**
      * KernelID is an identifier for a Script + root function pair. It is used
      * as an identifier for ScriptGroup creation.
@@ -43,7 +65,7 @@
         Script mScript;
         int mSlot;
         int mSig;
-        KernelID(int id, RenderScript rs, Script s, int slot, int sig) {
+        KernelID(long id, RenderScript rs, Script s, int slot, int sig) {
             super(id, rs);
             mScript = s;
             mSlot = slot;
@@ -69,19 +91,7 @@
             return k;
         }
 
-        // Any native callers to createKernelID must initialize their own native IDs
-        // excpet ScriptCThunker
-        if (mRS.isNative == true) {
-            k = new KernelID(0, mRS, this, slot, sig);
-            if (mT != null) {
-                k.mN = mT.thunkCreateKernelID(slot, sig, ein, eout);
-            }
-            mKIDs.put(slot, k);
-            return k;
-        }
-
-
-        int id = mRS.nScriptKernelIDCreate(getID(mRS), slot, sig);
+        long id = mRS.nScriptKernelIDCreate(getID(mRS), slot, sig, mUseIncSupp);
         if (id == 0) {
             throw new RSDriverException("Failed to create KernelID");
         }
@@ -93,6 +103,45 @@
     }
 
     /**
+     * InvokeID is an identifier for a invoke function. It is used
+     * as an identifier for ScriptGroup creation.
+     *
+     * This class should not be directly created. Instead use the method in the
+     * reflected or intrinsic code "getInvokeID_funcname()".
+     *
+     * @hide
+     */
+    public static final class InvokeID extends BaseObj {
+        Script mScript;
+        int mSlot;
+        InvokeID(long id, RenderScript rs, Script s, int slot) {
+            super(id, rs);
+            mScript = s;
+            mSlot = slot;
+        }
+    }
+
+    private final SparseArray<InvokeID> mIIDs = new SparseArray<InvokeID>();
+    /**
+     * Only to be used by generated reflected classes.
+     */
+    protected InvokeID createInvokeID(int slot) {
+        InvokeID i = mIIDs.get(slot);
+        if (i != null) {
+            return i;
+        }
+
+        long id = mRS.nScriptInvokeIDCreate(getID(mRS), slot);
+        if (id == 0) {
+            throw new RSDriverException("Failed to create KernelID");
+        }
+
+        i = new InvokeID(id, mRS, this, slot);
+        mIIDs.put(slot, i);
+        return i;
+    }
+
+    /**
      * FieldID is an identifier for a Script + exported field pair. It is used
      * as an identifier for ScriptGroup creation.
      *
@@ -104,7 +153,7 @@
         android.renderscript.Script.FieldID mN;
         Script mScript;
         int mSlot;
-        FieldID(int id, RenderScript rs, Script s, int slot) {
+        FieldID(long id, RenderScript rs, Script s, int slot) {
             super(id, rs);
             mScript = s;
             mSlot = slot;
@@ -121,23 +170,12 @@
      * @return FieldID
      */
     protected FieldID createFieldID(int slot, Element e) {
-
-        // Any thunking caller to createFieldID must create its own native IDs
-        // except ScriptC
-        if (mRS.isNative == true) {
-            FieldID f = new FieldID(0, mRS, this, slot);
-            if (mT != null) {
-                f.mN = mT.thunkCreateFieldID(slot, e);
-            }
-            mFIDs.put(slot, f);
-            return f;
-        }
         FieldID f = mFIDs.get(slot);
         if (f != null) {
             return f;
         }
 
-        int id = mRS.nScriptFieldIDCreate(getID(mRS), slot);
+        long id = mRS.nScriptFieldIDCreate(getID(mRS), slot, mUseIncSupp);
         if (id == 0) {
             throw new RSDriverException("Failed to create FieldID");
         }
@@ -147,19 +185,13 @@
         return f;
     }
 
-
     /**
      * Only intended for use by generated reflected code.
      *
      * @param slot
      */
     protected void invoke(int slot) {
-        if (mT != null) {
-            mT.thunkInvoke(slot);
-            return;
-        }
-
-        mRS.nScriptInvoke(getID(mRS), slot);
+        mRS.nScriptInvoke(getID(mRS), slot, mUseIncSupp);
     }
 
     /**
@@ -169,15 +201,10 @@
      * @param v
      */
     protected void invoke(int slot, FieldPacker v) {
-        if (mT != null) {
-            mT.thunkInvoke(slot, v);
-            return;
-        }
-
         if (v != null) {
-            mRS.nScriptInvokeV(getID(mRS), slot, v.getData());
+            mRS.nScriptInvokeV(getID(mRS), slot, v.getData(), mUseIncSupp);
         } else {
-            mRS.nScriptInvoke(getID(mRS), slot);
+            mRS.nScriptInvoke(getID(mRS), slot, mUseIncSupp);
         }
     }
 
@@ -188,28 +215,18 @@
      * @param slot
      */
     public void bindAllocation(Allocation va, int slot) {
-        if (mT != null) {
-            mT.thunkBindAllocation(va, slot);
-            return;
-        }
-
         mRS.validate();
         if (va != null) {
-            mRS.nScriptBindAllocation(getID(mRS), va.getID(mRS), slot);
+            mRS.nScriptBindAllocation(getID(mRS), va.getID(mRS), slot, mUseIncSupp);
         } else {
-            mRS.nScriptBindAllocation(getID(mRS), 0, slot);
+            mRS.nScriptBindAllocation(getID(mRS), 0, slot, mUseIncSupp);
         }
     }
 
     public void setTimeZone(String timeZone) {
-        if (mT != null) {
-            mT.thunkSetTimeZone(timeZone);
-            return;
-        }
-
         mRS.validate();
         try {
-            mRS.nScriptSetTimeZone(getID(mRS), timeZone.getBytes("UTF-8"));
+            mRS.nScriptSetTimeZone(getID(mRS), timeZone.getBytes("UTF-8"), mUseIncSupp);
         } catch (java.io.UnsupportedEncodingException e) {
             throw new RuntimeException(e);
         }
@@ -225,28 +242,31 @@
      * @param v
      */
     protected void forEach(int slot, Allocation ain, Allocation aout, FieldPacker v) {
-        if (mT != null) {
-            mT.thunkForEach(slot, ain, aout, v);
-            return;
-        }
-
         if (ain == null && aout == null) {
             throw new RSIllegalArgumentException(
                 "At least one of ain or aout is required to be non-null.");
         }
-        int in_id = 0;
+        long in_id = 0;
+        long out_id = 0;
         if (ain != null) {
             in_id = ain.getID(mRS);
         }
-        int out_id = 0;
         if (aout != null) {
             out_id = aout.getID(mRS);
         }
+
         byte[] params = null;
         if (v != null) {
             params = v.getData();
         }
-        mRS.nScriptForEach(getID(mRS), slot, in_id, out_id, params);
+
+        if (mUseIncSupp) {
+            long ainInc = getDummyAlloc(ain);
+            long aoutInc = getDummyAlloc(aout);
+            mRS.nScriptForEach(getID(mRS), slot, ainInc, aoutInc, params, mUseIncSupp);
+        } else {
+            mRS.nScriptForEach(getID(mRS), slot, in_id, out_id, params, mUseIncSupp);
+        }
     }
 
     /**
@@ -259,11 +279,6 @@
      * @param sc
      */
     protected void forEach(int slot, Allocation ain, Allocation aout, FieldPacker v, LaunchOptions sc) {
-        if (mT != null) {
-            mT.thunkForEach(slot, ain, aout, v, sc);
-            return;
-        }
-
         if (ain == null && aout == null) {
             throw new RSIllegalArgumentException(
                 "At least one of ain or aout is required to be non-null.");
@@ -273,23 +288,31 @@
             forEach(slot, ain, aout, v);
             return;
         }
-        int in_id = 0;
+        long in_id = 0;
+        long out_id = 0;
         if (ain != null) {
             in_id = ain.getID(mRS);
         }
-        int out_id = 0;
         if (aout != null) {
             out_id = aout.getID(mRS);
         }
+
         byte[] params = null;
         if (v != null) {
             params = v.getData();
         }
-        mRS.nScriptForEachClipped(getID(mRS), slot, in_id, out_id, params, sc.xstart, sc.xend, sc.ystart, sc.yend, sc.zstart, sc.zend);
+        if (mUseIncSupp) {
+            long ainInc = getDummyAlloc(ain);
+            long aoutInc = getDummyAlloc(aout);
+            mRS.nScriptForEachClipped(getID(mRS), slot, ainInc, aoutInc, params, sc.xstart, sc.xend, sc.ystart, sc.yend, sc.zstart, sc.zend, mUseIncSupp);        
+        } else {
+            mRS.nScriptForEachClipped(getID(mRS), slot, in_id, out_id, params, sc.xstart, sc.xend, sc.ystart, sc.yend, sc.zstart, sc.zend, mUseIncSupp);
+        }
     }
 
-    Script(int id, RenderScript rs) {
+    Script(long id, RenderScript rs) {
         super(id, rs);
+        mUseIncSupp = false;
     }
 
     /**
@@ -299,12 +322,7 @@
      * @param v
      */
     public void setVar(int index, float v) {
-        if (mT != null) {
-            mT.thunkSetVar(index, v);
-            return;
-        }
-
-        mRS.nScriptSetVarF(getID(mRS), index, v);
+        mRS.nScriptSetVarF(getID(mRS), index, v, mUseIncSupp);
     }
 
     /**
@@ -314,12 +332,7 @@
      * @param v
      */
     public void setVar(int index, double v) {
-        if (mT != null) {
-            mT.thunkSetVar(index, v);
-            return;
-        }
-
-        mRS.nScriptSetVarD(getID(mRS), index, v);
+        mRS.nScriptSetVarD(getID(mRS), index, v, mUseIncSupp);
     }
 
     /**
@@ -329,12 +342,7 @@
      * @param v
      */
     public void setVar(int index, int v) {
-        if (mT != null) {
-            mT.thunkSetVar(index, v);
-            return;
-        }
-
-        mRS.nScriptSetVarI(getID(mRS), index, v);
+        mRS.nScriptSetVarI(getID(mRS), index, v, mUseIncSupp);
     }
 
     /**
@@ -344,12 +352,7 @@
      * @param v
      */
     public void setVar(int index, long v) {
-        if (mT != null) {
-            mT.thunkSetVar(index, v);
-            return;
-        }
-
-        mRS.nScriptSetVarJ(getID(mRS), index, v);
+        mRS.nScriptSetVarD(getID(mRS), index, v, mUseIncSupp);
     }
 
     /**
@@ -359,12 +362,7 @@
      * @param v
      */
     public void setVar(int index, boolean v) {
-        if (mT != null) {
-            mT.thunkSetVar(index, v);
-            return;
-        }
-
-        mRS.nScriptSetVarI(getID(mRS), index, v ? 1 : 0);
+        mRS.nScriptSetVarI(getID(mRS), index, v ? 1 : 0, mUseIncSupp);
     }
 
     /**
@@ -374,12 +372,12 @@
      * @param o
      */
     public void setVar(int index, BaseObj o) {
-        if (mT != null) {
-            mT.thunkSetVar(index, o);
-            return;
+        if (mUseIncSupp) {
+            long oInc = getDummyAlloc((Allocation)o);
+            mRS.nScriptSetVarObj(getID(mRS), index, (o == null) ? 0 : oInc, mUseIncSupp);            
+        } else {
+            mRS.nScriptSetVarObj(getID(mRS), index, (o == null) ? 0 : o.getID(mRS), mUseIncSupp);
         }
-
-        mRS.nScriptSetVarObj(getID(mRS), index, (o == null) ? 0 : o.getID(mRS));
     }
 
     /**
@@ -389,12 +387,7 @@
      * @param v
      */
     public void setVar(int index, FieldPacker v) {
-        if (mT != null) {
-            mT.thunkSetVar(index, v);
-            return;
-        }
-
-        mRS.nScriptSetVarV(getID(mRS), index, v.getData());
+        mRS.nScriptSetVarV(getID(mRS), index, v.getData(), mUseIncSupp);
     }
 
     /**
@@ -406,12 +399,12 @@
      * @param dims
      */
     public void setVar(int index, FieldPacker v, Element e, int[] dims) {
-        if (mT != null) {
-            mT.thunkSetVar(index, v, e, dims);
-            return;
+        if (mUseIncSupp) {
+            long dElement = e.getDummyElement(mRS);
+            mRS.nScriptSetVarVE(getID(mRS), index, v.getData(), dElement, dims, mUseIncSupp);
+        } else {
+            mRS.nScriptSetVarVE(getID(mRS), index, v.getData(), e.getID(mRS), dims, mUseIncSupp);
         }
-
-        mRS.nScriptSetVarVE(getID(mRS), index, v.getData(), e.getID(mRS), dims);
     }
 
     /**
@@ -583,4 +576,3 @@
 
     }
 }
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptC.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptC.java
index 7b01154..28e9613 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptC.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptC.java
@@ -42,7 +42,7 @@
      * @param id
      * @param rs
      */
-    protected ScriptC(int id, RenderScript rs) {
+    protected ScriptC(long id, RenderScript rs) {
         super(id, rs);
     }
 
@@ -56,23 +56,36 @@
      */
     protected ScriptC(RenderScript rs, Resources resources, int resourceID) {
         super(0, rs);
-
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker)rs;
-            ScriptCThunker s = new ScriptCThunker(rst, resources, resourceID);
-            mT = s;
-            return;
-        }
-
-        int id = internalCreate(rs, resources, resourceID);
+        long id = internalCreate(rs, resources, resourceID);
         if (id == 0) {
             throw new RSRuntimeException("Loading of ScriptC script failed.");
         }
         setID(id);
     }
 
+    /**
+     * Only intended for use by the generated derived classes.
+     *
+     * @param rs
+     * @param resName
+     * @param bitcode32
+     * @param bitcode64
+     */
+    protected ScriptC(RenderScript rs, String resName, byte[] bitcode32, byte[] bitcode64) {
+        super(0, rs);
+        long id = 0;
+        if (RenderScript.sPointerSize == 4) {
+            id = internalStringCreate(rs, resName, bitcode32);
+        } else {
+            id = internalStringCreate(rs, resName, bitcode64);
+        }
+        if (id == 0) {
+            throw new RSRuntimeException("Loading of ScriptC script failed.");
+        }
+        setID(id);
+    }
 
-    private static synchronized int internalCreate(RenderScript rs, Resources resources, int resourceID) {
+    private static synchronized long internalCreate(RenderScript rs, Resources resources, int resourceID) {
         byte[] pgm;
         int pgmLength;
         InputStream is = resources.openRawResource(resourceID);
@@ -108,4 +121,11 @@
         //Log.v(TAG, " path = " + cachePath);
         return rs.nScriptCCreate(resName, cachePath, pgm, pgmLength);
     }
+
+    private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) {
+        //        Log.v(TAG, "Create script for resource = " + resName);
+        String cachePath = rs.getApplicationContext().getCacheDir().toString();
+        return rs.nScriptCCreate(resName, cachePath, bitcode, bitcode.length);
+    }
+
 }
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptCThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptCThunker.java
deleted file mode 100644
index 2dd9ae9..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptCThunker.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map.Entry;
-import java.util.HashMap;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-
-/**
- *
- **/
-class ScriptCThunker extends android.renderscript.ScriptC {
-    private static final String TAG = "ScriptC";
-
-    protected ScriptCThunker(RenderScriptThunker rs, Resources resources, int resourceID) {
-        super(rs.mN, resources, resourceID);
-    }
-
-    android.renderscript.Script.KernelID thunkCreateKernelID(
-            int slot, int sig, Element ein, Element eout) {
-
-        android.renderscript.Element nein = null;
-        android.renderscript.Element neout = null;
-        if (ein != null) {
-            nein = ((ElementThunker)ein).mN;
-        }
-        if (eout != null) {
-            neout = ((ElementThunker)eout).mN;
-        }
-        try {
-            android.renderscript.Script.KernelID kid = createKernelID(slot, sig, nein, neout);
-            return kid;
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-
-    void thunkInvoke(int slot) {
-        try {
-            invoke(slot);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    void thunkBindAllocation(Allocation va, int slot) {
-        android.renderscript.Allocation nva = null;
-        if (va != null) {
-            nva = ((AllocationThunker)va).mN;
-        }
-        try {
-            bindAllocation(nva, slot);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    void thunkSetTimeZone(String timeZone) {
-        try {
-            setTimeZone(timeZone);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    void thunkInvoke(int slot, FieldPacker v) {
-        try {
-            android.renderscript.FieldPacker nfp =
-                new android.renderscript.FieldPacker(v.getData());
-            invoke(slot, nfp);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    void thunkForEach(int slot, Allocation ain, Allocation aout, FieldPacker v) {
-        android.renderscript.Allocation nin = null;
-        android.renderscript.Allocation nout = null;
-        android.renderscript.FieldPacker nfp = null;
-        if (ain != null) {
-            nin = ((AllocationThunker)ain).mN;
-        }
-        if (aout != null) {
-            nout = ((AllocationThunker)aout).mN;
-        }
-        try {
-            if (v != null) {
-                nfp = new android.renderscript.FieldPacker(v.getData());
-            }
-            forEach(slot, nin, nout, nfp);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    void thunkForEach(int slot, Allocation ain, Allocation aout, FieldPacker v,
-                      android.support.v8.renderscript.Script.LaunchOptions sc) {
-        try {
-            android.renderscript.Script.LaunchOptions lo = null;
-            if (sc != null) {
-                lo = new android.renderscript.Script.LaunchOptions();
-                if (sc.getXEnd() > 0) lo.setX(sc.getXStart(), sc.getXEnd());
-                if (sc.getYEnd() > 0) lo.setY(sc.getYStart(), sc.getYEnd());
-                if (sc.getZEnd() > 0) lo.setZ(sc.getZStart(), sc.getZEnd());
-            }
-
-            android.renderscript.Allocation nin = null;
-            android.renderscript.Allocation nout = null;
-            android.renderscript.FieldPacker nfp = null;
-            if (ain != null) {
-                nin = ((AllocationThunker)ain).mN;
-            }
-            if (aout != null) {
-                nout = ((AllocationThunker)aout).mN;
-            }
-            if (v != null) {
-                nfp = new android.renderscript.FieldPacker(v.getData());
-            }
-            forEach(slot, nin, nout, nfp, lo);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    void thunkSetVar(int index, float v) {
-        try {
-            setVar(index, v);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    void thunkSetVar(int index, double v) {
-        try {
-            setVar(index, v);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    void thunkSetVar(int index, int v) {
-        try {
-            setVar(index, v);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    void thunkSetVar(int index, long v) {
-        try {
-            setVar(index, v);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    void thunkSetVar(int index, boolean v) {
-        try {
-            setVar(index, v);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    void thunkSetVar(int index, BaseObj o) {
-        if (o == null) {
-            try {
-                setVar(index, 0);
-            } catch (android.renderscript.RSRuntimeException e) {
-                throw ExceptionThunker.convertException(e);
-            }
-            return;
-        }
-        try {
-            setVar(index, o.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-    void thunkSetVar(int index, FieldPacker v) {
-        try {
-            android.renderscript.FieldPacker nfp =
-                new android.renderscript.FieldPacker(v.getData());
-            setVar(index, nfp);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    void thunkSetVar(int index, FieldPacker v, Element e, int[] dims) {
-        try {
-            android.renderscript.FieldPacker nfp =
-                new android.renderscript.FieldPacker(v.getData());
-            ElementThunker et = (ElementThunker)e;
-            setVar(index, nfp, et.mN, dims);
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-    }
-
-    android.renderscript.Script.FieldID thunkCreateFieldID(int slot, Element e) {
-        try {
-            ElementThunker et = (ElementThunker) e;
-            return createFieldID(slot, et.getNObj());
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-    }
-
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
index 473d67d..a6e6d00 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
@@ -18,6 +18,8 @@
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 
 /**
  * ScriptGroup creates a group of kernels that are executed
@@ -46,6 +48,8 @@
 public class ScriptGroup extends BaseObj {
     IO mOutputs[];
     IO mInputs[];
+    private boolean mUseIncSupp = false;
+    private ArrayList<Node> mNodes = new ArrayList<Node>();
 
     static class IO {
         Script.KernelID mKID;
@@ -73,6 +77,7 @@
         Script.KernelID mToK;
         Script.KernelID mFrom;
         Type mAllocationType;
+        Allocation mAllocation;
     }
 
     static class Node {
@@ -81,6 +86,8 @@
         ArrayList<ConnectLine> mInputs = new ArrayList<ConnectLine>();
         ArrayList<ConnectLine> mOutputs = new ArrayList<ConnectLine>();
         int dagNumber;
+        boolean mSeen;
+        int mOrder;
 
         Node mNext;
 
@@ -90,7 +97,7 @@
     }
 
 
-    ScriptGroup(int id, RenderScript rs) {
+    ScriptGroup(long id, RenderScript rs) {
         super(id, rs);
     }
 
@@ -107,7 +114,9 @@
         for (int ct=0; ct < mInputs.length; ct++) {
             if (mInputs[ct].mKID == s) {
                 mInputs[ct].mAllocation = a;
-                mRS.nScriptGroupSetInput(getID(mRS), s.getID(mRS), mRS.safeID(a));
+                if (!mUseIncSupp) {
+                    mRS.nScriptGroupSetInput(getID(mRS), s.getID(mRS), mRS.safeID(a));
+                }
                 return;
             }
         }
@@ -127,7 +136,9 @@
         for (int ct=0; ct < mOutputs.length; ct++) {
             if (mOutputs[ct].mKID == s) {
                 mOutputs[ct].mAllocation = a;
-                mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a));
+                if (!mUseIncSupp) {
+                    mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a));
+                }
                 return;
             }
         }
@@ -138,9 +149,70 @@
      * Execute the ScriptGroup.  This will run all the kernels in
      * the ScriptGroup.  No internal connection results will be visible
      * after execution of the ScriptGroup.
+     *
+     * If Incremental Support for intrinsics is needed, the execution
+     * will take the naive path: execute kernels one by one in the
+     * correct order.
      */
     public void execute() {
-        mRS.nScriptGroupExecute(getID(mRS));
+        if (!mUseIncSupp) {
+            mRS.nScriptGroupExecute(getID(mRS));
+        } else {
+            // setup the allocations.
+            for (int ct=0; ct < mNodes.size(); ct++) {
+                Node n = mNodes.get(ct);
+                for (int ct2=0; ct2 < n.mOutputs.size(); ct2++) {
+                    ConnectLine l = n.mOutputs.get(ct2);
+                    if (l.mAllocation !=null) {
+                        continue;
+                    }
+
+                    //create allocation here
+                    Allocation alloc = Allocation.createTyped(mRS, l.mAllocationType,
+                                                              Allocation.MipmapControl.MIPMAP_NONE,
+                                                              Allocation.USAGE_SCRIPT);
+
+                    l.mAllocation = alloc;
+                    for (int ct3=ct2+1; ct3 < n.mOutputs.size(); ct3++) {
+                        if (n.mOutputs.get(ct3).mFrom == l.mFrom) {
+                            n.mOutputs.get(ct3).mAllocation = alloc;
+                        }
+                    }
+                }
+            }
+            for (Node node : mNodes) {
+                for (Script.KernelID kernel : node.mKernels) {
+                    Allocation ain  = null;
+                    Allocation aout = null;
+
+                    for (ConnectLine nodeInput : node.mInputs) {
+                        if (nodeInput.mToK == kernel) {
+                            ain = nodeInput.mAllocation;
+                        }
+                    }
+
+                    for (IO sgInput : mInputs) {
+                        if (sgInput.mKID == kernel) {
+                            ain = sgInput.mAllocation;
+                        }
+                    }
+
+                    for (ConnectLine nodeOutput : node.mOutputs) {
+                        if (nodeOutput.mFrom == kernel) {
+                            aout = nodeOutput.mAllocation;
+                        }
+                    }
+
+                    for (IO sgOutput : mOutputs) {
+                        if (sgOutput.mKID == kernel) {
+                            aout = sgOutput.mAllocation;
+                        }
+                    }
+
+                    kernel.mScript.forEach(kernel.mSlot, ain, aout, null);
+                }
+            }
+        }
     }
 
 
@@ -172,8 +244,7 @@
         private ArrayList<Node> mNodes = new ArrayList<Node>();
         private ArrayList<ConnectLine> mLines = new ArrayList<ConnectLine>();
         private int mKernelCount;
-
-        private ScriptGroupThunker.Builder mT;
+        private boolean mUseIncSupp = false;
 
         /**
          * Create a Builder for generating a ScriptGroup.
@@ -182,9 +253,6 @@
          * @param rs The RenderScript context.
          */
         public Builder(RenderScript rs) {
-            if (rs.isNative) {
-                mT = new ScriptGroupThunker.Builder(rs);
-            }
             mRS = rs;
         }
 
@@ -286,16 +354,13 @@
          * @return Builder Returns this.
          */
         public Builder addKernel(Script.KernelID k) {
-            if (mT != null) {
-                mT.addKernel(k);
-                return this;
-            }
-
             if (mLines.size() != 0) {
                 throw new RSInvalidStateException(
                     "Kernels may not be added once connections exist.");
             }
-
+            if (k.mScript.isIncSupp()) {
+                mUseIncSupp = true;
+            }
             //android.util.Log.v("RSR", "addKernel 1 k=" + k);
             if (findNode(k) != null) {
                 return this;
@@ -326,12 +391,6 @@
          */
         public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) {
             //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
-
-            if (mT != null) {
-                mT.addConnection(t, from, to);
-                return this;
-            }
-
             Node nf = findNode(from);
             if (nf == null) {
                 throw new RSInvalidStateException("From script not found.");
@@ -366,12 +425,6 @@
          */
         public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) {
             //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
-
-            if (mT != null) {
-                mT.addConnection(t, from, to);
-                return this;
-            }
-
             Node nf = findNode(from);
             if (nf == null) {
                 throw new RSInvalidStateException("From script not found.");
@@ -392,7 +445,54 @@
             return this;
         }
 
+        /**
+         * Calculate the order of each node.
+         *
+         *
+         * @return Success or Fail
+         */
+        private boolean calcOrderRecurse(Node node0, int depth) {
+            node0.mSeen = true;
+            if (node0.mOrder < depth) {
+                node0.mOrder = depth;
+            }
+            boolean ret = true;
 
+            for (ConnectLine link : node0.mOutputs) {
+                Node node1 = null;
+                if (link.mToF != null) {
+                    node1 = findNode(link.mToF.mScript);
+                } else {
+                    node1 = findNode(link.mToK.mScript);
+                }
+                if (node1.mSeen) {
+                    return false;
+                }
+                ret &= calcOrderRecurse(node1, node0.mOrder + 1);
+            }
+
+            return ret;
+        }
+
+        private boolean calcOrder() {
+            boolean ret = true;
+            for (Node n0 : mNodes) {
+                if (n0.mInputs.size() == 0) {
+                    for (Node n1 : mNodes) {
+                        n1.mSeen = false;
+                    }
+                    ret &= calcOrderRecurse(n0, 1);
+                }
+            }
+
+            Collections.sort(mNodes, new Comparator<Node>() {
+                public int compare(Node n1, Node n2) {
+                    return n1.mOrder - n2.mOrder;
+                }
+            });
+
+            return ret;
+        }
 
         /**
          * Creates the Script group.
@@ -402,10 +502,6 @@
          */
         public ScriptGroup create() {
 
-            if (mT != null) {
-                return mT.create();
-            }
-
             if (mNodes.size() == 0) {
                 throw new RSInvalidStateException("Empty script groups are not allowed");
             }
@@ -419,7 +515,7 @@
             ArrayList<IO> inputs = new ArrayList<IO>();
             ArrayList<IO> outputs = new ArrayList<IO>();
 
-            int[] kernels = new int[mKernelCount];
+            long[] kernels = new long[mKernelCount];
             int idx = 0;
             for (int ct=0; ct < mNodes.size(); ct++) {
                 Node n = mNodes.get(ct);
@@ -445,33 +541,37 @@
                     if (!hasOutput) {
                         outputs.add(new IO(kid));
                     }
-
                 }
             }
             if (idx != mKernelCount) {
                 throw new RSRuntimeException("Count mismatch, should not happen.");
             }
 
-            int[] src = new int[mLines.size()];
-            int[] dstk = new int[mLines.size()];
-            int[] dstf = new int[mLines.size()];
-            int[] types = new int[mLines.size()];
+            long id = 0;
+            if (!mUseIncSupp) {
+                long[] src = new long[mLines.size()];
+                long[] dstk = new long[mLines.size()];
+                long[] dstf = new long[mLines.size()];
+                long[] types = new long[mLines.size()];
 
-            for (int ct=0; ct < mLines.size(); ct++) {
-                ConnectLine cl = mLines.get(ct);
-                src[ct] = cl.mFrom.getID(mRS);
-                if (cl.mToK != null) {
-                    dstk[ct] = cl.mToK.getID(mRS);
+                for (int ct=0; ct < mLines.size(); ct++) {
+                    ConnectLine cl = mLines.get(ct);
+                    src[ct] = cl.mFrom.getID(mRS);
+                    if (cl.mToK != null) {
+                        dstk[ct] = cl.mToK.getID(mRS);
+                    }
+                    if (cl.mToF != null) {
+                        dstf[ct] = cl.mToF.getID(mRS);
+                    }
+                    types[ct] = cl.mAllocationType.getID(mRS);
                 }
-                if (cl.mToF != null) {
-                    dstf[ct] = cl.mToF.getID(mRS);
+                id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types);
+                if (id == 0) {
+                    throw new RSRuntimeException("Object creation error, should not happen.");
                 }
-                types[ct] = cl.mAllocationType.getID(mRS);
-            }
-
-            int id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types);
-            if (id == 0) {
-                throw new RSRuntimeException("Object creation error, should not happen.");
+            } else {
+                //Calculate the order of the DAG so that script can run one after another.
+                calcOrder();
             }
 
             ScriptGroup sg = new ScriptGroup(id, mRS);
@@ -484,7 +584,8 @@
             for (int ct=0; ct < inputs.size(); ct++) {
                 sg.mInputs[ct] = inputs.get(ct);
             }
-
+            sg.mNodes = mNodes;
+            sg.mUseIncSupp = mUseIncSupp;
             return sg;
         }
 
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroupThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroupThunker.java
deleted file mode 100644
index 588d674..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroupThunker.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-
-class ScriptGroupThunker extends ScriptGroup {
-    android.renderscript.ScriptGroup mN;
-
-    android.renderscript.ScriptGroup getNObj() {
-        return mN;
-    }
-
-    ScriptGroupThunker(int id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    public void setInput(Script.KernelID s, Allocation a) {
-        AllocationThunker at = (AllocationThunker) a;
-        try {
-            mN.setInput(s.mN, at.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setOutput(Script.KernelID s, Allocation a) {
-        AllocationThunker at = (AllocationThunker) a;
-        try {
-            mN.setOutput(s.mN, at.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void execute() {
-        try {
-            mN.execute();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-
-    public static final class Builder {
-
-        android.renderscript.ScriptGroup.Builder bN;
-        RenderScript mRS;
-
-        Builder(RenderScript rs) {
-            RenderScriptThunker rst = (RenderScriptThunker) rs;
-            mRS = rs;
-            try {
-                bN = new android.renderscript.ScriptGroup.Builder(rst.mN);
-            } catch (android.renderscript.RSRuntimeException e) {
-                throw ExceptionThunker.convertException(e);
-            }
-        }
-
-        public Builder addKernel(Script.KernelID k) {
-            try {
-                bN.addKernel(k.mN);
-            } catch (android.renderscript.RSRuntimeException e) {
-                throw ExceptionThunker.convertException(e);
-            }
-            return this;
-        }
-
-        public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) {
-            TypeThunker tt = (TypeThunker) t;
-            try {
-                bN.addConnection(tt.getNObj(), from.mN, to.mN);
-            } catch (android.renderscript.RSRuntimeException e) {
-                throw ExceptionThunker.convertException(e);
-            }
-            return this;
-        }
-
-        public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) {
-            TypeThunker tt = (TypeThunker) t;
-            try {
-                bN.addConnection(tt.getNObj(), from.mN, to.mN);
-            } catch (android.renderscript.RSRuntimeException e) {
-                throw ExceptionThunker.convertException(e);
-            }
-            return this;
-        }
-
-
-
-        public ScriptGroupThunker create() {
-            ScriptGroupThunker sg = new ScriptGroupThunker(0, mRS);
-            try {
-                sg.mN = bN.create();
-            } catch (android.renderscript.RSRuntimeException e) {
-                throw ExceptionThunker.convertException(e);
-            }
-            return sg;
-        }
-    }
-
-
-}
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic.java
index c370017..e59cf6d 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic.java
@@ -25,7 +25,10 @@
  * Not intended for direct use.
  **/
 public abstract class ScriptIntrinsic extends Script {
-    ScriptIntrinsic(int id, RenderScript rs) {
+    ScriptIntrinsic(long id, RenderScript rs) {
         super(id, rs);
+        if (id == 0) {
+            throw new RSRuntimeException("Loading of ScriptIntrinsic failed.");
+        }
     }
 }
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic3DLUT.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic3DLUT.java
index 94727de..dcd1bc1 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic3DLUT.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic3DLUT.java
@@ -30,8 +30,10 @@
 public class ScriptIntrinsic3DLUT extends ScriptIntrinsic {
     private Allocation mLUT;
     private Element mElement;
+    // API level for the intrinsic
+    private static final int INTRINSIC_API_LEVEL = 19;
 
-    protected ScriptIntrinsic3DLUT(int id, RenderScript rs, Element e) {
+    protected ScriptIntrinsic3DLUT(long id, RenderScript rs, Element e) {
         super(id, rs);
         mElement = e;
     }
@@ -47,17 +49,18 @@
      * @return ScriptIntrinsic3DLUT
      */
     public static ScriptIntrinsic3DLUT create(RenderScript rs, Element e) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker) rs;
-            return ScriptIntrinsic3DLUTThunker.create(rs, e);
-        }
-        int id = rs.nScriptIntrinsicCreate(8, e.getID(rs));
-
         if (!e.isCompatible(Element.U8_4(rs))) {
             throw new RSIllegalArgumentException("Element must be compatible with uchar4.");
         }
+        long id;
+        boolean mUseIncSupp = rs.isUseNative() &&
+                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
 
-        return new ScriptIntrinsic3DLUT(id, rs, e);
+        id = rs.nScriptIntrinsicCreate(8, e.getID(rs), mUseIncSupp);
+
+        ScriptIntrinsic3DLUT si = new ScriptIntrinsic3DLUT(id, rs, e);
+        si.setIncSupp(mUseIncSupp);
+        return si;
     }
 
     /**
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic3DLUTThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic3DLUTThunker.java
deleted file mode 100644
index a1c6ada..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic3DLUTThunker.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import android.util.Log;
-
-/**
- *
- * @hide
- **/
-class ScriptIntrinsic3DLUTThunker extends ScriptIntrinsic3DLUT {
-    android.renderscript.ScriptIntrinsic3DLUT mN;
-
-    android.renderscript.ScriptIntrinsic3DLUT getNObj() {
-        return mN;
-    }
-
-    private ScriptIntrinsic3DLUTThunker(int id, RenderScript rs, Element e) {
-        super(id, rs, e);
-    }
-
-    public static ScriptIntrinsic3DLUTThunker create(RenderScript rs, Element e) {
-        RenderScriptThunker rst = (RenderScriptThunker) rs;
-        ElementThunker et = (ElementThunker) e;
-
-        ScriptIntrinsic3DLUTThunker lut = new ScriptIntrinsic3DLUTThunker(0, rs, e);
-        try {
-            lut.mN = android.renderscript.ScriptIntrinsic3DLUT.create(rst.mN, et.getNObj());
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-        return lut;
-    }
-
-    public void setLUT(Allocation lut) {
-        AllocationThunker lutt = (AllocationThunker) lut;
-        try {
-            mN.setLUT(lutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-
-    /**
-     * Invoke the kernel and apply the lookup to each cell of ain
-     * and copy to aout.
-     *
-     * @param ain Input allocation
-     * @param aout Output allocation
-     */
-    public void forEach(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-        try {
-            mN.forEach(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    /**
-     * Get a KernelID for this intrinsic kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelID() {
-        Script.KernelID k = createKernelID(0, 3, null, null);
-        try {
-            k.mN = mN.getKernelID();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlend.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlend.java
index c48fd45..7b1e59b 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlend.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlend.java
@@ -22,7 +22,10 @@
  * {@link android.support.v8.renderscript.Allocation} objects.
  **/
 public class ScriptIntrinsicBlend extends ScriptIntrinsic {
-    ScriptIntrinsicBlend(int id, RenderScript rs) {
+    // API level for the intrinsic
+    private static final int INTRINSIC_API_LEVEL = 19;
+
+    ScriptIntrinsicBlend(long id, RenderScript rs) {
         super(id, rs);
     }
 
@@ -35,13 +38,16 @@
      * @return ScriptIntrinsicBlend
      */
     public static ScriptIntrinsicBlend create(RenderScript rs, Element e) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker) rs;
-            return ScriptIntrinsicBlendThunker.create(rs, e);
-        }
         // 7 comes from RS_SCRIPT_INTRINSIC_ID_BLEND in rsDefines.h
-        int id = rs.nScriptIntrinsicCreate(7, e.getID(rs));
-        return new ScriptIntrinsicBlend(id, rs);
+        long id;
+        boolean mUseIncSupp = rs.isUseNative() &&
+                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
+
+        id = rs.nScriptIntrinsicCreate(7, e.getID(rs), mUseIncSupp);
+
+        ScriptIntrinsicBlend si = new ScriptIntrinsicBlend(id, rs);
+        si.setIncSupp(mUseIncSupp);
+        return si;
 
     }
 
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlendThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlendThunker.java
deleted file mode 100644
index 7c70934..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlendThunker.java
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-class ScriptIntrinsicBlendThunker extends ScriptIntrinsicBlend {
-    android.renderscript.ScriptIntrinsicBlend mN;
-
-    android.renderscript.ScriptIntrinsicBlend getNObj() {
-        return mN;
-    }
-
-    ScriptIntrinsicBlendThunker(int id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    public static ScriptIntrinsicBlendThunker create(RenderScript rs, Element e) {
-        RenderScriptThunker rst = (RenderScriptThunker) rs;
-        ElementThunker et = (ElementThunker)e;
-
-        ScriptIntrinsicBlendThunker blend = new ScriptIntrinsicBlendThunker(0, rs);
-        try {
-            blend.mN = android.renderscript.ScriptIntrinsicBlend.create(rst.mN, et.getNObj());
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-        return blend;
-    }
-
-    public void forEachClear(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachClear(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDClear() {
-        Script.KernelID k = createKernelID(0, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDClear();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachSrc(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachSrc(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDSrc() {
-        Script.KernelID k = createKernelID(1, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDSrc();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachDst(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachDst(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDDst() {
-        Script.KernelID k = createKernelID(2, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDDst();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachSrcOver(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachSrcOver(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDSrcOver() {
-        Script.KernelID k = createKernelID(3, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDSrcOver();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachDstOver(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachDstOver(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDDstOver() {
-        Script.KernelID k = createKernelID(4, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDDstOver();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachSrcIn(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachSrcIn(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDSrcIn() {
-        Script.KernelID k = createKernelID(5, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDSrcIn();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachDstIn(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachDstIn(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDDstIn() {
-        Script.KernelID k = createKernelID(6, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDDstIn();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachSrcOut(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachSrcOut(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDSrcOut() {
-        Script.KernelID k = createKernelID(7, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDSrcOut();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachDstOut(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachDstOut(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDDstOut() {
-        Script.KernelID k = createKernelID(8, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDDstOut();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachSrcAtop(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachSrcAtop(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDSrcAtop() {
-        Script.KernelID k = createKernelID(9, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDSrcAtop();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachDstAtop(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachDstAtop(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDDstAtop() {
-        Script.KernelID k = createKernelID(10, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDDstAtop();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachXor(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachXor(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDXor() {
-        Script.KernelID k = createKernelID(11, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDXor();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachMultiply(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachMultiply(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDMultiply() {
-        Script.KernelID k = createKernelID(14, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDMultiply();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachAdd(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachAdd(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDAdd() {
-        Script.KernelID k = createKernelID(34, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDAdd();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public void forEachSubtract(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-
-        try {
-            mN.forEachSubtract(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelIDSubtract() {
-        Script.KernelID k = createKernelID(35, 3, null, null);
-        try {
-            k.mN = mN.getKernelIDSubtract();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
index b2b74cb..a5b046b 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
@@ -29,8 +29,10 @@
 public class ScriptIntrinsicBlur extends ScriptIntrinsic {
     private final float[] mValues = new float[9];
     private Allocation mInput;
+    // API level for the intrinsic
+    private static final int INTRINSIC_API_LEVEL = 19;
 
-    protected ScriptIntrinsicBlur(int id, RenderScript rs) {
+    protected ScriptIntrinsicBlur(long id, RenderScript rs) {
         super(id, rs);
     }
 
@@ -46,17 +48,20 @@
      * @return ScriptIntrinsicBlur
      */
     public static ScriptIntrinsicBlur create(RenderScript rs, Element e) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker) rs;
-            return ScriptIntrinsicBlurThunker.create(rs, e);
-        }
         if ((!e.isCompatible(Element.U8_4(rs))) && (!e.isCompatible(Element.U8(rs)))) {
             throw new RSIllegalArgumentException("Unsuported element type.");
         }
-        int id = rs.nScriptIntrinsicCreate(5, e.getID(rs));
-        ScriptIntrinsicBlur sib = new ScriptIntrinsicBlur(id, rs);
-        sib.setRadius(5.f);
-        return sib;
+        long id;
+        boolean mUseIncSupp = rs.isUseNative() &&
+                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
+
+        id = rs.nScriptIntrinsicCreate(5, e.getID(rs), mUseIncSupp);
+
+        ScriptIntrinsicBlur si = new ScriptIntrinsicBlur(id, rs);
+        si.setIncSupp(mUseIncSupp);
+        si.setRadius(5.f);
+
+        return si;
     }
 
     /**
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlurThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlurThunker.java
deleted file mode 100644
index 31e22bb..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlurThunker.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-class ScriptIntrinsicBlurThunker extends ScriptIntrinsicBlur {
-
-    android.renderscript.ScriptIntrinsicBlur mN;
-
-    android.renderscript.ScriptIntrinsicBlur getNObj() {
-        return mN;
-    }
-
-    protected ScriptIntrinsicBlurThunker(int id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    public static ScriptIntrinsicBlurThunker create(RenderScript rs, Element e) {
-        RenderScriptThunker rst = (RenderScriptThunker) rs;
-        ElementThunker et = (ElementThunker) e;
-
-        ScriptIntrinsicBlurThunker blur = new ScriptIntrinsicBlurThunker(0, rs);
-        try {
-            blur.mN = android.renderscript.ScriptIntrinsicBlur.create(rst.mN, et.getNObj());
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-        return blur;
-    }
-
-    public void setInput(Allocation ain) {
-        AllocationThunker aint = (AllocationThunker) ain;
-        try {
-            mN.setInput(aint.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setRadius(float radius) {
-        try {
-            mN.setRadius(radius);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void forEach(Allocation aout) {
-        AllocationThunker aoutt = (AllocationThunker) aout;
-        if (aoutt != null) {
-            try {
-                mN.forEach(aoutt.getNObj());
-            } catch (android.renderscript.RSRuntimeException e) {
-                throw ExceptionThunker.convertException(e);
-            }
-        }
-    }
-
-    public Script.KernelID getKernelID() {
-        Script.KernelID k = createKernelID(0, 2, null, null);
-        try {
-            k.mN = mN.getKernelID();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public Script.FieldID getFieldID_Input() {
-        Script.FieldID f = createFieldID(1, null);
-        try {
-            f.mN = mN.getFieldID_Input();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return f;
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicColorMatrix.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicColorMatrix.java
index f80d4ac..5aa9572 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicColorMatrix.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicColorMatrix.java
@@ -29,9 +29,12 @@
  **/
 public class ScriptIntrinsicColorMatrix extends ScriptIntrinsic {
     private final Matrix4f mMatrix = new Matrix4f();
+    private final Float4 mAdd = new Float4();
     private Allocation mInput;
+    // API level for the intrinsic
+    private static final int INTRINSIC_API_LEVEL = 19;
 
-    protected ScriptIntrinsicColorMatrix(int id, RenderScript rs) {
+    protected ScriptIntrinsicColorMatrix(long id, RenderScript rs) {
         super(id, rs);
     }
 
@@ -47,16 +50,18 @@
      * @return ScriptIntrinsicColorMatrix
      */
     public static ScriptIntrinsicColorMatrix create(RenderScript rs, Element e) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker) rs;
-            return ScriptIntrinsicColorMatrixThunker.create(rs, e);
-        }
-
         if (!e.isCompatible(Element.U8_4(rs))) {
             throw new RSIllegalArgumentException("Unsuported element type.");
         }
-        int id = rs.nScriptIntrinsicCreate(2, e.getID(rs));
-        return new ScriptIntrinsicColorMatrix(id, rs);
+        long id;
+        boolean mUseIncSupp = rs.isUseNative() &&
+                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
+
+        id = rs.nScriptIntrinsicCreate(2, e.getID(rs), mUseIncSupp);
+
+        ScriptIntrinsicColorMatrix si = new ScriptIntrinsicColorMatrix(id, rs);
+        si.setIncSupp(mUseIncSupp);
+        return si;
 
     }
 
@@ -89,6 +94,49 @@
     }
 
     /**
+     * Set the value to be added after the color matrix has been
+     * applied. The default value is {0, 0, 0, 0}
+     *
+     * @param f The float4 value to be added.
+     */
+    public void setAdd(Float4 f) {
+        mAdd.x = f.x;
+        mAdd.y = f.y;
+        mAdd.z = f.z;
+        mAdd.w = f.w;
+
+        FieldPacker fp = new FieldPacker(4*4);
+        fp.addF32(f.x);
+        fp.addF32(f.y);
+        fp.addF32(f.z);
+        fp.addF32(f.w);
+        setVar(1, fp);
+    }
+
+    /**
+     * Set the value to be added after the color matrix has been
+     * applied. The default value is {0, 0, 0, 0}
+     *
+     * @param r The red add value.
+     * @param g The green add value.
+     * @param b The blue add value.
+     * @param a The alpha add value.
+     */
+    public void setAdd(float r, float g, float b, float a) {
+        mAdd.x = r;
+        mAdd.y = g;
+        mAdd.z = b;
+        mAdd.w = a;
+
+        FieldPacker fp = new FieldPacker(4*4);
+        fp.addF32(mAdd.x);
+        fp.addF32(mAdd.y);
+        fp.addF32(mAdd.z);
+        fp.addF32(mAdd.w);
+        setVar(1, fp);
+    }
+
+    /**
      * Set a color matrix to convert from RGB to luminance. The alpha channel
      * will be a copy.
      *
@@ -158,6 +206,50 @@
     }
 
     /**
+     * Invoke the kernel and apply the matrix to each cell of input
+     * {@link Allocation} and copy to the output {@link Allocation}.
+     *
+     * If the vector size of the input is less than four, the
+     * remaining components are treated as zero for the matrix
+     * multiply.
+     *
+     * If the output vector size is less than four, the unused
+     * vector components are discarded.
+     *
+     *
+     * @param ain Input allocation
+     * @param aout Output allocation
+     * @param opt LaunchOptions for clipping
+     */
+    public void forEach(Allocation ain, Allocation aout, Script.LaunchOptions opt) {
+        if (!ain.getElement().isCompatible(Element.U8(mRS)) &&
+            !ain.getElement().isCompatible(Element.U8_2(mRS)) &&
+            !ain.getElement().isCompatible(Element.U8_3(mRS)) &&
+            !ain.getElement().isCompatible(Element.U8_4(mRS)) &&
+            !ain.getElement().isCompatible(Element.F32(mRS)) &&
+            !ain.getElement().isCompatible(Element.F32_2(mRS)) &&
+            !ain.getElement().isCompatible(Element.F32_3(mRS)) &&
+            !ain.getElement().isCompatible(Element.F32_4(mRS))) {
+
+            throw new RSIllegalArgumentException("Unsuported element type.");
+        }
+
+        if (!aout.getElement().isCompatible(Element.U8(mRS)) &&
+            !aout.getElement().isCompatible(Element.U8_2(mRS)) &&
+            !aout.getElement().isCompatible(Element.U8_3(mRS)) &&
+            !aout.getElement().isCompatible(Element.U8_4(mRS)) &&
+            !aout.getElement().isCompatible(Element.F32(mRS)) &&
+            !aout.getElement().isCompatible(Element.F32_2(mRS)) &&
+            !aout.getElement().isCompatible(Element.F32_3(mRS)) &&
+            !aout.getElement().isCompatible(Element.F32_4(mRS))) {
+
+            throw new RSIllegalArgumentException("Unsuported element type.");
+        }
+
+        forEach(0, ain, aout, null, opt);
+    }
+
+    /**
      * Get a KernelID for this intrinsic kernel.
      *
      * @return Script.KernelID The KernelID object.
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicColorMatrixThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicColorMatrixThunker.java
deleted file mode 100644
index 797c0e7..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicColorMatrixThunker.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import android.util.Log;
-
-class ScriptIntrinsicColorMatrixThunker extends ScriptIntrinsicColorMatrix {
-    android.renderscript.ScriptIntrinsicColorMatrix mN;
-
-    android.renderscript.ScriptIntrinsicColorMatrix getNObj() {
-        return mN;
-    }
-
-    private ScriptIntrinsicColorMatrixThunker(int id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    public static ScriptIntrinsicColorMatrixThunker create(RenderScript rs, Element e) {
-        RenderScriptThunker rst = (RenderScriptThunker) rs;
-        ElementThunker et = (ElementThunker)e;
-
-        ScriptIntrinsicColorMatrixThunker cm =  new ScriptIntrinsicColorMatrixThunker(0, rs);
-        try {
-            cm.mN = android.renderscript.ScriptIntrinsicColorMatrix.create(rst.mN, et.getNObj());
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-        return cm;
-
-    }
-
-    public void setColorMatrix(Matrix4f m) {
-        try {
-            mN.setColorMatrix(new android.renderscript.Matrix4f(m.getArray()));
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setColorMatrix(Matrix3f m) {
-        try {
-            mN.setColorMatrix(new android.renderscript.Matrix3f(m.getArray()));
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setGreyscale() {
-        try {
-            mN.setGreyscale();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setYUVtoRGB() {
-        try {
-            mN.setYUVtoRGB();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setRGBtoYUV() {
-        try {
-            mN.setRGBtoYUV();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-
-    public void forEach(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-        try {
-            mN.forEach(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelID() {
-        Script.KernelID k = createKernelID(0, 3, null, null);
-        try {
-            k.mN = mN.getKernelID();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
index c902917..ea45461 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
@@ -25,13 +25,18 @@
 public class ScriptIntrinsicConvolve3x3 extends ScriptIntrinsic {
     private final float[] mValues = new float[9];
     private Allocation mInput;
+    // API level for the intrinsic
+    private static final int INTRINSIC_API_LEVEL = 19;
 
-    ScriptIntrinsicConvolve3x3(int id, RenderScript rs) {
+    ScriptIntrinsicConvolve3x3(long id, RenderScript rs) {
         super(id, rs);
     }
 
     /**
-     * Supported elements types are {@link Element#U8_4}
+     * Supported elements types are {@link Element#U8}, {@link
+     * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
+     * {@link Element#F32}, {@link Element#F32_2}, {@link
+     * Element#F32_3}, and {@link Element#F32_4}
      *
      * The default coefficients are.
      *
@@ -47,20 +52,27 @@
      * @return ScriptIntrinsicConvolve3x3
      */
     public static ScriptIntrinsicConvolve3x3 create(RenderScript rs, Element e) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker) rs;
-            return ScriptIntrinsicConvolve3x3Thunker.create(rs, e);
-        }
-
         float f[] = { 0, 0, 0, 0, 1, 0, 0, 0, 0};
-        if (!e.isCompatible(Element.U8_4(rs))) {
+        if (!e.isCompatible(Element.U8(rs)) &&
+            !e.isCompatible(Element.U8_2(rs)) &&
+            !e.isCompatible(Element.U8_3(rs)) &&
+            !e.isCompatible(Element.U8_4(rs)) &&
+            !e.isCompatible(Element.F32(rs)) &&
+            !e.isCompatible(Element.F32_2(rs)) &&
+            !e.isCompatible(Element.F32_3(rs)) &&
+            !e.isCompatible(Element.F32_4(rs))) {
             throw new RSIllegalArgumentException("Unsuported element type.");
         }
-        int id = rs.nScriptIntrinsicCreate(1, e.getID(rs));
+        long id;
+        boolean mUseIncSupp = rs.isUseNative() &&
+                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
+
+        id = rs.nScriptIntrinsicCreate(1, e.getID(rs), mUseIncSupp);
+
         ScriptIntrinsicConvolve3x3 si = new ScriptIntrinsicConvolve3x3(id, rs);
+        si.setIncSupp(mUseIncSupp);
         si.setCoefficients(f);
         return si;
-
     }
 
     /**
@@ -107,6 +119,18 @@
     }
 
     /**
+     * Apply the filter to the input and save to the specified
+     * allocation.
+     *
+     * @param aout Output allocation. Must match creation element
+     *             type.
+     * @param opt LaunchOptions for clipping
+     */
+    public void forEach(Allocation aout, Script.LaunchOptions opt) {
+        forEach(0, (Allocation) null, aout, null, opt);
+    }
+
+    /**
      * Get a KernelID for this intrinsic kernel.
      *
      * @return Script.KernelID The KernelID object.
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3Thunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3Thunker.java
deleted file mode 100644
index fa997c1..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3Thunker.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import android.util.Log;
-
-class ScriptIntrinsicConvolve3x3Thunker extends ScriptIntrinsicConvolve3x3 {
-    android.renderscript.ScriptIntrinsicConvolve3x3 mN;
-
-    android.renderscript.ScriptIntrinsicConvolve3x3 getNObj() {
-        return mN;
-    }
-
-
-    ScriptIntrinsicConvolve3x3Thunker(int id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    public static ScriptIntrinsicConvolve3x3Thunker create(RenderScript rs, Element e) {
-        RenderScriptThunker rst = (RenderScriptThunker) rs;
-        ElementThunker et = (ElementThunker) e;
-
-        ScriptIntrinsicConvolve3x3Thunker si = new ScriptIntrinsicConvolve3x3Thunker(0, rs);
-        try {
-            si.mN = android.renderscript.ScriptIntrinsicConvolve3x3.create(rst.mN, et.getNObj());
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-        return si;
-    }
-
-    public void setInput(Allocation ain) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        try {
-            mN.setInput(aint.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setCoefficients(float v[]) {
-        try {
-            mN.setCoefficients(v);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void forEach(Allocation aout) {
-        AllocationThunker aoutt = (AllocationThunker)aout;
-        try {
-            mN.forEach(aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-
-    }
-
-    public Script.KernelID getKernelID() {
-        Script.KernelID k = createKernelID(0, 2, null, null);
-        try {
-            k.mN = mN.getKernelID();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public Script.FieldID getFieldID_Input() {
-        Script.FieldID f = createFieldID(1, null);
-        try {
-            f.mN = mN.getFieldID_Input();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return f;
-    }
-
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
index a651c0f..bcd37f1 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
@@ -25,13 +25,18 @@
 public class ScriptIntrinsicConvolve5x5 extends ScriptIntrinsic {
     private final float[] mValues = new float[25];
     private Allocation mInput;
+    // API level for the intrinsic
+    private static final int INTRINSIC_API_LEVEL = 19;
 
-    ScriptIntrinsicConvolve5x5(int id, RenderScript rs) {
+    ScriptIntrinsicConvolve5x5(long id, RenderScript rs) {
         super(id, rs);
     }
 
     /**
-     * Supported elements types are {@link Element#U8_4}
+     * Supported elements types are {@link Element#U8}, {@link
+     * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
+     * {@link Element#F32}, {@link Element#F32_2}, {@link
+     * Element#F32_3}, and {@link Element#F32_4}
      *
      * The default coefficients are.
      * <code>
@@ -48,12 +53,25 @@
      * @return ScriptIntrinsicConvolve5x5
      */
     public static ScriptIntrinsicConvolve5x5 create(RenderScript rs, Element e) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker) rs;
-            return ScriptIntrinsicConvolve5x5Thunker.create(rs, e);
+        if (!e.isCompatible(Element.U8(rs)) &&
+            !e.isCompatible(Element.U8_2(rs)) &&
+            !e.isCompatible(Element.U8_3(rs)) &&
+            !e.isCompatible(Element.U8_4(rs)) &&
+            !e.isCompatible(Element.F32(rs)) &&
+            !e.isCompatible(Element.F32_2(rs)) &&
+            !e.isCompatible(Element.F32_3(rs)) &&
+            !e.isCompatible(Element.F32_4(rs))) {
+            throw new RSIllegalArgumentException("Unsuported element type.");
         }
-        int id = rs.nScriptIntrinsicCreate(4, e.getID(rs));
-        return new ScriptIntrinsicConvolve5x5(id, rs);
+        long id;
+        boolean mUseIncSupp = rs.isUseNative() &&
+                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
+
+        id = rs.nScriptIntrinsicCreate(4, e.getID(rs), mUseIncSupp);
+
+        ScriptIntrinsicConvolve5x5 si = new ScriptIntrinsicConvolve5x5(id, rs);
+        si.setIncSupp(mUseIncSupp);
+        return si;
 
     }
 
@@ -103,6 +121,19 @@
     }
 
     /**
+     * Apply the filter to the input and save to the specified
+     * allocation.
+     *
+     * @param aout Output allocation. Must match creation element
+     *             type.
+     * @param opt LaunchOptions for clipping
+     */
+    public void forEach(Allocation aout, Script.LaunchOptions opt) {
+        forEach(0, (Allocation) null, aout, null, opt);
+    }
+
+
+    /**
      * Get a KernelID for this intrinsic kernel.
      *
      * @return Script.KernelID The KernelID object.
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5Thunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5Thunker.java
deleted file mode 100644
index 2071ddd..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5Thunker.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import android.util.Log;
-
-class ScriptIntrinsicConvolve5x5Thunker extends ScriptIntrinsicConvolve5x5 {
-    android.renderscript.ScriptIntrinsicConvolve5x5 mN;
-
-    android.renderscript.ScriptIntrinsicConvolve5x5 getNObj() {
-        return mN;
-    }
-
-
-    ScriptIntrinsicConvolve5x5Thunker(int id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    public static ScriptIntrinsicConvolve5x5Thunker create(RenderScript rs, Element e) {
-        RenderScriptThunker rst = (RenderScriptThunker) rs;
-        ElementThunker et = (ElementThunker) e;
-
-        ScriptIntrinsicConvolve5x5Thunker si = new ScriptIntrinsicConvolve5x5Thunker(0, rs);
-        try {
-            si.mN = android.renderscript.ScriptIntrinsicConvolve5x5.create(rst.mN, et.getNObj());
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-        return si;
-    }
-
-    public void setInput(Allocation ain) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        try {
-            mN.setInput(aint.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setCoefficients(float v[]) {
-        try {
-            mN.setCoefficients(v);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void forEach(Allocation aout) {
-        AllocationThunker aoutt = (AllocationThunker)aout;
-        try {
-            mN.forEach(aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-
-    }
-
-    public Script.KernelID getKernelID() {
-        Script.KernelID k = createKernelID(0, 2, null, null);
-        try {
-            k.mN = mN.getKernelID();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public Script.FieldID getFieldID_Input() {
-        Script.FieldID f = createFieldID(1, null);
-        try {
-            f.mN = mN.getFieldID_Input();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return f;
-    }
-
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicHistogram.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicHistogram.java
new file mode 100644
index 0000000..43ac015
--- /dev/null
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicHistogram.java
@@ -0,0 +1,232 @@
+/*
+ * 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.v8.renderscript;
+
+import android.util.Log;
+
+/**
+ * Intrinsic Histogram filter.
+ *
+ *
+ **/
+public class ScriptIntrinsicHistogram extends ScriptIntrinsic {
+    private Allocation mOut;
+    // API level for the intrinsic
+    private static final int INTRINSIC_API_LEVEL = 19;
+
+    protected ScriptIntrinsicHistogram(long id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    /**
+     * Create an intrinsic for calculating the histogram of an uchar
+     * or uchar4 image.
+     *
+     * Supported elements types are
+     * {@link Element#U8_4}, {@link Element#U8_3},
+     * {@link Element#U8_2}, {@link Element#U8}
+     *
+     * @param rs The RenderScript context
+     * @param e Element type for inputs
+     *
+     * @return ScriptIntrinsicHistogram
+     */
+    public static ScriptIntrinsicHistogram create(RenderScript rs, Element e) {
+        if ((!e.isCompatible(Element.U8_4(rs))) &&
+            (!e.isCompatible(Element.U8_3(rs))) &&
+            (!e.isCompatible(Element.U8_2(rs))) &&
+            (!e.isCompatible(Element.U8(rs)))) {
+            throw new RSIllegalArgumentException("Unsuported element type.");
+        }
+        long id;
+        boolean mUseIncSupp = rs.isUseNative() &&
+                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
+
+        id = rs.nScriptIntrinsicCreate(9, e.getID(rs), mUseIncSupp);
+
+        ScriptIntrinsicHistogram si = new ScriptIntrinsicHistogram(id, rs);
+        si.setIncSupp(mUseIncSupp);
+        return si;
+    }
+
+    /**
+     * Process an input buffer and place the histogram into the
+     * output allocation. The output allocation may be a narrower
+     * vector size than the input. In this case the vector size of
+     * the output is used to determine how many of the input
+     * channels are used in the computation. This is useful if you
+     * have an RGBA input buffer but only want the histogram for
+     * RGB.
+     *
+     * 1D and 2D input allocations are supported.
+     *
+     * @param ain The input image
+     */
+    public void forEach(Allocation ain) {
+        forEach(ain, null);
+    }
+
+    /**
+     * Process an input buffer and place the histogram into the
+     * output allocation. The output allocation may be a narrower
+     * vector size than the input. In this case the vector size of
+     * the output is used to determine how many of the input
+     * channels are used in the computation. This is useful if you
+     * have an RGBA input buffer but only want the histogram for
+     * RGB.
+     *
+     * 1D and 2D input allocations are supported.
+     *
+     * @param ain The input image
+     * @param opt LaunchOptions for clipping
+     */
+    public void forEach(Allocation ain, Script.LaunchOptions opt) {
+        if (ain.getType().getElement().getVectorSize() <
+            mOut.getType().getElement().getVectorSize()) {
+
+            throw new RSIllegalArgumentException(
+                "Input vector size must be >= output vector size.");
+        }
+        if (mOut.getType().getElement().getVectorSize() == 3) {
+            throw new RSIllegalArgumentException(
+                "Output vector size should not be 3 for Input vector size 4.");
+        }
+        if (!ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
+            !ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
+            throw new RSIllegalArgumentException("Output type must be U8 or U8_4.");
+        }
+
+        forEach(0, ain, null, null, opt);
+    }
+
+
+
+    /**
+     * Set the coefficients used for the RGBA to Luminocity
+     * calculation. The default is {0.299f, 0.587f, 0.114f, 0.f}.
+     *
+     * Coefficients must be >= 0 and sum to 1.0 or less.
+     *
+     * @param r Red coefficient
+     * @param g Green coefficient
+     * @param b Blue coefficient
+     * @param a Alpha coefficient
+     */
+    public void setDotCoefficients(float r, float g, float b, float a) {
+        if ((r < 0.f) || (g < 0.f) || (b < 0.f) || (a < 0.f)) {
+            throw new RSIllegalArgumentException("Coefficient may not be negative.");
+        }
+        if ((r + g + b + a) > 1.f) {
+            throw new RSIllegalArgumentException("Sum of coefficients must be 1.0 or less.");
+        }
+
+        FieldPacker fp = new FieldPacker(16);
+        fp.addF32(r);
+        fp.addF32(g);
+        fp.addF32(b);
+        fp.addF32(a);
+        setVar(0, fp);
+    }
+
+    /**
+     * Set the output of the histogram.  32 bit integer types are
+     * supported.
+     *
+     * @param aout The output allocation
+     */
+    public void setOutput(Allocation aout) {
+        mOut = aout;
+        if (mOut.getType().getElement() != Element.U32(mRS) &&
+            mOut.getType().getElement() != Element.U32_2(mRS) &&
+            mOut.getType().getElement() != Element.U32_3(mRS) &&
+            mOut.getType().getElement() != Element.U32_4(mRS) &&
+            mOut.getType().getElement() != Element.I32(mRS) &&
+            mOut.getType().getElement() != Element.I32_2(mRS) &&
+            mOut.getType().getElement() != Element.I32_3(mRS) &&
+            mOut.getType().getElement() != Element.I32_4(mRS)) {
+
+            throw new RSIllegalArgumentException("Output type must be U32 or I32.");
+        }
+        if ((mOut.getType().getX() != 256) ||
+            (mOut.getType().getY() != 0) ||
+            mOut.getType().hasMipmaps() ||
+            (mOut.getType().getYuv() != 0)) {
+
+            throw new RSIllegalArgumentException("Output must be 1D, 256 elements.");
+        }
+        setVar(1, aout);
+    }
+
+
+    /**
+     * Process an input buffer and place the histogram into the
+     * output allocation. The dot product of the input channel and
+     * the coefficients from 'setDotCoefficients' are used to
+     * calculate the output values.
+     *
+     * 1D and 2D input allocations are supported.
+     *
+     * @param ain The input image
+     */
+    public void forEach_Dot(Allocation ain) {
+        forEach_Dot(ain, null);
+    }
+
+    /**
+     * Process an input buffer and place the histogram into the
+     * output allocation. The dot product of the input channel and
+     * the coefficients from 'setDotCoefficients' are used to
+     * calculate the output values.
+     *
+     * 1D and 2D input allocations are supported.
+     *
+     * @param ain The input image
+     * @param opt LaunchOptions for clipping
+     */
+    public void forEach_Dot(Allocation ain, Script.LaunchOptions opt) {
+        if (mOut.getType().getElement().getVectorSize() != 1) {
+            throw new RSIllegalArgumentException("Output vector size must be one.");
+        }
+        if (!ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
+            !ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
+            throw new RSIllegalArgumentException("Output type must be U8 or U8_4.");
+        }
+
+        forEach(1, ain, null, null, opt);
+    }
+
+
+
+    /**
+     * Get a KernelID for this intrinsic kernel.
+     *
+     * @return Script.KernelID The KernelID object.
+     */
+    public Script.KernelID getKernelID_Separate() {
+        return createKernelID(0, 3, null, null);
+    }
+
+    /**
+     * Get a FieldID for the input field of this intrinsic.
+     *
+     * @return Script.FieldID The FieldID object.
+     */
+    public Script.FieldID getFieldID_Input() {
+        return createFieldID(1, null);
+    }
+}
+
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicLUT.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicLUT.java
index 1c0c819..0b905ba 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicLUT.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicLUT.java
@@ -29,8 +29,10 @@
     private Allocation mTables;
     private final byte mCache[] = new byte[1024];
     private boolean mDirty = true;
+    // API level for the intrinsic
+    private static final int INTRINSIC_API_LEVEL = 19;
 
-    protected ScriptIntrinsicLUT(int id, RenderScript rs) {
+    protected ScriptIntrinsicLUT(long id, RenderScript rs) {
         super(id, rs);
     }
 
@@ -45,14 +47,14 @@
      * @return ScriptIntrinsicLUT
      */
     public static ScriptIntrinsicLUT create(RenderScript rs, Element e) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker) rs;
-            return ScriptIntrinsicLUTThunker.create(rs, e);
-        }
+        long id;
+        boolean mUseIncSupp = rs.isUseNative() &&
+                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
 
-        int id = rs.nScriptIntrinsicCreate(3, e.getID(rs));
+        id = rs.nScriptIntrinsicCreate(3, e.getID(rs), mUseIncSupp);
 
         ScriptIntrinsicLUT si = new ScriptIntrinsicLUT(id, rs);
+        si.setIncSupp(mUseIncSupp);
         si.mTables = Allocation.createSized(rs, Element.U8(rs), 1024);
         for (int ct=0; ct < 256; ct++) {
             si.mCache[ct] = (byte)ct;
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicLUTThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicLUTThunker.java
deleted file mode 100644
index ecd17f2..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicLUTThunker.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import android.util.Log;
-
-class ScriptIntrinsicLUTThunker extends ScriptIntrinsicLUT {
-    android.renderscript.ScriptIntrinsicLUT mN;
-
-    android.renderscript.ScriptIntrinsicLUT getNObj() {
-        return mN;
-    }
-
-    private ScriptIntrinsicLUTThunker(int id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    public static ScriptIntrinsicLUTThunker create(RenderScript rs, Element e) {
-        RenderScriptThunker rst = (RenderScriptThunker) rs;
-        ElementThunker et = (ElementThunker) e;
-
-        ScriptIntrinsicLUTThunker si = new ScriptIntrinsicLUTThunker(0, rs);
-        try {
-            si.mN = android.renderscript.ScriptIntrinsicLUT.create(rst.mN, et.getNObj());
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-        return si;
-    }
-
-    public void setRed(int index, int value) {
-        try {
-            mN.setRed(index, value);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setGreen(int index, int value) {
-        try {
-            mN.setGreen(index, value);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setBlue(int index, int value) {
-        try {
-            mN.setBlue(index, value);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void setAlpha(int index, int value) {
-        try {
-            mN.setAlpha(index, value);
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void forEach(Allocation ain, Allocation aout) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        AllocationThunker aoutt = (AllocationThunker)aout;
-        try {
-            mN.forEach(aint.getNObj(), aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelID() {
-        Script.KernelID k = createKernelID(0, 3, null, null);
-        try {
-            k.mN = mN.getKernelID();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicResize.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicResize.java
new file mode 100644
index 0000000..2fdf23e
--- /dev/null
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicResize.java
@@ -0,0 +1,127 @@
+/*
+ * 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.v8.renderscript;
+
+import android.util.Log;
+
+/**
+ * Intrinsic for performing a resize of a 2D allocation.
+ */
+public class ScriptIntrinsicResize extends ScriptIntrinsic {
+    private Allocation mInput;
+    // API level for the intrinsic
+    private static final int INTRINSIC_API_LEVEL = 21;
+
+    protected ScriptIntrinsicResize(long id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    /**
+     * Supported elements types are {@link Element#U8}, {@link
+     * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4}
+     * {@link Element#F32}, {@link Element#F32_2}, {@link
+     * Element#F32_3}, {@link Element#F32_4}
+     *
+     * @param rs The RenderScript context
+     *
+     * @return ScriptIntrinsicResize
+     */
+    public static ScriptIntrinsicResize create(RenderScript rs) {
+        long id;
+        boolean mUseIncSupp = rs.isUseNative() &&
+                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
+
+        id = rs.nScriptIntrinsicCreate(12, 0, mUseIncSupp);
+
+        ScriptIntrinsicResize si = new ScriptIntrinsicResize(id, rs);
+        si.setIncSupp(mUseIncSupp);
+        return si;
+
+    }
+
+    /**
+     * Set the input of the resize.
+     * Must match the element type supplied during create.
+     *
+     * @param ain The input allocation.
+     */
+    public void setInput(Allocation ain) {
+        Element e = ain.getElement();
+        if (!e.isCompatible(Element.U8(mRS)) &&
+            !e.isCompatible(Element.U8_2(mRS)) &&
+            !e.isCompatible(Element.U8_3(mRS)) &&
+            !e.isCompatible(Element.U8_4(mRS)) &&
+            !e.isCompatible(Element.F32(mRS)) &&
+            !e.isCompatible(Element.F32_2(mRS)) &&
+            !e.isCompatible(Element.F32_3(mRS)) &&
+            !e.isCompatible(Element.F32_4(mRS))) {
+            throw new RSIllegalArgumentException("Unsuported element type.");
+        }
+
+        mInput = ain;
+        setVar(0, ain);
+    }
+
+    /**
+     * Get a FieldID for the input field of this intrinsic.
+     *
+     * @return Script.FieldID The FieldID object.
+     */
+    public Script.FieldID getFieldID_Input() {
+        return createFieldID(0, null);
+    }
+
+
+    /**
+     * Resize copy the input allocation to the output specified. The
+     * Allocation is rescaled if necessary using bi-cubic
+     * interpolation.
+     *
+     * @param aout Output allocation. Element type must match
+     *             current input.  Must not be same as input.
+     */
+    public void forEach_bicubic(Allocation aout) {
+        if (aout == mInput) {
+            throw new RSIllegalArgumentException("Output cannot be same as Input.");
+        }
+        forEach_bicubic(aout, null);
+    }
+
+    /**
+     * Resize copy the input allocation to the output specified. The
+     * Allocation is rescaled if necessary using bi-cubic
+     * interpolation.
+     *
+     * @param aout Output allocation. Element type must match
+     *             current input.
+     * @param opt LaunchOptions for clipping
+     */
+    public void forEach_bicubic(Allocation aout, Script.LaunchOptions opt) {
+        forEach(0, (Allocation) null, aout, null, opt);
+    }
+
+    /**
+     * Get a KernelID for this intrinsic kernel.
+     *
+     * @return Script.KernelID The KernelID object.
+     */
+    public Script.KernelID getKernelID_bicubic() {
+        return createKernelID(0, 2, null, null);
+    }
+
+
+}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
index 70e43ba..f3d0df7 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
@@ -26,8 +26,10 @@
  */
 public class ScriptIntrinsicYuvToRGB extends ScriptIntrinsic {
     private Allocation mInput;
+    // API level for the intrinsic
+    private static final int INTRINSIC_API_LEVEL = 19;
 
-    ScriptIntrinsicYuvToRGB(int id, RenderScript rs) {
+    ScriptIntrinsicYuvToRGB(long id, RenderScript rs) {
         super(id, rs);
     }
 
@@ -42,14 +44,15 @@
      * @return ScriptIntrinsicYuvToRGB
      */
     public static ScriptIntrinsicYuvToRGB create(RenderScript rs, Element e) {
-        if (rs.isNative) {
-            RenderScriptThunker rst = (RenderScriptThunker) rs;
-            return ScriptIntrinsicYuvToRGBThunker.create(rs, e);
-        }
-
         // 6 comes from RS_SCRIPT_INTRINSIC_YUV_TO_RGB in rsDefines.h
-        int id = rs.nScriptIntrinsicCreate(6, e.getID(rs));
+        long id;
+        boolean mUseIncSupp = rs.isUseNative() &&
+                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
+
+        id = rs.nScriptIntrinsicCreate(6, e.getID(rs), mUseIncSupp);
+
         ScriptIntrinsicYuvToRGB si = new ScriptIntrinsicYuvToRGB(id, rs);
+        si.setIncSupp(mUseIncSupp);
         return si;
     }
 
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGBThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGBThunker.java
deleted file mode 100644
index 7338469..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGBThunker.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-
-class ScriptIntrinsicYuvToRGBThunker extends ScriptIntrinsicYuvToRGB {
-    android.renderscript.ScriptIntrinsicYuvToRGB mN;
-
-    android.renderscript.ScriptIntrinsicYuvToRGB getNObj() {
-        return mN;
-    }
-
-
-    private ScriptIntrinsicYuvToRGBThunker(int id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    public static ScriptIntrinsicYuvToRGBThunker create(RenderScript rs, Element e) {
-        RenderScriptThunker rst = (RenderScriptThunker) rs;
-        ElementThunker et = (ElementThunker) e;
-
-        ScriptIntrinsicYuvToRGBThunker si = new ScriptIntrinsicYuvToRGBThunker(0, rs);
-        try {
-            si.mN = android.renderscript.ScriptIntrinsicYuvToRGB.create(rst.mN, et.getNObj());
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-        return si;
-    }
-
-
-    public void setInput(Allocation ain) {
-        AllocationThunker aint = (AllocationThunker)ain;
-        try {
-            mN.setInput(aint.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public void forEach(Allocation aout) {
-        AllocationThunker aoutt = (AllocationThunker)aout;
-        try {
-            mN.forEach(aoutt.getNObj());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-    }
-
-    public Script.KernelID getKernelID() {
-        Script.KernelID k = createKernelID(0, 2, null, null);
-        try {
-            k.mN = mN.getKernelID();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return k;
-    }
-
-    public Script.FieldID getFieldID_Input() {
-        Script.FieldID f = createFieldID(0, null);
-        try {
-            f.mN = mN.getFieldID_Input();
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-        return f;
-    }
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Type.java b/v8/renderscript/java/src/android/support/v8/renderscript/Type.java
index a6eeb77..94ff06f 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Type.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/Type.java
@@ -187,10 +187,18 @@
     }
 
 
-    Type(int id, RenderScript rs) {
+    Type(long id, RenderScript rs) {
         super(id, rs);
     }
 
+    /*
+     * Get an identical dummy Type for Compat Context
+     *
+     */
+    public long getDummyType(RenderScript mRS, long eid) {
+        return mRS.nIncTypeCreate(eid, mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces, mDimYuv);
+    }
+
     /**
      * Builder class for Type.
      *
@@ -312,15 +320,10 @@
             }
 
             Type t;
-            if (mRS.isNative) {
-                RenderScriptThunker rst = (RenderScriptThunker)mRS;
-                t = TypeThunker.create(rst, mElement, mDimX, mDimY, mDimZ,
-                                       mDimMipmaps, mDimFaces, mYuv);
-            } else {
-                int id = mRS.nTypeCreate(mElement.getID(mRS),
-                                         mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces, mYuv);
-                t = new Type(id, mRS);
-            }
+            long id = mRS.nTypeCreate(mElement.getID(mRS),
+                                     mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces, mYuv);
+            t = new Type(id, mRS);
+
             t.mElement = mElement;
             t.mDimX = mDimX;
             t.mDimY = mDimY;
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/TypeThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/TypeThunker.java
deleted file mode 100644
index 557c545..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/TypeThunker.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2013 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.v8.renderscript;
-
-import android.graphics.ImageFormat;
-import android.util.Log;
-import java.util.HashMap;
-
-class TypeThunker extends Type {
-    android.renderscript.Type mN;
-
-    android.renderscript.Type getNObj() {
-        return mN;
-    }
-
-    static HashMap<android.renderscript.Type, Type> mMap = new HashMap();
-
-    void internalCalc() {
-        mDimX = mN.getX();
-        mDimY = mN.getY();
-        mDimZ = mN.getZ();
-        mDimFaces = mN.hasFaces();
-        mDimMipmaps = mN.hasMipmaps();
-        mDimYuv = mN.getYuv();
-        calcElementCount();
-    }
-
-    TypeThunker(RenderScript rs, android.renderscript.Type t) {
-        super(0, rs);
-        mN = t;
-        try {
-            internalCalc();
-            mElement = new ElementThunker(rs, t.getElement());
-        } catch (android.renderscript.RSRuntimeException e) {
-            throw ExceptionThunker.convertException(e);
-        }
-
-        synchronized(mMap) {
-            mMap.put(mN, this);
-        }
-    }
-
-    static Type find(android.renderscript.Type nt) {
-        return mMap.get(nt);
-    }
-
-    static Type create(RenderScript rs, Element e,
-                       int dx, int dy, int dz, boolean dmip, boolean dfaces, int yuv) {
-        ElementThunker et = (ElementThunker)e;
-        RenderScriptThunker rst = (RenderScriptThunker)rs;
-        try {
-            android.renderscript.Type.Builder tb =
-                new android.renderscript.Type.Builder(rst.mN, et.mN);
-            if (dx > 0) tb.setX(dx);
-            if (dy > 0) tb.setY(dy);
-            if (dz > 0) tb.setZ(dz);
-            if (dmip) tb.setMipmaps(dmip);
-            if (dfaces) tb.setFaces(dfaces);
-            if (yuv > 0) tb.setYuvFormat(yuv);
-            android.renderscript.Type nt = tb.create();
-            TypeThunker tt = new TypeThunker(rs, nt);
-            tt.internalCalc();
-
-            return tt;
-        } catch (android.renderscript.RSRuntimeException exc) {
-            throw ExceptionThunker.convertException(exc);
-        }
-    }
-}
diff --git a/v8/renderscript/jni/Android.mk b/v8/renderscript/jni/Android.mk
index 9b36b90..d2af4a9 100644
--- a/v8/renderscript/jni/Android.mk
+++ b/v8/renderscript/jni/Android.mk
@@ -2,32 +2,54 @@
 include $(CLEAR_VARS)
 
 LOCAL_CLANG := true
+LOCAL_SDK_VERSION := 14
+
+LOCAL_SRC_FILES:= \
+    android_rscompat_usage_io.cpp \
+    android_rscompat_usage_io_driver.cpp
+
+LOCAL_C_INCLUDES += \
+	$(JNI_H_INCLUDE) \
+	frameworks/rs \
+	frameworks/rs/cpp \
+	frameworks/rs/driver
+
+LOCAL_CFLAGS += -Wno-unused-parameter -U_FORTIFY_SOURCE
+LOCAL_CFLAGS += -DRS_COMPATIBILITY_LIB -std=c++11
+
+LOCAL_MODULE:= libRSSupportIO
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_LDLIBS += -landroid
+LOCAL_NDK_STL_VARIANT := stlport_static
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_CLANG := true
 LOCAL_SDK_VERSION := 8
 
 LOCAL_SRC_FILES:= \
     android_renderscript_RenderScript.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-        libRSSupport \
         libjnigraphics
 
 LOCAL_STATIC_LIBRARIES := \
-        libcutils
-
-rs_generated_include_dir := $(call generated-sources-dir-for,SHARED_LIBRARIES,libRSSupport,,)
+        libcutils \
+        libRSDispatch
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
 	frameworks/rs \
-	$(rs_generated_include_dir)
+	frameworks/rs/cpp
 
-LOCAL_CFLAGS += -Wno-unused-parameter -U_FORTIFY_SOURCE
+LOCAL_CFLAGS += -Wno-unused-parameter -U_FORTIFY_SOURCE -std=c++11
 
-LOCAL_ADDITIONAL_DEPENDENCIES := $(addprefix $(rs_generated_include_dir)/,rsgApiFuncDecl.h)
 LOCAL_MODULE:= librsjni
-LOCAL_ADDITIONAL_DEPENDENCIES += $(rs_generated_source)
 LOCAL_MODULE_TAGS := optional
 LOCAL_REQUIRED_MODULES := libRSSupport
-LOCAL_32_BIT_ONLY := true
+
+LOCAL_LDFLAGS += -ldl
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/v8/renderscript/jni/android_renderscript_RenderScript.cpp b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
index 07374b6..76f1876 100644
--- a/v8/renderscript/jni/android_renderscript_RenderScript.cpp
+++ b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
@@ -23,15 +23,191 @@
 #include <math.h>
 #include <android/bitmap.h>
 #include <android/log.h>
-#include "jni.h"
-#include <rs.h>
+
 #include <rsEnv.h>
+#include "rsDispatch.h"
+#include <dlfcn.h>
 
 //#define LOG_API ALOG
 #define LOG_API(...)
 
 #define NELEM(m) (sizeof(m) / sizeof((m)[0]))
 
+template <typename... T>
+void UNUSED(T... t) {}
+#define PER_ARRAY_TYPE(flag, fnc, readonly, ...) {                                      \
+    jint len = 0;                                                                       \
+    void *ptr = nullptr;                                                                \
+    void *srcPtr = nullptr;                                                             \
+    size_t typeBytes = 0;                                                               \
+    jint relFlag = 0;                                                                   \
+    if (readonly) {                                                                     \
+        /* The on-release mode should only be JNI_ABORT for read-only accesses. */      \
+        /* readonly = true, also indicates we are copying to the allocation   . */      \
+        relFlag = JNI_ABORT;                                                            \
+    }                                                                                   \
+    switch(dataType) {                                                                  \
+    case RS_TYPE_FLOAT_32:                                                              \
+        len = _env->GetArrayLength((jfloatArray)data);                                  \
+        ptr = _env->GetFloatArrayElements((jfloatArray)data, flag);                     \
+        typeBytes = 4;                                                                  \
+        if (usePadding) {                                                               \
+            srcPtr = ptr;                                                               \
+            len = len / 3 * 4;                                                          \
+            if (count == 0) {                                                           \
+                count = len / 4;                                                        \
+            }                                                                           \
+            ptr = malloc (len * typeBytes);                                             \
+            if (readonly) {                                                             \
+                copyWithPadding(ptr, srcPtr, mSize, count);                             \
+                fnc(__VA_ARGS__);                                                       \
+            } else {                                                                    \
+                fnc(__VA_ARGS__);                                                       \
+                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
+            }                                                                           \
+            free(ptr);                                                                  \
+            ptr = srcPtr;                                                               \
+        } else {                                                                        \
+            fnc(__VA_ARGS__);                                                           \
+        }                                                                               \
+        _env->ReleaseFloatArrayElements((jfloatArray)data, (jfloat *)ptr, relFlag);     \
+        return;                                                                         \
+    case RS_TYPE_FLOAT_64:                                                              \
+        len = _env->GetArrayLength((jdoubleArray)data);                                 \
+        ptr = _env->GetDoubleArrayElements((jdoubleArray)data, flag);                   \
+        typeBytes = 8;                                                                  \
+        if (usePadding) {                                                               \
+            srcPtr = ptr;                                                               \
+            len = len / 3 * 4;                                                          \
+            if (count == 0) {                                                           \
+                count = len / 4;                                                        \
+            }                                                                           \
+            ptr = malloc (len * typeBytes);                                             \
+            if (readonly) {                                                             \
+                copyWithPadding(ptr, srcPtr, mSize, count);                             \
+                fnc(__VA_ARGS__);                                                       \
+            } else {                                                                    \
+                fnc(__VA_ARGS__);                                                       \
+                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
+            }                                                                           \
+            free(ptr);                                                                  \
+            ptr = srcPtr;                                                               \
+        } else {                                                                        \
+            fnc(__VA_ARGS__);                                                           \
+        }                                                                               \
+        _env->ReleaseDoubleArrayElements((jdoubleArray)data, (jdouble *)ptr, relFlag);  \
+        return;                                                                         \
+    case RS_TYPE_SIGNED_8:                                                              \
+    case RS_TYPE_UNSIGNED_8:                                                            \
+        len = _env->GetArrayLength((jbyteArray)data);                                   \
+        ptr = _env->GetByteArrayElements((jbyteArray)data, flag);                       \
+        typeBytes = 1;                                                                  \
+        if (usePadding) {                                                               \
+            srcPtr = ptr;                                                               \
+            len = len / 3 * 4;                                                          \
+            if (count == 0) {                                                           \
+                count = len / 4;                                                        \
+            }                                                                           \
+            ptr = malloc (len * typeBytes);                                             \
+            if (readonly) {                                                             \
+                copyWithPadding(ptr, srcPtr, mSize, count);                             \
+                fnc(__VA_ARGS__);                                                       \
+            } else {                                                                    \
+                fnc(__VA_ARGS__);                                                       \
+                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
+            }                                                                           \
+            free(ptr);                                                                  \
+            ptr = srcPtr;                                                               \
+        } else {                                                                        \
+            fnc(__VA_ARGS__);                                                           \
+        }                                                                               \
+        _env->ReleaseByteArrayElements((jbyteArray)data, (jbyte*)ptr, relFlag);         \
+        return;                                                                         \
+    case RS_TYPE_SIGNED_16:                                                             \
+    case RS_TYPE_UNSIGNED_16:                                                           \
+        len = _env->GetArrayLength((jshortArray)data);                                  \
+        ptr = _env->GetShortArrayElements((jshortArray)data, flag);                     \
+        typeBytes = 2;                                                                  \
+        if (usePadding) {                                                               \
+            srcPtr = ptr;                                                               \
+            len = len / 3 * 4;                                                          \
+            if (count == 0) {                                                           \
+                count = len / 4;                                                        \
+            }                                                                           \
+            ptr = malloc (len * typeBytes);                                             \
+            if (readonly) {                                                             \
+                copyWithPadding(ptr, srcPtr, mSize, count);                             \
+                fnc(__VA_ARGS__);                                                       \
+            } else {                                                                    \
+                fnc(__VA_ARGS__);                                                       \
+                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
+            }                                                                           \
+            free(ptr);                                                                  \
+            ptr = srcPtr;                                                               \
+        } else {                                                                        \
+            fnc(__VA_ARGS__);                                                           \
+        }                                                                               \
+        _env->ReleaseShortArrayElements((jshortArray)data, (jshort *)ptr, relFlag);     \
+        return;                                                                         \
+    case RS_TYPE_SIGNED_32:                                                             \
+    case RS_TYPE_UNSIGNED_32:                                                           \
+        len = _env->GetArrayLength((jintArray)data);                                    \
+        ptr = _env->GetIntArrayElements((jintArray)data, flag);                         \
+        typeBytes = 4;                                                                  \
+        if (usePadding) {                                                               \
+            srcPtr = ptr;                                                               \
+            len = len / 3 * 4;                                                          \
+            if (count == 0) {                                                           \
+                count = len / 4;                                                        \
+            }                                                                           \
+            ptr = malloc (len * typeBytes);                                             \
+            if (readonly) {                                                             \
+                copyWithPadding(ptr, srcPtr, mSize, count);                             \
+                fnc(__VA_ARGS__);                                                       \
+            } else {                                                                    \
+                fnc(__VA_ARGS__);                                                       \
+                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
+            }                                                                           \
+            free(ptr);                                                                  \
+            ptr = srcPtr;                                                               \
+        } else {                                                                        \
+            fnc(__VA_ARGS__);                                                           \
+        }                                                                               \
+        _env->ReleaseIntArrayElements((jintArray)data, (jint *)ptr, relFlag);           \
+        return;                                                                         \
+    case RS_TYPE_SIGNED_64:                                                             \
+    case RS_TYPE_UNSIGNED_64:                                                           \
+        len = _env->GetArrayLength((jlongArray)data);                                   \
+        ptr = _env->GetLongArrayElements((jlongArray)data, flag);                       \
+        typeBytes = 8;                                                                  \
+        if (usePadding) {                                                               \
+            srcPtr = ptr;                                                               \
+            len = len / 3 * 4;                                                          \
+            if (count == 0) {                                                           \
+                count = len / 4;                                                        \
+            }                                                                           \
+            ptr = malloc (len * typeBytes);                                             \
+            if (readonly) {                                                             \
+                copyWithPadding(ptr, srcPtr, mSize, count);                             \
+                fnc(__VA_ARGS__);                                                       \
+            } else {                                                                    \
+                fnc(__VA_ARGS__);                                                       \
+                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
+            }                                                                           \
+            free(ptr);                                                                  \
+            ptr = srcPtr;                                                               \
+        } else {                                                                        \
+            fnc(__VA_ARGS__);                                                           \
+        }                                                                               \
+        _env->ReleaseLongArrayElements((jlongArray)data, (jlong *)ptr, relFlag);        \
+        return;                                                                         \
+    default:                                                                            \
+        break;                                                                          \
+    }                                                                                   \
+    UNUSED(len, ptr, srcPtr, typeBytes, relFlag);                                       \
+}
+
+
 class AutoJavaStringToUTF8 {
 public:
     AutoJavaStringToUTF8(JNIEnv* env, jstring str) : fEnv(env), fJStr(str) {
@@ -87,89 +263,249 @@
     jsize        mStringsLength;
 };
 
-// ---------------------------------------------------------------------------
 
-static void
-nContextFinish(JNIEnv *_env, jobject _this, RsContext con)
-{
-    LOG_API("nContextFinish, con(%p)", con);
-    rsContextFinish(con);
+// ---------------------------------------------------------------------------
+static dispatchTable dispatchTab;
+// Incremental Support lib
+static dispatchTable dispatchTabInc;
+
+static jboolean nLoadSO(JNIEnv *_env, jobject _this, jboolean useNative) {
+    void* handle = NULL;
+    if (useNative) {
+        handle = dlopen("libRS.so", RTLD_LAZY | RTLD_LOCAL);
+    } else {
+        handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
+    }
+    if (handle == NULL) {
+        LOG_API("couldn't dlopen %s, %s", filename, dlerror());
+        return false;
+    }
+
+    if (loadSymbols(handle, dispatchTab) == false) {
+        LOG_API("%s init failed!", filename);
+        return false;
+    }
+    LOG_API("Successfully loaded %s", filename);
+    return true;
 }
 
-static void
-nObjDestroy(JNIEnv *_env, jobject _this, RsContext con, jint obj)
-{
-    LOG_API("nObjDestroy, con(%p) obj(%p)", con, (void *)obj);
-    rsObjDestroy(con, (void *)obj);
+static ioSuppDT ioDispatch;
+static jboolean nLoadIOSO(JNIEnv *_env, jobject _this) {
+    void* handleIO = NULL;
+    handleIO = dlopen("libRSSupportIO.so", RTLD_LAZY | RTLD_LOCAL);
+    if (handleIO == NULL) {
+        LOG_API("Couldn't load libRSSupportIO.so");
+        return false;
+    }
+    if (loadIOSuppSyms(handleIO, ioDispatch) == false) {
+        LOG_API("libRSSupportIO init failed!");
+        return false;
+    }
+    return true;
 }
 
 // ---------------------------------------------------------------------------
 
-static jint
+static void copyWithPadding(void* ptr, void* srcPtr, int mSize, int count) {
+    int sizeBytesPad = mSize * 4;
+    int sizeBytes = mSize * 3;
+    uint8_t *dst = static_cast<uint8_t *>(ptr);
+    uint8_t *src = static_cast<uint8_t *>(srcPtr);
+    for (int i = 0; i < count; i++) {
+        memcpy(dst, src, sizeBytes);
+        dst += sizeBytesPad;
+        src += sizeBytes;
+    }
+}
+
+static void copyWithUnPadding(void* ptr, void* srcPtr, int mSize, int count) {
+    int sizeBytesPad = mSize * 4;
+    int sizeBytes = mSize * 3;
+    uint8_t *dst = static_cast<uint8_t *>(ptr);
+    uint8_t *src = static_cast<uint8_t *>(srcPtr);
+    for (int i = 0; i < count; i++) {
+        memcpy(dst, src, sizeBytes);
+        dst += sizeBytes;
+        src += sizeBytesPad;
+    }
+}
+
+
+// ---------------------------------------------------------------------------
+
+static void
+nContextFinish(JNIEnv *_env, jobject _this, jlong con)
+{
+    LOG_API("nContextFinish, con(%p)", (RsContext)con);
+    dispatchTab.ContextFinish((RsContext)con);
+}
+
+static jlong
+nClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong kernelID,
+               jlong returnValue, jlongArray fieldIDArray,
+               jlongArray valueArray, jintArray sizeArray,
+               jlongArray depClosureArray, jlongArray depFieldIDArray) {
+  LOG_API("nClosureCreate: con(%p)", con);
+  jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
+  jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
+  RsScriptFieldID* fieldIDs =
+      (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
+  for (int i = 0; i< fieldIDs_length; i++) {
+    fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
+  }
+
+  jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
+  jsize values_length = _env->GetArrayLength(valueArray);
+  uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
+  for (int i = 0; i < values_length; i++) {
+    values[i] = (uintptr_t)jValues[i];
+  }
+
+  jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
+  jsize sizes_length = _env->GetArrayLength(sizeArray);
+
+  jlong* jDepClosures =
+      _env->GetLongArrayElements(depClosureArray, nullptr);
+  jsize depClosures_length = _env->GetArrayLength(depClosureArray);
+  RsClosure* depClosures =
+      (RsClosure*)alloca(sizeof(RsClosure) * depClosures_length);
+  for (int i = 0; i < depClosures_length; i++) {
+    depClosures[i] = (RsClosure)jDepClosures[i];
+  }
+
+  jlong* jDepFieldIDs =
+      _env->GetLongArrayElements(depFieldIDArray, nullptr);
+  jsize depFieldIDs_length = _env->GetArrayLength(depFieldIDArray);
+  RsScriptFieldID* depFieldIDs =
+      (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * depFieldIDs_length);
+  for (int i = 0; i < depClosures_length; i++) {
+    depFieldIDs[i] = (RsClosure)jDepFieldIDs[i];
+  }
+
+  return (jlong)(uintptr_t)dispatchTab.ClosureCreate(
+      (RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
+      fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
+      (size_t*)sizes, (size_t)sizes_length,
+      depClosures, (size_t)depClosures_length,
+      depFieldIDs, (size_t)depFieldIDs_length);
+}
+
+static void
+nClosureSetArg(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
+               jint index, jlong value, jint size) {
+  dispatchTab.ClosureSetArg((RsContext)con, (RsClosure)closureID,
+                            (uint32_t)index, (uintptr_t)value, (size_t)size);
+}
+
+static void
+nClosureSetGlobal(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
+                  jlong fieldID, jlong value, jint size) {
+  dispatchTab.ClosureSetGlobal((RsContext)con, (RsClosure)closureID,
+                               (RsScriptFieldID)fieldID, (uintptr_t)value,
+                               (size_t)size);
+}
+
+static long
+nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con,
+                    jlongArray closureArray) {
+  jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
+  jsize numClosures = _env->GetArrayLength(closureArray);
+  RsClosure* closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
+  for (int i = 0; i < numClosures; i++) {
+    closures[i] = (RsClosure)jClosures[i];
+  }
+
+  return (jlong)(uintptr_t)dispatchTab.ScriptGroup2Create((RsContext)con,
+                                                          closures,
+                                                          numClosures);
+}
+
+static void
+nObjDestroy(JNIEnv *_env, jobject _this, jlong con, jlong obj)
+{
+    LOG_API("nObjDestroy, con(%p) obj(%p)", (RsContext)con, (void *)obj);
+    dispatchTab.ObjDestroy((RsContext)con, (void *)obj);
+}
+
+// ---------------------------------------------------------------------------
+static jlong
 nDeviceCreate(JNIEnv *_env, jobject _this)
 {
     LOG_API("nDeviceCreate");
-    return (jint)rsDeviceCreate();
+    return (jlong)(uintptr_t)dispatchTab.DeviceCreate();
 }
 
 static void
-nDeviceDestroy(JNIEnv *_env, jobject _this, jint dev)
+nDeviceDestroy(JNIEnv *_env, jobject _this, jlong dev)
 {
     LOG_API("nDeviceDestroy");
-    return rsDeviceDestroy((RsDevice)dev);
+    return dispatchTab.DeviceDestroy((RsDevice)dev);
 }
 
 static void
-nDeviceSetConfig(JNIEnv *_env, jobject _this, jint dev, jint p, jint value)
+nDeviceSetConfig(JNIEnv *_env, jobject _this, jlong dev, jint p, jint value)
 {
     LOG_API("nDeviceSetConfig  dev(%p), param(%i), value(%i)", (void *)dev, p, value);
-    return rsDeviceSetConfig((RsDevice)dev, (RsDeviceParam)p, value);
+    return dispatchTab.DeviceSetConfig((RsDevice)dev, (RsDeviceParam)p, value);
 }
 
-static jint
-nContextCreate(JNIEnv *_env, jobject _this, jint dev, jint ver, jint sdkVer, jint ct)
+static jlong
+nContextCreate(JNIEnv *_env, jobject _this, jlong dev, jint ver, jint sdkVer,
+               jint ct, jstring nativeLibDirJava)
 {
     LOG_API("nContextCreate");
-    return (jint)rsContextCreate((RsDevice)dev, ver, sdkVer, (RsContextType)ct, 0);
+    // Access the NativeLibDir in the Java Context.
+    const char * nativeLibDir = _env->GetStringUTFChars(nativeLibDirJava, JNI_FALSE);
+    size_t length = (size_t)_env->GetStringUTFLength(nativeLibDirJava);
+
+    jlong id = (jlong)(uintptr_t)dispatchTab.ContextCreate((RsDevice)dev, ver,
+                                                           sdkVer,
+                                                           (RsContextType)ct, 0);
+    if (dispatchTab.SetNativeLibDir) {
+        dispatchTab.SetNativeLibDir((RsContext)id, nativeLibDir, length);
+    }
+
+    _env->ReleaseStringUTFChars(nativeLibDirJava, nativeLibDir);
+    return id;
 }
 
 
 static void
-nContextSetPriority(JNIEnv *_env, jobject _this, RsContext con, jint p)
+nContextSetPriority(JNIEnv *_env, jobject _this, jlong con, jint p)
 {
-    LOG_API("ContextSetPriority, con(%p), priority(%i)", con, p);
-    rsContextSetPriority(con, p);
+    LOG_API("ContextSetPriority, con(%p), priority(%i)", (RsContext)con, p);
+    dispatchTab.ContextSetPriority((RsContext)con, p);
 }
 
 
 
 static void
-nContextDestroy(JNIEnv *_env, jobject _this, RsContext con)
+nContextDestroy(JNIEnv *_env, jobject _this, jlong con)
 {
-    LOG_API("nContextDestroy, con(%p)", con);
-    rsContextDestroy(con);
+    LOG_API("nContextDestroy, con(%p)", (RsContext)con);
+    dispatchTab.ContextDestroy((RsContext)con);
 }
 
 static void
-nContextDump(JNIEnv *_env, jobject _this, RsContext con, jint bits)
+nContextDump(JNIEnv *_env, jobject _this, jlong con, jint bits)
 {
     LOG_API("nContextDump, con(%p)  bits(%i)", (RsContext)con, bits);
-    rsContextDump((RsContext)con, bits);
+    dispatchTab.ContextDump((RsContext)con, bits);
 }
 
 
 static jstring
-nContextGetErrorMessage(JNIEnv *_env, jobject _this, RsContext con)
+nContextGetErrorMessage(JNIEnv *_env, jobject _this, jlong con)
 {
-    LOG_API("nContextGetErrorMessage, con(%p)", con);
+    LOG_API("nContextGetErrorMessage, con(%p)", (RsContext)con);
     char buf[1024];
 
     size_t receiveLen;
     uint32_t subID;
-    int id = rsContextGetMessage(con,
-                                 buf, sizeof(buf),
-                                 &receiveLen, sizeof(receiveLen),
-                                 &subID, sizeof(subID));
+    int id = dispatchTab.ContextGetMessage((RsContext)con,
+                                           buf, sizeof(buf),
+                                           &receiveLen, sizeof(receiveLen),
+                                           &subID, sizeof(subID));
     if (!id && receiveLen) {
         //        __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG,
         //            "message receive buffer too small.  %zu", receiveLen);
@@ -178,54 +514,54 @@
 }
 
 static jint
-nContextGetUserMessage(JNIEnv *_env, jobject _this, RsContext con, jintArray data)
+nContextGetUserMessage(JNIEnv *_env, jobject _this, jlong con, jintArray data)
 {
     jint len = _env->GetArrayLength(data);
-    LOG_API("nContextGetMessage, con(%p), len(%i)", con, len);
+    LOG_API("nContextGetMessage, con(%p), len(%i)", (RsContext)con, len);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
     size_t receiveLen;
     uint32_t subID;
-    int id = rsContextGetMessage(con,
-                                 ptr, len * 4,
-                                 &receiveLen, sizeof(receiveLen),
-                                 &subID, sizeof(subID));
+    int id = dispatchTab.ContextGetMessage((RsContext)con,
+                                           ptr, len * 4,
+                                           &receiveLen, sizeof(receiveLen),
+                                           &subID, sizeof(subID));
     if (!id && receiveLen) {
         //        __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG,
         //            "message receive buffer too small.  %zu", receiveLen);
     }
     _env->ReleaseIntArrayElements(data, ptr, 0);
-    return id;
+    return (jint)id;
 }
 
 static jint
-nContextPeekMessage(JNIEnv *_env, jobject _this, RsContext con, jintArray auxData)
+nContextPeekMessage(JNIEnv *_env, jobject _this, jlong con, jintArray auxData)
 {
-    LOG_API("nContextPeekMessage, con(%p)", con);
+    LOG_API("nContextPeekMessage, con(%p)", (RsContext)con);
     jint *auxDataPtr = _env->GetIntArrayElements(auxData, NULL);
     size_t receiveLen;
     uint32_t subID;
-    int id = rsContextPeekMessage(con, &receiveLen, sizeof(receiveLen),
+    int id = dispatchTab.ContextPeekMessage((RsContext)con, &receiveLen, sizeof(receiveLen),
                                   &subID, sizeof(subID));
     auxDataPtr[0] = (jint)subID;
     auxDataPtr[1] = (jint)receiveLen;
     _env->ReleaseIntArrayElements(auxData, auxDataPtr, 0);
-    return id;
+    return (jint)id;
 }
 
-static void nContextInitToClient(JNIEnv *_env, jobject _this, RsContext con)
+static void nContextInitToClient(JNIEnv *_env, jobject _this, jlong con)
 {
-    LOG_API("nContextInitToClient, con(%p)", con);
-    rsContextInitToClient(con);
+    LOG_API("nContextInitToClient, con(%p)", (RsContext)con);
+    dispatchTab.ContextInitToClient((RsContext)con);
 }
 
-static void nContextDeinitToClient(JNIEnv *_env, jobject _this, RsContext con)
+static void nContextDeinitToClient(JNIEnv *_env, jobject _this, jlong con)
 {
-    LOG_API("nContextDeinitToClient, con(%p)", con);
-    rsContextDeinitToClient(con);
+    LOG_API("nContextDeinitToClient, con(%p)", (RsContext)con);
+    dispatchTab.ContextDeinitToClient((RsContext)con);
 }
 
 static void
-nContextSendMessage(JNIEnv *_env, jobject _this, RsContext con, jint id, jintArray data)
+nContextSendMessage(JNIEnv *_env, jobject _this, jlong con, jint id, jintArray data)
 {
     jint *ptr = NULL;
     jint len = 0;
@@ -233,8 +569,8 @@
         len = _env->GetArrayLength(data);
         jint *ptr = _env->GetIntArrayElements(data, NULL);
     }
-    LOG_API("nContextSendMessage, con(%p), id(%i), len(%i)", con, id, len);
-    rsContextSendMessage(con, id, (const uint8_t *)ptr, len * sizeof(int));
+    LOG_API("nContextSendMessage, con(%p), id(%i), len(%i)", (RsContext)con, id, len);
+    dispatchTab.ContextSendMessage((RsContext)con, id, (const uint8_t *)ptr, len * sizeof(int));
     if (data) {
         _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
     }
@@ -242,59 +578,78 @@
 
 
 
-static jint
-nElementCreate(JNIEnv *_env, jobject _this, RsContext con, jint type, jint kind, jboolean norm, jint size)
+static jlong
+nElementCreate(JNIEnv *_env, jobject _this, jlong con, jlong type, jint kind,
+               jboolean norm, jint size)
 {
-    LOG_API("nElementCreate, con(%p), type(%i), kind(%i), norm(%i), size(%i)", con, type, kind, norm, size);
-    return (jint)rsElementCreate(con, (RsDataType)type, (RsDataKind)kind, norm, size);
+    LOG_API("nElementCreate, con(%p), type(%i), kind(%i), norm(%i), size(%i)", (RsContext)con,
+            type, kind, norm, size);
+    return (jlong)(uintptr_t)dispatchTab.ElementCreate((RsContext)con,
+                                                       (RsDataType)type,
+                                                       (RsDataKind)kind,
+                                                       norm, size);
 }
 
-static jint
-nElementCreate2(JNIEnv *_env, jobject _this, RsContext con,
-                jintArray _ids, jobjectArray _names, jintArray _arraySizes)
+static jlong
+nElementCreate2(JNIEnv *_env, jobject _this, jlong con,
+                jlongArray _ids, jobjectArray _names, jintArray _arraySizes)
 {
     int fieldCount = _env->GetArrayLength(_ids);
-    LOG_API("nElementCreate2, con(%p)", con);
+    LOG_API("nElementCreate2, con(%p)", (RsContext)con);
 
-    jint *ids = _env->GetIntArrayElements(_ids, NULL);
-    jint *arraySizes = _env->GetIntArrayElements(_arraySizes, NULL);
+    jlong *jIds = _env->GetLongArrayElements(_ids, NULL);
+    jint *jArraySizes = _env->GetIntArrayElements(_arraySizes, NULL);
+
+    RsElement *ids = (RsElement*)malloc(fieldCount * sizeof(RsElement));
+    uint32_t *arraySizes = (uint32_t *)malloc(fieldCount * sizeof(uint32_t));
+
+    for(int i = 0; i < fieldCount; i ++) {
+        ids[i] = (RsElement)jIds[i];
+        arraySizes[i] = (uint32_t)jArraySizes[i];
+    }
 
     AutoJavaStringArrayToUTF8 names(_env, _names, fieldCount);
 
     const char **nameArray = names.c_str();
     size_t *sizeArray = names.c_str_len();
 
-    jint id = (jint)rsElementCreate2(con,
-                                     (RsElement *)ids, fieldCount,
-                                     nameArray, fieldCount * sizeof(size_t),  sizeArray,
-                                     (const uint32_t *)arraySizes, fieldCount);
+    jlong id = (jlong)(uintptr_t)dispatchTab.ElementCreate2((RsContext)con, (RsElement *)ids,
+                                                            fieldCount, nameArray,
+                                                            fieldCount * sizeof(size_t),  sizeArray,
+                                                            (const uint32_t *)arraySizes, fieldCount);
 
-    _env->ReleaseIntArrayElements(_ids, ids, JNI_ABORT);
-    _env->ReleaseIntArrayElements(_arraySizes, arraySizes, JNI_ABORT);
-    return (jint)id;
+    free(ids);
+    free(arraySizes);
+    _env->ReleaseLongArrayElements(_ids, jIds, JNI_ABORT);
+    _env->ReleaseIntArrayElements(_arraySizes, jArraySizes, JNI_ABORT);
+    return id;
 }
 
 
 
+
 static void
-nElementGetSubElements(JNIEnv *_env, jobject _this, RsContext con, jint id,
-                       jintArray _IDs,
+nElementGetSubElements(JNIEnv *_env, jobject _this, jlong con, jlong id,
+                       jlongArray _IDs,
                        jobjectArray _names,
                        jintArray _arraySizes)
 {
-    int dataSize = _env->GetArrayLength(_IDs);
-    LOG_API("nElementGetSubElements, con(%p)", con);
+    uint32_t dataSize = _env->GetArrayLength(_IDs);
+    LOG_API("nElementGetSubElements, con(%p)", (RsContext)con);
 
-    uint32_t *ids = (uint32_t *)malloc((uint32_t)dataSize * sizeof(uint32_t));
+    uintptr_t *ids = (uintptr_t *)malloc(dataSize * sizeof(uintptr_t));
     const char **names = (const char **)malloc((uint32_t)dataSize * sizeof(const char *));
     uint32_t *arraySizes = (uint32_t *)malloc((uint32_t)dataSize * sizeof(uint32_t));
 
-    rsaElementGetSubElements(con, (RsElement)id, ids, names, arraySizes, (uint32_t)dataSize);
+    dispatchTab.ElementGetSubElements((RsContext)con, (RsElement)id, ids, names, arraySizes,
+                                      (uint32_t)dataSize);
 
-    for(jint i = 0; i < dataSize; i++) {
+    for(uint32_t i = 0; i < dataSize; i++) {
+        const jlong id = (jlong)(uintptr_t)ids[i];
+        const jint arraySize = (jint)arraySizes[i];
         _env->SetObjectArrayElement(_names, i, _env->NewStringUTF(names[i]));
-        _env->SetIntArrayRegion(_IDs, i, 1, (const jint*)&ids[i]);
-        _env->SetIntArrayRegion(_arraySizes, i, 1, (const jint*)&arraySizes[i]);
+        _env->SetLongArrayRegion(_IDs, i, 1, &id);
+        _env->SetIntArrayRegion(_arraySizes, i, 1, &arraySize);
     }
 
     free(ids);
@@ -304,38 +659,54 @@
 
 // -----------------------------------
 
-static int
-nTypeCreate(JNIEnv *_env, jobject _this, RsContext con, RsElement eid,
+static jlong
+nTypeCreate(JNIEnv *_env, jobject _this, jlong con, jlong eid,
             jint dimx, jint dimy, jint dimz, jboolean mips, jboolean faces, jint yuv)
 {
     LOG_API("nTypeCreate, con(%p) eid(%p), x(%i), y(%i), z(%i), mips(%i), faces(%i), yuv(%i)",
-            con, eid, dimx, dimy, dimz, mips, faces, yuv);
+            (RsContext)con, eid, dimx, dimy, dimz, mips, faces, yuv);
 
-    jint id = (jint)rsTypeCreate(con, (RsElement)eid, dimx, dimy, dimz, mips, faces, yuv);
-    return (jint)id;
+    return (jlong)(uintptr_t)dispatchTab.TypeCreate((RsContext)con, (RsElement)eid, dimx, dimy,
+                                                    dimz, mips, faces, yuv);
 }
 
 // -----------------------------------
 
-static jint
-nAllocationCreateTyped(JNIEnv *_env, jobject _this, RsContext con, jint type, jint mips, jint usage, jint pointer)
+static jlong
+nAllocationCreateTyped(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mips, jint usage,
+                       jlong pointer)
 {
-    LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i), ptr(%p)", con, (RsElement)type, mips, usage, (void *)pointer);
-    return (jint) rsAllocationCreateTyped(con, (RsType)type, (RsAllocationMipmapControl)mips, (uint32_t)usage, (uint32_t)pointer);
+    LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i), ptr(%p)",
+            (RsContext)con, (RsElement)type, mips, usage, (void *)pointer);
+    return (jlong)(uintptr_t) dispatchTab.AllocationCreateTyped((RsContext)con, (RsType)type,
+                                                                (RsAllocationMipmapControl)mips,
+                                                                (uint32_t)usage, (uintptr_t)pointer);
 }
 
 static void
-nAllocationSyncAll(JNIEnv *_env, jobject _this, RsContext con, jint a, jint bits)
+nAllocationSyncAll(JNIEnv *_env, jobject _this, jlong con, jlong a, jint bits)
 {
-    LOG_API("nAllocationSyncAll, con(%p), a(%p), bits(0x%08x)", con, (RsAllocation)a, bits);
-    rsAllocationSyncAll(con, (RsAllocation)a, (RsAllocationUsageType)bits);
+    LOG_API("nAllocationSyncAll, con(%p), a(%p), bits(0x%08x)", (RsContext)con, (RsAllocation)a, bits);
+    dispatchTab.AllocationSyncAll((RsContext)con, (RsAllocation)a, (RsAllocationUsageType)bits);
 }
 
 static void
-nAllocationGenerateMipmaps(JNIEnv *_env, jobject _this, RsContext con, jint alloc)
+nAllocationSetSurface(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject sur)
 {
-    LOG_API("nAllocationGenerateMipmaps, con(%p), a(%p)", con, (RsAllocation)alloc);
-    rsAllocationGenerateMipmaps(con, (RsAllocation)alloc);
+    ioDispatch.sAllocationSetSurface(_env, _this, (RsContext)con, (RsAllocation)alloc, sur, dispatchTab);
+}
+
+static void
+nAllocationIoSend(JNIEnv *_env, jobject _this, jlong con, jlong alloc)
+{
+    dispatchTab.AllocationIoSend((RsContext)con, (RsAllocation)alloc);
+}
+
+static void
+nAllocationGenerateMipmaps(JNIEnv *_env, jobject _this, jlong con, jlong alloc)
+{
+    LOG_API("nAllocationGenerateMipmaps, con(%p), a(%p)", (RsContext)con, (RsAllocation)alloc);
+    dispatchTab.AllocationGenerateMipmaps((RsContext)con, (RsAllocation)alloc);
 }
 
 static size_t GetBitmapSize(JNIEnv *env, jobject jbitmap) {
@@ -351,56 +722,67 @@
     return s;
 }
 
-static int
-nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, RsContext con, jint type, jint mip, jobject jbitmap, jint usage)
+static jlong
+nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mip,
+                            jobject jbitmap, jint usage)
 {
-    jint id = 0;
+    jlong id = 0;
     void *pixels = NULL;
     AndroidBitmap_lockPixels(_env, jbitmap, &pixels);
 
     if (pixels != NULL) {
-        id = (jint)rsAllocationCreateFromBitmap(con,
-                                                (RsType)type, (RsAllocationMipmapControl)mip,
-                                                pixels, GetBitmapSize(_env, jbitmap), usage);
+        id = (jlong)(uintptr_t)dispatchTab.AllocationCreateFromBitmap((RsContext)con,
+                                                                      (RsType)type,
+                                                                      (RsAllocationMipmapControl)mip,
+                                                                      pixels,
+                                                                      GetBitmapSize(_env, jbitmap),
+                                                                      usage);
         AndroidBitmap_unlockPixels(_env, jbitmap);
     }
     return id;
 }
 
-static int
-nAllocationCreateBitmapBackedAllocation(JNIEnv *_env, jobject _this, RsContext con, jint type, jint mip, jobject jbitmap, jint usage)
+static jlong
+nAllocationCreateBitmapBackedAllocation(JNIEnv *_env, jobject _this, jlong con, jlong type,
+                                        jint mip, jobject jbitmap, jint usage)
 {
-    jint id = 0;
+    jlong id = 0;
     void *pixels = NULL;
     AndroidBitmap_lockPixels(_env, jbitmap, &pixels);
 
     if (pixels != NULL) {
-        id = (jint)rsAllocationCreateTyped(con,
-                                          (RsType)type, (RsAllocationMipmapControl)mip,
-                                          (uint32_t)usage, (uintptr_t)pixels);
+        id = (jlong)(uintptr_t)dispatchTab.AllocationCreateTyped((RsContext)con,
+                                                                 (RsType)type,
+                                                                 (RsAllocationMipmapControl)mip,
+                                                                 (uint32_t)usage,
+                                                                 (uintptr_t)pixels);
         AndroidBitmap_unlockPixels(_env, jbitmap);
     }
     return id;
 }
 
-static int
-nAllocationCubeCreateFromBitmap(JNIEnv *_env, jobject _this, RsContext con, jint type, jint mip, jobject jbitmap, jint usage)
+static jlong
+nAllocationCubeCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type,
+                                jint mip, jobject jbitmap, jint usage)
 {
     void *pixels = NULL;
     AndroidBitmap_lockPixels(_env, jbitmap, &pixels);
 
-    jint id = 0;
+    jlong id = 0;
     if (pixels != NULL) {
-        id = (jint)rsAllocationCubeCreateFromBitmap(con,
-                                                    (RsType)type, (RsAllocationMipmapControl)mip,
-                                                    pixels, GetBitmapSize(_env, jbitmap), usage);
+        id = (jlong)(uintptr_t)dispatchTab.AllocationCubeCreateFromBitmap((RsContext)con,
+                                                                          (RsType)type,
+                                                                          (RsAllocationMipmapControl)mip,
+                                                                          pixels,
+                                                                          GetBitmapSize(_env, jbitmap),
+                                                                          usage);
         AndroidBitmap_unlockPixels(_env, jbitmap);
     }
     return id;
 }
 
 static void
-nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jobject jbitmap)
+nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap)
 {
     AndroidBitmapInfo info;
     memset(&info, 0, sizeof(info));
@@ -410,15 +792,15 @@
     AndroidBitmap_lockPixels(_env, jbitmap, &pixels);
 
     if (pixels != NULL) {
-        rsAllocation2DData(con, (RsAllocation)alloc, 0, 0,
-                           0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X,
-                           info.width, info.height, pixels, GetBitmapSize(_env, jbitmap), 0);
+        dispatchTab.Allocation2DData((RsContext)con, (RsAllocation)alloc, 0, 0, 0,
+                                     RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, info.width,
+                                     info.height, pixels, GetBitmapSize(_env, jbitmap), 0);
         AndroidBitmap_unlockPixels(_env, jbitmap);
     }
 }
 
 static void
-nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jobject jbitmap)
+nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap)
 {
     AndroidBitmapInfo info;
     memset(&info, 0, sizeof(info));
@@ -428,335 +810,350 @@
     AndroidBitmap_lockPixels(_env, jbitmap, &pixels);
 
     if (pixels != NULL) {
-        rsAllocationCopyToBitmap(con, (RsAllocation)alloc, pixels, GetBitmapSize(_env, jbitmap));
+        dispatchTab.AllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, pixels,
+                                           GetBitmapSize(_env, jbitmap));
         AndroidBitmap_unlockPixels(_env, jbitmap);
     }
     //bitmap.notifyPixelsChanged();
 }
 
-
+// Copies from the Java object data into the Allocation pointed to by _alloc.
 static void
-nAllocationData1D_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint lod, jint count, jintArray data, int sizeBytes)
+nAllocationData1D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint offset, jint lod,
+                  jint count, jobject data, jint sizeBytes, jint dataType, jint mSize,
+                  jboolean usePadding)
 {
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation1DData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
-    jint *ptr = _env->GetIntArrayElements(data, NULL);
-    rsAllocation1DData(con, (RsAllocation)alloc, offset, lod, count, ptr, sizeBytes);
-    _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+    RsAllocation *alloc = (RsAllocation *)_alloc;
+    LOG_API("nAllocation1DData, con(%p), adapter(%p), offset(%i), count(%i), sizeBytes(%i), "
+            "dataType(%i)", (RsContext)con, (RsAllocation)alloc, offset, count, sizeBytes,
+            dataType);
+    PER_ARRAY_TYPE(nullptr, dispatchTab.Allocation1DData, true,
+                   (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes);
 }
 
-static void
-nAllocationData1D_s(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint lod, jint count, jshortArray data, int sizeBytes)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation1DData_s, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
-    jshort *ptr = _env->GetShortArrayElements(data, NULL);
-    rsAllocation1DData(con, (RsAllocation)alloc, offset, lod, count, ptr, sizeBytes);
-    _env->ReleaseShortArrayElements(data, ptr, JNI_ABORT);
-}
 
 static void
-nAllocationData1D_b(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint lod, jint count, jbyteArray data, int sizeBytes)
+nAllocationElementData1D(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jint xoff,
+                         jint lod, jint compIdx, jbyteArray data, jint sizeBytes)
 {
     jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation1DData_b, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
-    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
-    rsAllocation1DData(con, (RsAllocation)alloc, offset, lod, count, ptr, sizeBytes);
+    LOG_API("nAllocationElementData1D, con(%p), alloc(%p), xoff(%i), comp(%i), len(%i), "
+            "sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, compIdx, len,
+            sizeBytes);
+    jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+    dispatchTab.Allocation1DElementData((RsContext)con, (RsAllocation)alloc, xoff,
+                                        lod, ptr, sizeBytes, compIdx);
     _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
 }
 
+/*
 static void
-nAllocationData1D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint lod, jint count, jfloatArray data, int sizeBytes)
+nAllocationElementData(JNIEnv *_env, jobject _this, jlong con, jlong alloc,
+                       jint xoff, jint yoff, jint zoff,
+                       jint lod, jint compIdx, jbyteArray data, jint sizeBytes)
 {
     jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation1DData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
-    jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
-    rsAllocation1DData(con, (RsAllocation)alloc, offset, lod, count, ptr, sizeBytes);
-    _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
-}
-
-static void
-//    native void rsnAllocationElementData1D(int con, int id, int xoff, int compIdx, byte[] d, int sizeBytes);
-nAllocationElementData1D(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint lod, jint compIdx, jbyteArray data, int sizeBytes)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocationElementData1D, con(%p), alloc(%p), offset(%i), comp(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, compIdx, len, sizeBytes);
-    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
-    rsAllocation1DElementData(con, (RsAllocation)alloc, offset, lod, ptr, sizeBytes, compIdx);
+    LOG_API("nAllocationElementData, con(%p), alloc(%p), xoff(%i), yoff(%i), zoff(%i), comp(%i), len(%i), "
+            "sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, yoff, zoff, compIdx, len,
+            sizeBytes);
+    jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+    dispatchTab.AllocationElementData((RsContext)con, (RsAllocation)alloc,
+                                      xoff, yoff, zoff,
+                                      lod, ptr, sizeBytes, compIdx);
     _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
 }
+*/
 
+// Copies from the Java object data into the Allocation pointed to by _alloc.
 static void
-nAllocationData2D_s(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint lod, jint face,
-                    jint w, jint h, jshortArray data, int sizeBytes)
+nAllocationData2D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint lod, jint _face,
+                  jint w, jint h, jobject data, jint sizeBytes, jint dataType, jint mSize,
+                  jboolean usePadding)
 {
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation2DData_s, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
-    jshort *ptr = _env->GetShortArrayElements(data, NULL);
-    rsAllocation2DData(con, (RsAllocation)alloc, xoff, yoff, lod, (RsAllocationCubemapFace)face, w, h, ptr, sizeBytes, 0);
-    _env->ReleaseShortArrayElements(data, ptr, JNI_ABORT);
+    RsAllocation *alloc = (RsAllocation *)_alloc;
+    RsAllocationCubemapFace face = (RsAllocationCubemapFace)_face;
+    LOG_API("nAllocation2DData, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i) "
+            "type(%i)", (RsContext)con, alloc, xoff, yoff, w, h, sizeBytes, dataType);
+    int count = w * h;
+    PER_ARRAY_TYPE(nullptr, dispatchTab.Allocation2DData, true,
+                   (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0);
 }
 
 static void
-nAllocationData2D_b(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint lod, jint face,
-                    jint w, jint h, jbyteArray data, int sizeBytes)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation2DData_b, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
-    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
-    rsAllocation2DData(con, (RsAllocation)alloc, xoff, yoff, lod, (RsAllocationCubemapFace)face, w, h, ptr, sizeBytes, 0);
-    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
-}
-
-static void
-nAllocationData2D_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint lod, jint face,
-                    jint w, jint h, jintArray data, int sizeBytes)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation2DData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
-    jint *ptr = _env->GetIntArrayElements(data, NULL);
-    rsAllocation2DData(con, (RsAllocation)alloc, xoff, yoff, lod, (RsAllocationCubemapFace)face, w, h, ptr, sizeBytes, 0);
-    _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
-}
-
-static void
-nAllocationData2D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint lod, jint face,
-                    jint w, jint h, jfloatArray data, int sizeBytes)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation2DData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
-    jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
-    rsAllocation2DData(con, (RsAllocation)alloc, xoff, yoff, lod, (RsAllocationCubemapFace)face, w, h, ptr, sizeBytes, 0);
-    _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
-}
-
-static void
-nAllocationData2D_alloc(JNIEnv *_env, jobject _this, RsContext con,
-                        jint dstAlloc, jint dstXoff, jint dstYoff,
+nAllocationData2D_alloc(JNIEnv *_env, jobject _this, jlong con,
+                        jlong dstAlloc, jint dstXoff, jint dstYoff,
                         jint dstMip, jint dstFace,
                         jint width, jint height,
-                        jint srcAlloc, jint srcXoff, jint srcYoff,
+                        jlong srcAlloc, jint srcXoff, jint srcYoff,
                         jint srcMip, jint srcFace)
 {
     LOG_API("nAllocation2DData_s, con(%p), dstAlloc(%p), dstXoff(%i), dstYoff(%i),"
             " dstMip(%i), dstFace(%i), width(%i), height(%i),"
             " srcAlloc(%p), srcXoff(%i), srcYoff(%i), srcMip(%i), srcFace(%i)",
-            con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace,
+            (RsContext)con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace,
             width, height, (RsAllocation)srcAlloc, srcXoff, srcYoff, srcMip, srcFace);
 
-    rsAllocationCopy2DRange(con,
-                            (RsAllocation)dstAlloc,
-                            dstXoff, dstYoff,
-                            dstMip, dstFace,
-                            width, height,
-                            (RsAllocation)srcAlloc,
-                            srcXoff, srcYoff,
-                            srcMip, srcFace);
+    dispatchTab.AllocationCopy2DRange((RsContext)con,
+                                      (RsAllocation)dstAlloc,
+                                      dstXoff, dstYoff,
+                                      dstMip, dstFace,
+                                      width, height,
+                                      (RsAllocation)srcAlloc,
+                                      srcXoff, srcYoff,
+                                      srcMip, srcFace);
 }
 
+// Copies from the Java object data into the Allocation pointed to by _alloc.
 static void
-nAllocationData3D_s(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
-                    jint w, jint h, jint d, jshortArray data, int sizeBytes)
+nAllocationData3D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint zoff, jint lod,
+                  jint w, jint h, jint d, jobject data, jint sizeBytes, jint dataType,
+                  jint mSize, jboolean usePadding)
 {
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation3DData_s, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
-    jshort *ptr = _env->GetShortArrayElements(data, NULL);
-    rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
-    _env->ReleaseShortArrayElements(data, ptr, JNI_ABORT);
+    RsAllocation *alloc = (RsAllocation *)_alloc;
+    LOG_API("nAllocation3DData, con(%p), alloc(%p), xoff(%i), yoff(%i), zoff(%i), lod(%i), w(%i),"
+            " h(%i), d(%i), sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, yoff, zoff,
+            lod, w, h, d, sizeBytes);
+    int count = w * h * d;
+    PER_ARRAY_TYPE(nullptr, dispatchTab.Allocation3DData, true,
+                   (RsContext)con, alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
 }
 
 static void
-nAllocationData3D_b(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
-                    jint w, jint h, jint d, jbyteArray data, int sizeBytes)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation3DData_b, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
-    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
-    rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
-    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
-}
-
-static void
-nAllocationData3D_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
-                    jint w, jint h, jint d, jintArray data, int sizeBytes)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation3DData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
-    jint *ptr = _env->GetIntArrayElements(data, NULL);
-    rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
-    _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
-}
-
-static void
-nAllocationData3D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
-                    jint w, jint h, jint d, jfloatArray data, int sizeBytes)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocation3DData_f, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
-    jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
-    rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
-    _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
-}
-
-static void
-nAllocationData3D_alloc(JNIEnv *_env, jobject _this, RsContext con,
-                        jint dstAlloc, jint dstXoff, jint dstYoff, jint dstZoff,
+nAllocationData3D_alloc(JNIEnv *_env, jobject _this, jlong con,
+                        jlong dstAlloc, jint dstXoff, jint dstYoff, jint dstZoff,
                         jint dstMip,
                         jint width, jint height, jint depth,
-                        jint srcAlloc, jint srcXoff, jint srcYoff, jint srcZoff,
+                        jlong srcAlloc, jint srcXoff, jint srcYoff, jint srcZoff,
                         jint srcMip)
 {
     LOG_API("nAllocationData3D_alloc, con(%p), dstAlloc(%p), dstXoff(%i), dstYoff(%i),"
             " dstMip(%i), width(%i), height(%i),"
             " srcAlloc(%p), srcXoff(%i), srcYoff(%i), srcMip(%i)",
-            con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace,
+            (RsContext)con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace,
             width, height, (RsAllocation)srcAlloc, srcXoff, srcYoff, srcMip, srcFace);
 
-    rsAllocationCopy3DRange(con,
-                            (RsAllocation)dstAlloc,
-                            dstXoff, dstYoff, dstZoff, dstMip,
-                            width, height, depth,
-                            (RsAllocation)srcAlloc,
-                            srcXoff, srcYoff, srcZoff, srcMip);
+    dispatchTab.AllocationCopy3DRange((RsContext)con,
+                                      (RsAllocation)dstAlloc,
+                                      dstXoff, dstYoff, dstZoff, dstMip,
+                                      width, height, depth,
+                                      (RsAllocation)srcAlloc,
+                                      srcXoff, srcYoff, srcZoff, srcMip);
 }
 
+// Copies from the Allocation pointed to by _alloc into the Java object data.
 static void
-nAllocationRead_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jintArray data)
+nAllocationRead(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jobject data, jint dataType,
+                jint mSize, jboolean usePadding)
+{
+    RsAllocation *alloc = (RsAllocation *)_alloc;
+    LOG_API("nAllocationRead, con(%p), alloc(%p)", (RsContext)con, (RsAllocation)alloc);
+    int count = 0;
+    PER_ARRAY_TYPE(0, dispatchTab.AllocationRead, false,
+                   (RsContext)con, alloc, ptr, len * typeBytes);
+}
+
+// Copies from the Allocation pointed to by _alloc into the Java object data.
+static void
+nAllocationRead1D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint offset, jint lod,
+                  jint count, jobject data, jint sizeBytes, jint dataType,
+                  jint mSize, jboolean usePadding)
+{
+    RsAllocation *alloc = (RsAllocation *)_alloc;
+    LOG_API("nAllocation1DRead, con(%p), adapter(%p), offset(%i), count(%i), sizeBytes(%i), "
+              "dataType(%i)", (RsContext)con, alloc, offset, count, sizeBytes, dataType);
+    PER_ARRAY_TYPE(0, dispatchTab.Allocation1DRead, false,
+                   (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes);
+}
+
+// Copies from the Element in the Allocation pointed to by _alloc into the Java array data.
+/*
+static void
+nAllocationElementRead(JNIEnv *_env, jobject _this, jlong con, jlong _alloc,
+                       jint xoff, jint yoff, jint zoff,
+                       jint lod, jint compIdx, jobject data, jint sizeBytes)
 {
     jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
-    jint *ptr = _env->GetIntArrayElements(data, NULL);
-    jsize length = _env->GetArrayLength(data);
-    rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(int));
-    _env->ReleaseIntArrayElements(data, ptr, 0);
+    LOG_API("nAllocationElementRead, con(%p), alloc(%p), xoff(%i), yoff(%i), zoff(%i), comp(%i), len(%i), "
+            "sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, yoff, zoff, compIdx, len,
+            sizeBytes);
+    jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+    dispatchTab.AllocationElementRead((RsContext)con, (RsAllocation)alloc,
+                                      xoff, yoff, zoff,
+                                      lod, ptr, sizeBytes, compIdx);
+    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
+}
+*/
+
+// Copies from the Allocation pointed to by _alloc into the Java object data.
+static void
+nAllocationRead2D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint lod, jint _face,
+                  jint w, jint h, jobject data, jint sizeBytes, jint dataType,
+                  jint mSize, jboolean usePadding)
+{
+    RsAllocation *alloc = (RsAllocation *)_alloc;
+    RsAllocationCubemapFace face = (RsAllocationCubemapFace)_face;
+    LOG_API("nAllocation2DRead, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i) "
+              "type(%i)", (RsContext)con, alloc, xoff, yoff, w, h, sizeBytes, dataType);
+    int count = w * h;
+    PER_ARRAY_TYPE(0, dispatchTab.Allocation2DRead, false,
+                   (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0);
+}
+
+// Copies from the Allocation pointed to by _alloc into the Java object data.
+/*
+static void
+nAllocationRead3D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint zoff, jint lod,
+                  jint w, jint h, jint d, jobject data, int sizeBytes, int dataType,
+                  jint mSize, jboolean usePadding)
+{
+    RsAllocation *alloc = (RsAllocation *)_alloc;
+    LOG_API("nAllocation3DRead, con(%p), alloc(%p), xoff(%i), yoff(%i), zoff(%i), lod(%i), w(%i),"
+            " h(%i), d(%i), sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, yoff, zoff,
+            lod, w, h, d, sizeBytes);
+    int count = w * h * d;
+    PER_ARRAY_TYPE(nullptr, dispatchTab.Allocation3DRead, false,
+                   (RsContext)con, alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+}
+*/
+
+static jlong
+nAllocationGetType(JNIEnv *_env, jobject _this, jlong con, jlong a)
+{
+    LOG_API("nAllocationGetType, con(%p), a(%p)", (RsContext)con, (RsAllocation)a);
+    return (jlong)(uintptr_t) dispatchTab.AllocationGetType((RsContext)con, (RsAllocation)a);
 }
 
 static void
-nAllocationRead_s(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jshortArray data)
+nAllocationResize1D(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jint dimX)
 {
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
-    jshort *ptr = _env->GetShortArrayElements(data, NULL);
-    jsize length = _env->GetArrayLength(data);
-    rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(short));
-    _env->ReleaseShortArrayElements(data, ptr, 0);
-}
-
-static void
-nAllocationRead_b(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jbyteArray data)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
-    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
-    jsize length = _env->GetArrayLength(data);
-    rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(char));
-    _env->ReleaseByteArrayElements(data, ptr, 0);
-}
-
-static void
-nAllocationRead_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jfloatArray data)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocationRead_f, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
-    jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
-    jsize length = _env->GetArrayLength(data);
-    rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(float));
-    _env->ReleaseFloatArrayElements(data, ptr, 0);
-}
-
-static jint
-nAllocationGetType(JNIEnv *_env, jobject _this, RsContext con, jint a)
-{
-    LOG_API("nAllocationGetType, con(%p), a(%p)", con, (RsAllocation)a);
-    return (jint) rsaAllocationGetType(con, (RsAllocation)a);
-}
-
-static void
-nAllocationResize1D(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint dimX)
-{
-    LOG_API("nAllocationResize1D, con(%p), alloc(%p), sizeX(%i)", con, (RsAllocation)alloc, dimX);
-    rsAllocationResize1D(con, (RsAllocation)alloc, dimX);
+    LOG_API("nAllocationResize1D, con(%p), alloc(%p), sizeX(%i)", (RsContext)con,
+            (RsAllocation)alloc, dimX);
+    dispatchTab.AllocationResize1D((RsContext)con, (RsAllocation)alloc, dimX);
 }
 
 // -----------------------------------
 
 static void
-nScriptBindAllocation(JNIEnv *_env, jobject _this, RsContext con, jint script, jint alloc, jint slot)
+nScriptBindAllocation(JNIEnv *_env, jobject _this, jlong con, jlong script, jlong alloc, jint slot, jboolean mUseInc)
 {
-    LOG_API("nScriptBindAllocation, con(%p), script(%p), alloc(%p), slot(%i)", con, (RsScript)script, (RsAllocation)alloc, slot);
-    rsScriptBindAllocation(con, (RsScript)script, (RsAllocation)alloc, slot);
+    LOG_API("nScriptBindAllocation, con(%p), script(%p), alloc(%p), slot(%i)",
+            (RsContext)con, (RsScript)script, (RsAllocation)alloc, slot);
+    if (mUseInc) {
+        dispatchTabInc.ScriptBindAllocation((RsContext)con, (RsScript)script, (RsAllocation)alloc, slot);
+    } else {
+        dispatchTab.ScriptBindAllocation((RsContext)con, (RsScript)script, (RsAllocation)alloc, slot);
+    }
 }
 
 static void
-nScriptSetVarI(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jint val)
+nScriptSetVarI(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jint val, jboolean mUseInc)
 {
-    LOG_API("nScriptSetVarI, con(%p), s(%p), slot(%i), val(%i)", con, (void *)script, slot, val);
-    rsScriptSetVarI(con, (RsScript)script, slot, val);
+    LOG_API("nScriptSetVarI, con(%p), s(%p), slot(%i), val(%i)", (RsContext)con,
+            (void *)script, slot, val);
+    if (mUseInc) {
+        dispatchTabInc.ScriptSetVarI((RsContext)con, (RsScript)script, slot, val);
+    } else {
+        dispatchTab.ScriptSetVarI((RsContext)con, (RsScript)script, slot, val);
+    }
 }
 
 static void
-nScriptSetVarObj(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jint val)
+nScriptSetVarObj(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jlong val, jboolean mUseInc)
 {
-    LOG_API("nScriptSetVarObj, con(%p), s(%p), slot(%i), val(%i)", con, (void *)script, slot, val);
-    rsScriptSetVarObj(con, (RsScript)script, slot, (RsObjectBase)val);
+    LOG_API("nScriptSetVarObj, con(%p), s(%p), slot(%i), val(%i)", (RsContext)con,
+            (void *)script, slot, val);
+    if (mUseInc) {
+        dispatchTabInc.ScriptSetVarObj((RsContext)con, (RsScript)script, slot, (RsObjectBase)val);
+    } else {
+        dispatchTab.ScriptSetVarObj((RsContext)con, (RsScript)script, slot, (RsObjectBase)val);
+    }
 }
 
 static void
-nScriptSetVarJ(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jlong val)
+nScriptSetVarJ(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jlong val, jboolean mUseInc)
 {
-    LOG_API("nScriptSetVarJ, con(%p), s(%p), slot(%i), val(%lli)", con, (void *)script, slot, val);
-    rsScriptSetVarJ(con, (RsScript)script, slot, val);
+    LOG_API("nScriptSetVarJ, con(%p), s(%p), slot(%i), val(%lli)", (RsContext)con,
+            (void *)script, slot, val);
+    if (mUseInc) {
+        dispatchTabInc.ScriptSetVarJ((RsContext)con, (RsScript)script, slot, val);
+    } else {
+        dispatchTab.ScriptSetVarJ((RsContext)con, (RsScript)script, slot, val);
+    }
 }
 
 static void
-nScriptSetVarF(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, float val)
+nScriptSetVarF(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, float val, jboolean mUseInc)
 {
-    LOG_API("nScriptSetVarF, con(%p), s(%p), slot(%i), val(%f)", con, (void *)script, slot, val);
-    rsScriptSetVarF(con, (RsScript)script, slot, val);
+    LOG_API("nScriptSetVarF, con(%p), s(%p), slot(%i), val(%f)", (RsContext)con,
+            (void *)script, slot, val);
+    if (mUseInc) {
+        dispatchTabInc.ScriptSetVarF((RsContext)con, (RsScript)script, slot, val);
+    } else {
+        dispatchTab.ScriptSetVarF((RsContext)con, (RsScript)script, slot, val);
+    }
 }
 
 static void
-nScriptSetVarD(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, double val)
+nScriptSetVarD(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, double val, jboolean mUseInc)
 {
-    LOG_API("nScriptSetVarD, con(%p), s(%p), slot(%i), val(%lf)", con, (void *)script, slot, val);
-    rsScriptSetVarD(con, (RsScript)script, slot, val);
+    LOG_API("nScriptSetVarD, con(%p), s(%p), slot(%i), val(%lf)", (RsContext)con,
+            (void *)script, slot, val);
+    if (mUseInc) {
+        dispatchTabInc.ScriptSetVarD((RsContext)con, (RsScript)script, slot, val);
+    } else { 
+        dispatchTab.ScriptSetVarD((RsContext)con, (RsScript)script, slot, val);
+    }
 }
 
 static void
-nScriptSetVarV(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jbyteArray data)
+nScriptSetVarV(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jbyteArray data, jboolean mUseInc)
 {
-    LOG_API("nScriptSetVarV, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+    LOG_API("nScriptSetVarV, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
     jint len = _env->GetArrayLength(data);
     jbyte *ptr = _env->GetByteArrayElements(data, NULL);
-    rsScriptSetVarV(con, (RsScript)script, slot, ptr, len);
+    if (mUseInc) {
+        dispatchTabInc.ScriptSetVarV((RsContext)con, (RsScript)script, slot, ptr, len);
+    } else {
+        dispatchTab.ScriptSetVarV((RsContext)con, (RsScript)script, slot, ptr, len);
+    }
     _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
 }
 
 static void
-nScriptSetVarVE(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jbyteArray data, jint elem, jintArray dims)
+nScriptSetVarVE(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jbyteArray data,
+                jlong elem, jintArray dims, jboolean mUseInc)
 {
-    LOG_API("nScriptSetVarVE, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+    LOG_API("nScriptSetVarVE, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
     jint len = _env->GetArrayLength(data);
     jbyte *ptr = _env->GetByteArrayElements(data, NULL);
     jint dimsLen = _env->GetArrayLength(dims) * sizeof(int);
     jint *dimsPtr = _env->GetIntArrayElements(dims, NULL);
-    rsScriptSetVarVE(con, (RsScript)script, slot, ptr, len, (RsElement)elem,
-                     (const uint32_t *)dimsPtr, dimsLen);
+    if (mUseInc) {
+        dispatchTabInc.ScriptSetVarVE((RsContext)con, (RsScript)script, slot, ptr, len, (RsElement)elem,
+                                      (const uint32_t *)dimsPtr, dimsLen);
+    } else {
+        dispatchTab.ScriptSetVarVE((RsContext)con, (RsScript)script, slot, ptr, len, (RsElement)elem,
+                                   (const uint32_t *)dimsPtr, dimsLen);
+    }
     _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
     _env->ReleaseIntArrayElements(dims, dimsPtr, JNI_ABORT);
 }
 
 
 static void
-nScriptSetTimeZone(JNIEnv *_env, jobject _this, RsContext con, jint script, jbyteArray timeZone)
+nScriptSetTimeZone(JNIEnv *_env, jobject _this, jlong con, jlong script, jbyteArray timeZone, jboolean mUseInc)
 {
-    LOG_API("nScriptCSetTimeZone, con(%p), s(%p), timeZone(%s)", con, (void *)script, (const char *)timeZone);
+    LOG_API("nScriptCSetTimeZone, con(%p), s(%p), timeZone(%s)", (RsContext)con,
+            (void *)script, (const char *)timeZone);
 
     jint length = _env->GetArrayLength(timeZone);
     jbyte* timeZone_ptr;
     timeZone_ptr = (jbyte *) _env->GetPrimitiveArrayCritical(timeZone, (jboolean *)0);
-
-    rsScriptSetTimeZone(con, (RsScript)script, (const char *)timeZone_ptr, length);
+    if (mUseInc) {
+        dispatchTabInc.ScriptSetTimeZone((RsContext)con, (RsScript)script, (const char *)timeZone_ptr, length);
+    } else {
+        dispatchTab.ScriptSetTimeZone((RsContext)con, (RsScript)script, (const char *)timeZone_ptr, length);
+    }
 
     if (timeZone_ptr) {
         _env->ReleasePrimitiveArrayCritical(timeZone, timeZone_ptr, 0);
@@ -764,47 +1161,73 @@
 }
 
 static void
-nScriptInvoke(JNIEnv *_env, jobject _this, RsContext con, jint obj, jint slot)
+nScriptInvoke(JNIEnv *_env, jobject _this, jlong con, jlong obj, jint slot, jboolean mUseInc)
 {
-    LOG_API("nScriptInvoke, con(%p), script(%p)", con, (void *)obj);
-    rsScriptInvoke(con, (RsScript)obj, slot);
+    LOG_API("nScriptInvoke, con(%p), script(%p)", (RsContext)con, (void *)obj);
+    if (mUseInc) {
+        dispatchTabInc.ScriptInvoke((RsContext)con, (RsScript)obj, slot);
+    } else {
+        dispatchTab.ScriptInvoke((RsContext)con, (RsScript)obj, slot);
+    }
 }
 
 static void
-nScriptInvokeV(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jbyteArray data)
+nScriptInvokeV(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jbyteArray data, jboolean mUseInc)
 {
-    LOG_API("nScriptInvokeV, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+    LOG_API("nScriptInvokeV, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
     jint len = _env->GetArrayLength(data);
     jbyte *ptr = _env->GetByteArrayElements(data, NULL);
-    rsScriptInvokeV(con, (RsScript)script, slot, ptr, len);
+    if (mUseInc) {
+        dispatchTabInc.ScriptInvokeV((RsContext)con, (RsScript)script, slot, ptr, len);
+    } else {
+        dispatchTab.ScriptInvokeV((RsContext)con, (RsScript)script, slot, ptr, len);
+    }
     _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
 }
 
 static void
-nScriptForEach(JNIEnv *_env, jobject _this, RsContext con,
-               jint script, jint slot, jint ain, jint aout)
+nScriptForEach(JNIEnv *_env, jobject _this, jlong con, jlong incCon,
+               jlong script, jint slot, jlong ain, jlong aout, jboolean mUseInc)
 {
-    LOG_API("nScriptForEach, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
-    rsScriptForEach(con, (RsScript)script, slot, (RsAllocation)ain, (RsAllocation)aout, NULL, 0, NULL, 0);
+    LOG_API("nScriptForEach, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
+    if (mUseInc) {
+        dispatchTab.ContextFinish((RsContext)con);
+        dispatchTabInc.ScriptForEach((RsContext)incCon, (RsScript)script, slot,
+                                     (RsAllocation)ain, (RsAllocation)aout,
+                                     NULL, 0, NULL, 0);
+    } else {
+        dispatchTab.ScriptForEach((RsContext)con, (RsScript)script, slot,
+                                  (RsAllocation)ain, (RsAllocation)aout,
+                                  NULL, 0, NULL, 0);
+    }
 }
 static void
-nScriptForEachV(JNIEnv *_env, jobject _this, RsContext con,
-                jint script, jint slot, jint ain, jint aout, jbyteArray params)
+nScriptForEachV(JNIEnv *_env, jobject _this, jlong con, jlong incCon,
+                jlong script, jint slot, jlong ain, jlong aout, jbyteArray params, jboolean mUseInc)
 {
-    LOG_API("nScriptForEach, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+    LOG_API("nScriptForEach, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
     jint len = _env->GetArrayLength(params);
     jbyte *ptr = _env->GetByteArrayElements(params, NULL);
-    rsScriptForEach(con, (RsScript)script, slot, (RsAllocation)ain, (RsAllocation)aout, ptr, len, NULL, 0);
+    if (mUseInc) {
+        dispatchTab.ContextFinish((RsContext)con);
+        dispatchTabInc.ScriptForEach((RsContext)incCon, (RsScript)script, slot,
+                                     (RsAllocation)ain, (RsAllocation)aout,
+                                     ptr, len, NULL, 0);
+    } else {
+        dispatchTab.ScriptForEach((RsContext)con, (RsScript)script, slot,
+                                  (RsAllocation)ain, (RsAllocation)aout,
+                                  ptr, len, NULL, 0);
+    }
     _env->ReleaseByteArrayElements(params, ptr, JNI_ABORT);
 }
 
 static void
-nScriptForEachClipped(JNIEnv *_env, jobject _this, RsContext con,
-                      jint script, jint slot, jint ain, jint aout,
+nScriptForEachClipped(JNIEnv *_env, jobject _this, jlong con, jlong incCon,
+                      jlong script, jint slot, jlong ain, jlong aout,
                       jint xstart, jint xend,
-                      jint ystart, jint yend, jint zstart, jint zend)
+                      jint ystart, jint yend, jint zstart, jint zend, jboolean mUseInc)
 {
-    LOG_API("nScriptForEachClipped, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+    LOG_API("nScriptForEachClipped, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
     RsScriptCall sc;
     sc.xStart = xstart;
     sc.xEnd = xend;
@@ -815,16 +1238,25 @@
     sc.strategy = RS_FOR_EACH_STRATEGY_DONT_CARE;
     sc.arrayStart = 0;
     sc.arrayEnd = 0;
-    rsScriptForEach(con, (RsScript)script, slot, (RsAllocation)ain, (RsAllocation)aout, NULL, 0, &sc, sizeof(sc));
+    if (mUseInc) {
+        dispatchTab.ContextFinish((RsContext)con);
+        dispatchTabInc.ScriptForEach((RsContext)incCon, (RsScript)script, slot,
+                                     (RsAllocation)ain, (RsAllocation)aout,
+                                     NULL, 0, &sc, sizeof(sc));
+    } else {
+        dispatchTab.ScriptForEach((RsContext)con, (RsScript)script, slot,
+                                  (RsAllocation)ain, (RsAllocation)aout,
+                                  NULL, 0, &sc, sizeof(sc));
+    }
 }
 
 static void
-nScriptForEachClippedV(JNIEnv *_env, jobject _this, RsContext con,
-                       jint script, jint slot, jint ain, jint aout,
+nScriptForEachClippedV(JNIEnv *_env, jobject _this, jlong con, jlong incCon,
+                       jlong script, jint slot, jlong ain, jlong aout,
                        jbyteArray params, jint xstart, jint xend,
-                       jint ystart, jint yend, jint zstart, jint zend)
+                       jint ystart, jint yend, jint zstart, jint zend, jboolean mUseInc)
 {
-    LOG_API("nScriptForEachClipped, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+    LOG_API("nScriptForEachClipped, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
     jint len = _env->GetArrayLength(params);
     jbyte *ptr = _env->GetByteArrayElements(params, NULL);
     RsScriptCall sc;
@@ -837,22 +1269,31 @@
     sc.strategy = RS_FOR_EACH_STRATEGY_DONT_CARE;
     sc.arrayStart = 0;
     sc.arrayEnd = 0;
-    rsScriptForEach(con, (RsScript)script, slot, (RsAllocation)ain, (RsAllocation)aout, ptr, len, &sc, sizeof(sc));
+    if (mUseInc) {
+        dispatchTab.ContextFinish((RsContext)con);
+        dispatchTabInc.ScriptForEach((RsContext)incCon, (RsScript)script, slot,
+                                     (RsAllocation)ain, (RsAllocation)aout,
+                                     ptr, len, &sc, sizeof(sc));
+    } else {
+        dispatchTab.ScriptForEach((RsContext)con, (RsScript)script, slot,
+                                  (RsAllocation)ain, (RsAllocation)aout,
+                                  ptr, len, &sc, sizeof(sc));
+    }
     _env->ReleaseByteArrayElements(params, ptr, JNI_ABORT);
 }
 
 // -----------------------------------
 
-static jint
-nScriptCCreate(JNIEnv *_env, jobject _this, RsContext con,
+static jlong
+nScriptCCreate(JNIEnv *_env, jobject _this, jlong con,
                jstring resName, jstring cacheDir,
                jbyteArray scriptRef, jint length)
 {
-    LOG_API("nScriptCCreate, con(%p)", con);
+    LOG_API("nScriptCCreate, con(%p)", (RsContext)con);
 
     AutoJavaStringToUTF8 resNameUTF(_env, resName);
     AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
-    jint ret = 0;
+    jlong ret = 0;
     jbyte* script_ptr = NULL;
     jint _exception = 0;
     jint remaining;
@@ -878,10 +1319,10 @@
 
     //rsScriptCSetText(con, (const char *)script_ptr, length);
 
-    ret = (jint)rsScriptCCreate(con,
-                                resNameUTF.c_str(), resNameUTF.length(),
-                                cacheDirUTF.c_str(), cacheDirUTF.length(),
-                                (const char *)script_ptr, length);
+    ret = (jlong)(uintptr_t)dispatchTab.ScriptCCreate((RsContext)con,
+                                                      resNameUTF.c_str(), resNameUTF.length(),
+                                                      cacheDirUTF.c_str(), cacheDirUTF.length(),
+                                                      (const char *)script_ptr, length);
 
 exit:
     if (script_ptr) {
@@ -889,99 +1330,269 @@
                 _exception ? JNI_ABORT: 0);
     }
 
-    return ret;
+    return (jlong)(uintptr_t)ret;
 }
 
-static jint
-nScriptIntrinsicCreate(JNIEnv *_env, jobject _this, RsContext con, jint id, jint eid)
+static jlong
+nScriptIntrinsicCreate(JNIEnv *_env, jobject _this, jlong con, jint id, jlong eid, jboolean mUseInc)
 {
-    LOG_API("nScriptIntrinsicCreate, con(%p) id(%i) element(%p)", con, id, (void *)eid);
-    return (jint)rsScriptIntrinsicCreate(con, id, (RsElement)eid);
+    LOG_API("nScriptIntrinsicCreate, con(%p) id(%i) element(%p)", (RsContext)con, id, (void *)eid);
+    if (mUseInc) {
+        return (jlong)(uintptr_t)dispatchTabInc.ScriptIntrinsicCreate((RsContext)con, id, (RsElement)eid);
+    } else {
+        return (jlong)(uintptr_t)dispatchTab.ScriptIntrinsicCreate((RsContext)con, id, (RsElement)eid);
+    }
 }
 
-static jint
-nScriptKernelIDCreate(JNIEnv *_env, jobject _this, RsContext con, jint sid, jint slot, jint sig)
+static jlong
+nScriptKernelIDCreate(JNIEnv *_env, jobject _this, jlong con, jlong sid, jint slot, jint sig, jboolean mUseInc)
 {
-    LOG_API("nScriptKernelIDCreate, con(%p) script(%p), slot(%i), sig(%i)", con, (void *)sid, slot, sig);
-    return (jint)rsScriptKernelIDCreate(con, (RsScript)sid, slot, sig);
+    LOG_API("nScriptKernelIDCreate, con(%p) script(%p), slot(%i), sig(%i)", (RsContext)con,
+            (void *)sid, slot, sig);
+    if (mUseInc) {
+        return (jlong)(uintptr_t)dispatchTabInc.ScriptKernelIDCreate((RsContext)con, (RsScript)sid,
+                                                                     slot, sig);    
+    } else {
+        return (jlong)(uintptr_t)dispatchTab.ScriptKernelIDCreate((RsContext)con, (RsScript)sid,
+                                                                  slot, sig);
+    }
 }
 
-static jint
-nScriptFieldIDCreate(JNIEnv *_env, jobject _this, RsContext con, jint sid, jint slot)
+static jlong
+nScriptInvokeIDCreate(JNIEnv *_env, jobject _this, jlong con, jlong sid, jint slot)
 {
-    LOG_API("nScriptFieldIDCreate, con(%p) script(%p), slot(%i)", con, (void *)sid, slot);
-    return (jint)rsScriptFieldIDCreate(con, (RsScript)sid, slot);
+    LOG_API("nScriptInvokeIDCreate, con(%p) script(%p), slot(%i), sig(%i)", con,
+            (void *)sid, slot);
+    return (jlong)dispatchTab.ScriptInvokeIDCreate((RsContext)con, (RsScript)sid, slot);
 }
 
-static jint
-nScriptGroupCreate(JNIEnv *_env, jobject _this, RsContext con, jintArray _kernels, jintArray _src,
-    jintArray _dstk, jintArray _dstf, jintArray _types)
+static jlong
+nScriptFieldIDCreate(JNIEnv *_env, jobject _this, jlong con, jlong sid, jint slot, jboolean mUseInc)
 {
-    LOG_API("nScriptGroupCreate, con(%p)", con);
+    LOG_API("nScriptFieldIDCreate, con(%p) script(%p), slot(%i)", (RsContext)con, (void *)sid, slot);
+    if (mUseInc) {
+        return (jlong)(uintptr_t)dispatchTabInc.ScriptFieldIDCreate((RsContext)con, (RsScript)sid, slot);
+    } else {
+        return (jlong)(uintptr_t)dispatchTab.ScriptFieldIDCreate((RsContext)con, (RsScript)sid, slot);
+    }
+}
 
-    jint kernelsLen = _env->GetArrayLength(_kernels) * sizeof(int);
-    jint *kernelsPtr = _env->GetIntArrayElements(_kernels, NULL);
-    jint srcLen = _env->GetArrayLength(_src) * sizeof(int);
-    jint *srcPtr = _env->GetIntArrayElements(_src, NULL);
-    jint dstkLen = _env->GetArrayLength(_dstk) * sizeof(int);
-    jint *dstkPtr = _env->GetIntArrayElements(_dstk, NULL);
-    jint dstfLen = _env->GetArrayLength(_dstf) * sizeof(int);
-    jint *dstfPtr = _env->GetIntArrayElements(_dstf, NULL);
-    jint typesLen = _env->GetArrayLength(_types) * sizeof(int);
-    jint *typesPtr = _env->GetIntArrayElements(_types, NULL);
+static jlong
+nScriptGroupCreate(JNIEnv *_env, jobject _this, jlong con, jlongArray _kernels, jlongArray _src,
+    jlongArray _dstk, jlongArray _dstf, jlongArray _types)
+{
+    LOG_API("nScriptGroupCreate, con(%p)", (RsContext)con);
 
-    int id = (int)rsScriptGroupCreate(con,
-                               (RsScriptKernelID *)kernelsPtr, kernelsLen,
-                               (RsScriptKernelID *)srcPtr, srcLen,
-                               (RsScriptKernelID *)dstkPtr, dstkLen,
-                               (RsScriptFieldID *)dstfPtr, dstfLen,
-                               (RsType *)typesPtr, typesLen);
+    jint kernelsLen = _env->GetArrayLength(_kernels);
+    jlong *jKernelsPtr = _env->GetLongArrayElements(_kernels, nullptr);
+    RsScriptKernelID* kernelsPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * kernelsLen);
+    for(int i = 0; i < kernelsLen; ++i) {
+        kernelsPtr[i] = (RsScriptKernelID)jKernelsPtr[i];
+    }
 
-    _env->ReleaseIntArrayElements(_kernels, kernelsPtr, 0);
-    _env->ReleaseIntArrayElements(_src, srcPtr, 0);
-    _env->ReleaseIntArrayElements(_dstk, dstkPtr, 0);
-    _env->ReleaseIntArrayElements(_dstf, dstfPtr, 0);
-    _env->ReleaseIntArrayElements(_types, typesPtr, 0);
+    jint srcLen = _env->GetArrayLength(_src);
+    jlong *jSrcPtr = _env->GetLongArrayElements(_src, nullptr);
+    RsScriptKernelID* srcPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * srcLen);
+    for(int i = 0; i < srcLen; ++i) {
+        srcPtr[i] = (RsScriptKernelID)jSrcPtr[i];
+    }
+
+    jint dstkLen = _env->GetArrayLength(_dstk);
+    jlong *jDstkPtr = _env->GetLongArrayElements(_dstk, nullptr);
+    RsScriptKernelID* dstkPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstkLen);
+    for(int i = 0; i < dstkLen; ++i) {
+        dstkPtr[i] = (RsScriptKernelID)jDstkPtr[i];
+    }
+
+    jint dstfLen = _env->GetArrayLength(_dstf);
+    jlong *jDstfPtr = _env->GetLongArrayElements(_dstf, nullptr);
+    RsScriptKernelID* dstfPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstfLen);
+    for(int i = 0; i < dstfLen; ++i) {
+        dstfPtr[i] = (RsScriptKernelID)jDstfPtr[i];
+    }
+
+    jint typesLen = _env->GetArrayLength(_types);
+    jlong *jTypesPtr = _env->GetLongArrayElements(_types, nullptr);
+    RsType* typesPtr = (RsType*) malloc(sizeof(RsType) * typesLen);
+    for(int i = 0; i < typesLen; ++i) {
+        typesPtr[i] = (RsType)jTypesPtr[i];
+    }
+
+    jlong id = (jlong)(uintptr_t) dispatchTab.ScriptGroupCreate((RsContext)con,
+                               (RsScriptKernelID *)kernelsPtr, kernelsLen * sizeof(RsScriptKernelID),
+                               (RsScriptKernelID *)srcPtr, srcLen * sizeof(RsScriptKernelID),
+                               (RsScriptKernelID *)dstkPtr, dstkLen * sizeof(RsScriptKernelID),
+                               (RsScriptFieldID *)dstfPtr, dstfLen * sizeof(RsScriptKernelID),
+                               (RsType *)typesPtr, typesLen * sizeof(RsType));
+
+    free(kernelsPtr);
+    free(srcPtr);
+    free(dstkPtr);
+    free(dstfPtr);
+    free(typesPtr);
+    _env->ReleaseLongArrayElements(_kernels, jKernelsPtr, 0);
+    _env->ReleaseLongArrayElements(_src, jSrcPtr, 0);
+    _env->ReleaseLongArrayElements(_dstk, jDstkPtr, 0);
+    _env->ReleaseLongArrayElements(_dstf, jDstfPtr, 0);
+    _env->ReleaseLongArrayElements(_types, jTypesPtr, 0);
     return id;
 }
 
 static void
-nScriptGroupSetInput(JNIEnv *_env, jobject _this, RsContext con, jint gid, jint kid, jint alloc)
+nScriptGroupSetInput(JNIEnv *_env, jobject _this, jlong con, jlong gid, jlong kid, jlong alloc)
 {
-    LOG_API("nScriptGroupSetInput, con(%p) group(%p), kernelId(%p), alloc(%p)", con,
-        (void *)gid, (void *)kid, (void *)alloc);
-    rsScriptGroupSetInput(con, (RsScriptGroup)gid, (RsScriptKernelID)kid, (RsAllocation)alloc);
+    LOG_API("nScriptGroupSetInput, con(%p) group(%p), kernelId(%p), alloc(%p)", (RsContext)con,
+            (void *)gid, (void *)kid, (void *)alloc);
+    dispatchTab.ScriptGroupSetInput((RsContext)con, (RsScriptGroup)gid, (RsScriptKernelID)kid,
+                                    (RsAllocation)alloc);
 }
 
 static void
-nScriptGroupSetOutput(JNIEnv *_env, jobject _this, RsContext con, jint gid, jint kid, jint alloc)
+nScriptGroupSetOutput(JNIEnv *_env, jobject _this, jlong con, jlong gid, jlong kid, jlong alloc)
 {
-    LOG_API("nScriptGroupSetOutput, con(%p) group(%p), kernelId(%p), alloc(%p)", con,
-        (void *)gid, (void *)kid, (void *)alloc);
-    rsScriptGroupSetOutput(con, (RsScriptGroup)gid, (RsScriptKernelID)kid, (RsAllocation)alloc);
+    LOG_API("nScriptGroupSetOutput, con(%p) group(%p), kernelId(%p), alloc(%p)", (RsContext)con,
+            (void *)gid, (void *)kid, (void *)alloc);
+    dispatchTab.ScriptGroupSetOutput((RsContext)con, (RsScriptGroup)gid, (RsScriptKernelID)kid,
+                                     (RsAllocation)alloc);
 }
 
 static void
-nScriptGroupExecute(JNIEnv *_env, jobject _this, RsContext con, jint gid)
+nScriptGroupExecute(JNIEnv *_env, jobject _this, jlong con, jlong gid)
 {
-    LOG_API("nScriptGroupSetOutput, con(%p) group(%p)", con, (void *)gid);
-    rsScriptGroupExecute(con, (RsScriptGroup)gid);
+    LOG_API("nScriptGroupSetOutput, con(%p) group(%p)", (RsContext)con, (void *)gid);
+    dispatchTab.ScriptGroupExecute((RsContext)con, (RsScriptGroup)gid);
 }
 
 // ---------------------------------------------------------------------------
 
-static jint
-nSamplerCreate(JNIEnv *_env, jobject _this, RsContext con, jint magFilter, jint minFilter,
+static jlong
+nSamplerCreate(JNIEnv *_env, jobject _this, jlong con, jint magFilter, jint minFilter,
                jint wrapS, jint wrapT, jint wrapR, jfloat aniso)
 {
-    LOG_API("nSamplerCreate, con(%p)", con);
-    return (jint)rsSamplerCreate(con,
-                                 (RsSamplerValue)magFilter,
-                                 (RsSamplerValue)minFilter,
-                                 (RsSamplerValue)wrapS,
-                                 (RsSamplerValue)wrapT,
-                                 (RsSamplerValue)wrapR,
-                                 aniso);
+    LOG_API("nSamplerCreate, con(%p)", (RsContext)con);
+    return (jlong)(uintptr_t)dispatchTab.SamplerCreate((RsContext)con,
+                                                       (RsSamplerValue)magFilter,
+                                                       (RsSamplerValue)minFilter,
+                                                       (RsSamplerValue)wrapS,
+                                                       (RsSamplerValue)wrapT,
+                                                       (RsSamplerValue)wrapR,
+                                                       aniso);
+}
+
+static jint
+nSystemGetPointerSize(JNIEnv *_env, jobject _this) {
+    return (jint)sizeof(void*);
+}
+
+// ---------------------------------------------------------------------------
+// For Incremental Intrinsic Support
+static bool nIncLoadSO() {
+    void* handle = NULL;
+    handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
+    if (handle == NULL) {
+        LOG_API("couldn't dlopen %s, %s", filename, dlerror());
+        return false;
+    }
+
+    if (loadSymbols(handle, dispatchTabInc) == false) {
+        LOG_API("%s init failed!", filename);
+        return false;
+    }
+    LOG_API("Successfully loaded %s", filename);
+    return true;
+}
+
+// -----------------------------------
+// To create/destroy a dummy context
+static void
+nIncObjDestroy(JNIEnv *_env, jobject _this, jlong con, jlong obj)
+{
+    LOG_API("nObjDestroy, con(%p) obj(%p)", (RsContext)con, (void *)obj);
+    dispatchTabInc.ObjDestroy((RsContext)con, (void *)obj);
+}
+
+
+static jlong
+nIncDeviceCreate(JNIEnv *_env, jobject _this)
+{
+    LOG_API("nDeviceCreate");
+    return (jlong)(uintptr_t)dispatchTabInc.DeviceCreate();
+}
+
+static void
+nIncDeviceDestroy(JNIEnv *_env, jobject _this, jlong dev)
+{
+    LOG_API("nDeviceDestroy");
+    return dispatchTabInc.DeviceDestroy((RsDevice)dev);
+}
+
+static jlong
+nIncContextCreate(JNIEnv *_env, jobject _this, jlong dev, jint ver, jint sdkVer, jint ct)
+{
+    LOG_API("nContextCreate");
+    //The compat context for incremental support will be synchronous.
+    return (jlong)(uintptr_t)dispatchTabInc.ContextCreate((RsDevice)dev, ver, sdkVer,
+                                                          (RsContextType)ct,
+                                                          RS_CONTEXT_SYNCHRONOUS);
+}
+
+static void
+nIncContextFinish(JNIEnv *_env, jobject _this, jlong con)
+{
+    LOG_API("nContextFinish, con(%p)", (RsContext)con);
+    dispatchTabInc.ContextFinish((RsContext)con);
+}
+
+static void
+nIncContextDestroy(JNIEnv *_env, jobject _this, jlong con)
+{
+    LOG_API("nContextDestroy, con(%p)", (RsContext)con);
+    dispatchTabInc.ContextDestroy((RsContext)con);
+}
+
+// -----------------------------------
+// Create dummy Element
+static jlong
+nIncElementCreate(JNIEnv *_env, jobject _this, jlong con, jlong type, jint kind, jboolean norm, jint size)
+{
+    LOG_API("nElementCreate, con(%p), type(%i), kind(%i), norm(%i), size(%i)", (RsContext)con,
+            type, kind, norm, size);
+    return (jlong)(uintptr_t)dispatchTabInc.ElementCreate((RsContext)con, (RsDataType)type,
+                                                          (RsDataKind)kind, norm, size);
+}
+// -----------------------------------
+// Create dummy Type
+static jlong
+nIncTypeCreate(JNIEnv *_env, jobject _this, jlong con, jlong eid,
+            jint dimx, jint dimy, jint dimz, jboolean mips, jboolean faces, jint yuv)
+{
+    LOG_API("nTypeCreate, con(%p) eid(%p), x(%i), y(%i), z(%i), mips(%i), faces(%i), yuv(%i)",
+            incCon, eid, dimx, dimy, dimz, mips, faces, yuv);
+
+    return (jlong)(uintptr_t)dispatchTabInc.TypeCreate((RsContext)con, (RsElement)eid, dimx, dimy,
+                                                       dimz, mips, faces, yuv);
+}
+
+// -----------------------------------
+// Create Allocation from pointer
+static jlong
+nIncAllocationCreateTyped(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong alloc, jlong type)
+{
+    LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i), ptr(%p)",
+            incCon, (RsElement)type, mips, usage, (void *)pointer);
+    size_t strideIn;
+    void* pIn = NULL;
+    RsAllocation ainI = NULL;
+    if (alloc != 0) {
+        pIn = dispatchTab.AllocationGetPointer((RsContext)con, (RsAllocation)alloc, 0,
+                                               RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, 0, 0,
+                                               &strideIn, sizeof(size_t));
+        ainI = dispatchTabInc.AllocationCreateTyped((RsContext)incCon, (RsType)type,
+                                                    RS_ALLOCATION_MIPMAP_NONE,
+                                                    RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
+                                                    (uintptr_t)pIn);
+    }
+    return (jlong)(uintptr_t) ainI;
 }
 
 // ---------------------------------------------------------------------------
@@ -990,91 +1601,105 @@
 static const char *classPathName = "android/support/v8/renderscript/RenderScript";
 
 static JNINativeMethod methods[] = {
-{"nDeviceCreate",                  "()I",                                     (void*)nDeviceCreate },
-{"nDeviceDestroy",                 "(I)V",                                    (void*)nDeviceDestroy },
-{"nDeviceSetConfig",               "(III)V",                                  (void*)nDeviceSetConfig },
-{"nContextGetUserMessage",         "(I[I)I",                                  (void*)nContextGetUserMessage },
-{"nContextGetErrorMessage",        "(I)Ljava/lang/String;",                   (void*)nContextGetErrorMessage },
-{"nContextPeekMessage",            "(I[I)I",                                  (void*)nContextPeekMessage },
-
-{"nContextInitToClient",           "(I)V",                                    (void*)nContextInitToClient },
-{"nContextDeinitToClient",         "(I)V",                                    (void*)nContextDeinitToClient },
+{"nLoadSO",                        "(Z)Z",                                    (bool*)nLoadSO },
+{"nLoadIOSO",                      "()Z",                                     (bool*)nLoadIOSO },
+{"nDeviceCreate",                  "()J",                                     (void*)nDeviceCreate },
+{"nDeviceDestroy",                 "(J)V",                                    (void*)nDeviceDestroy },
+{"nDeviceSetConfig",               "(JII)V",                                  (void*)nDeviceSetConfig },
+{"nContextGetUserMessage",         "(J[I)I",                                  (void*)nContextGetUserMessage },
+{"nContextGetErrorMessage",        "(J)Ljava/lang/String;",                   (void*)nContextGetErrorMessage },
+{"nContextPeekMessage",            "(J[I)I",                                  (void*)nContextPeekMessage },
+{"nContextInitToClient",           "(J)V",                                    (void*)nContextInitToClient },
+{"nContextDeinitToClient",         "(J)V",                                    (void*)nContextDeinitToClient },
 
 
 // All methods below are thread protected in java.
-{"rsnContextCreate",                 "(IIII)I",                               (void*)nContextCreate },
-{"rsnContextFinish",                 "(I)V",                                  (void*)nContextFinish },
-{"rsnContextSetPriority",            "(II)V",                                 (void*)nContextSetPriority },
-{"rsnContextDestroy",                "(I)V",                                  (void*)nContextDestroy },
-{"rsnContextDump",                   "(II)V",                                 (void*)nContextDump },
-{"rsnContextSendMessage",            "(II[I)V",                               (void*)nContextSendMessage },
-{"rsnObjDestroy",                    "(II)V",                                 (void*)nObjDestroy },
+{"rsnContextCreate",                 "(JIIILjava/lang/String;)J",             (void*)nContextCreate },
+{"rsnContextFinish",                 "(J)V",                                  (void*)nContextFinish },
+{"rsnContextSetPriority",            "(JI)V",                                 (void*)nContextSetPriority },
+{"rsnContextDestroy",                "(J)V",                                  (void*)nContextDestroy },
+{"rsnContextDump",                   "(JI)V",                                 (void*)nContextDump },
+{"rsnContextSendMessage",            "(JI[I)V",                               (void*)nContextSendMessage },
+//{"rsnClosureCreate",                 "(JJJ[J[J[I[J[J)J",                      (void*)nClosureCreate },
+//{"rsnClosureSetArg",                 "(JJIJI)V",                              (void*)nClosureSetArg },
+//{"rsnClosureSetGlobal",              "(JJJJI)V",                              (void*)nClosureSetGlobal },
+{"rsnObjDestroy",                    "(JJ)V",                                 (void*)nObjDestroy },
 
-{"rsnElementCreate",                 "(IIIZI)I",                              (void*)nElementCreate },
-{"rsnElementCreate2",                "(I[I[Ljava/lang/String;[I)I",           (void*)nElementCreate2 },
-{"rsnElementGetSubElements",         "(II[I[Ljava/lang/String;[I)V",          (void*)nElementGetSubElements },
+{"rsnElementCreate",                 "(JJIZI)J",                              (void*)nElementCreate },
+{"rsnElementCreate2",                "(J[J[Ljava/lang/String;[I)J",           (void*)nElementCreate2 },
+{"rsnElementGetSubElements",         "(JJ[J[Ljava/lang/String;[I)V",          (void*)nElementGetSubElements },
 
-{"rsnTypeCreate",                    "(IIIIIZZI)I",                           (void*)nTypeCreate },
+{"rsnTypeCreate",                    "(JJIIIZZI)J",                           (void*)nTypeCreate },
 
-{"rsnAllocationCreateTyped",         "(IIIII)I",                               (void*)nAllocationCreateTyped },
-{"rsnAllocationCreateFromBitmap",    "(IIILandroid/graphics/Bitmap;I)I",      (void*)nAllocationCreateFromBitmap },
-{"rsnAllocationCreateBitmapBackedAllocation",    "(IIILandroid/graphics/Bitmap;I)I",      (void*)nAllocationCreateBitmapBackedAllocation },
-{"rsnAllocationCubeCreateFromBitmap","(IIILandroid/graphics/Bitmap;I)I",      (void*)nAllocationCubeCreateFromBitmap },
+{"rsnAllocationCreateTyped",         "(JJIIJ)J",                              (void*)nAllocationCreateTyped },
+{"rsnAllocationCreateFromBitmap",    "(JJILandroid/graphics/Bitmap;I)J",      (void*)nAllocationCreateFromBitmap },
+{"rsnAllocationCreateBitmapBackedAllocation",    "(JJILandroid/graphics/Bitmap;I)J",      (void*)nAllocationCreateBitmapBackedAllocation },
+{"rsnAllocationCubeCreateFromBitmap","(JJILandroid/graphics/Bitmap;I)J",      (void*)nAllocationCubeCreateFromBitmap },
 
-{"rsnAllocationCopyFromBitmap",      "(IILandroid/graphics/Bitmap;)V",        (void*)nAllocationCopyFromBitmap },
-{"rsnAllocationCopyToBitmap",        "(IILandroid/graphics/Bitmap;)V",        (void*)nAllocationCopyToBitmap },
+{"rsnAllocationCopyFromBitmap",      "(JJLandroid/graphics/Bitmap;)V",        (void*)nAllocationCopyFromBitmap },
+{"rsnAllocationCopyToBitmap",        "(JJLandroid/graphics/Bitmap;)V",        (void*)nAllocationCopyToBitmap },
 
-{"rsnAllocationSyncAll",             "(III)V",                                (void*)nAllocationSyncAll },
-{"rsnAllocationData1D",              "(IIIII[II)V",                           (void*)nAllocationData1D_i },
-{"rsnAllocationData1D",              "(IIIII[SI)V",                           (void*)nAllocationData1D_s },
-{"rsnAllocationData1D",              "(IIIII[BI)V",                           (void*)nAllocationData1D_b },
-{"rsnAllocationData1D",              "(IIIII[FI)V",                           (void*)nAllocationData1D_f },
-{"rsnAllocationElementData1D",       "(IIIII[BI)V",                           (void*)nAllocationElementData1D },
-{"rsnAllocationData2D",              "(IIIIIIII[II)V",                        (void*)nAllocationData2D_i },
-{"rsnAllocationData2D",              "(IIIIIIII[SI)V",                        (void*)nAllocationData2D_s },
-{"rsnAllocationData2D",              "(IIIIIIII[BI)V",                        (void*)nAllocationData2D_b },
-{"rsnAllocationData2D",              "(IIIIIIII[FI)V",                        (void*)nAllocationData2D_f },
-{"rsnAllocationData2D",              "(IIIIIIIIIIIII)V",                      (void*)nAllocationData2D_alloc },
-{"rsnAllocationData3D",              "(IIIIIIIII[II)V",                       (void*)nAllocationData3D_i },
-{"rsnAllocationData3D",              "(IIIIIIIII[SI)V",                       (void*)nAllocationData3D_s },
-{"rsnAllocationData3D",              "(IIIIIIIII[BI)V",                       (void*)nAllocationData3D_b },
-{"rsnAllocationData3D",              "(IIIIIIIII[FI)V",                       (void*)nAllocationData3D_f },
-{"rsnAllocationData3D",              "(IIIIIIIIIIIIII)V",                     (void*)nAllocationData3D_alloc },
-{"rsnAllocationRead",                "(II[I)V",                               (void*)nAllocationRead_i },
-{"rsnAllocationRead",                "(II[S)V",                               (void*)nAllocationRead_s },
-{"rsnAllocationRead",                "(II[B)V",                               (void*)nAllocationRead_b },
-{"rsnAllocationRead",                "(II[F)V",                               (void*)nAllocationRead_f },
-{"rsnAllocationGetType",             "(II)I",                                 (void*)nAllocationGetType},
-{"rsnAllocationResize1D",            "(III)V",                                (void*)nAllocationResize1D },
-{"rsnAllocationGenerateMipmaps",     "(II)V",                                 (void*)nAllocationGenerateMipmaps },
+{"rsnAllocationSyncAll",             "(JJI)V",                                (void*)nAllocationSyncAll },
+{"rsnAllocationSetSurface",          "(JJLandroid/view/Surface;)V",           (void*)nAllocationSetSurface },
+{"rsnAllocationIoSend",              "(JJ)V",                                 (void*)nAllocationIoSend },
+{"rsnAllocationData1D",              "(JJIIILjava/lang/Object;IIIZ)V",        (void*)nAllocationData1D },
+{"rsnAllocationElementData1D",       "(JJIII[BI)V",                           (void*)nAllocationElementData1D },
+//{"rsnAllocationElementData",         "(JJIIIII[BI)V",                         (void*)nAllocationElementData },
+{"rsnAllocationData2D",              "(JJIIIIIILjava/lang/Object;IIIZ)V",     (void*)nAllocationData2D },
+{"rsnAllocationData2D",              "(JJIIIIIIJIIII)V",                      (void*)nAllocationData2D_alloc },
+{"rsnAllocationData3D",              "(JJIIIIIIILjava/lang/Object;IIIZ)V",    (void*)nAllocationData3D },
+{"rsnAllocationData3D",              "(JJIIIIIIIJIIII)V",                     (void*)nAllocationData3D_alloc },
+{"rsnAllocationRead",                "(JJLjava/lang/Object;IIZ)V",            (void*)nAllocationRead },
+{"rsnAllocationRead1D",              "(JJIIILjava/lang/Object;IIIZ)V",        (void*)nAllocationRead1D },
+//{"rsnAllocationElementRead",         "(JJIIIII[BI)V",                         (void*)nAllocationElementRead },
+{"rsnAllocationRead2D",              "(JJIIIIIILjava/lang/Object;IIIZ)V",     (void*)nAllocationRead2D },
+//{"rsnAllocationRead3D",              "(JJIIIIIIILjava/lang/Object;IIIZ)V",  (void*)nAllocationRead3D },
+{"rsnAllocationGetType",             "(JJ)J",                                 (void*)nAllocationGetType},
+{"rsnAllocationResize1D",            "(JJI)V",                                (void*)nAllocationResize1D },
+{"rsnAllocationGenerateMipmaps",     "(JJ)V",                                 (void*)nAllocationGenerateMipmaps },
 
-{"rsnScriptBindAllocation",          "(IIII)V",                               (void*)nScriptBindAllocation },
-{"rsnScriptSetTimeZone",             "(II[B)V",                               (void*)nScriptSetTimeZone },
-{"rsnScriptInvoke",                  "(III)V",                                (void*)nScriptInvoke },
-{"rsnScriptInvokeV",                 "(III[B)V",                              (void*)nScriptInvokeV },
-{"rsnScriptForEach",                 "(IIIII)V",                              (void*)nScriptForEach },
-{"rsnScriptForEach",                 "(IIIII[B)V",                            (void*)nScriptForEachV },
-{"rsnScriptForEachClipped",          "(IIIIIIIIIII)V",                        (void*)nScriptForEachClipped },
-{"rsnScriptForEachClipped",          "(IIIII[BIIIIII)V",                      (void*)nScriptForEachClippedV },
-{"rsnScriptSetVarI",                 "(IIII)V",                               (void*)nScriptSetVarI },
-{"rsnScriptSetVarJ",                 "(IIIJ)V",                               (void*)nScriptSetVarJ },
-{"rsnScriptSetVarF",                 "(IIIF)V",                               (void*)nScriptSetVarF },
-{"rsnScriptSetVarD",                 "(IIID)V",                               (void*)nScriptSetVarD },
-{"rsnScriptSetVarV",                 "(III[B)V",                              (void*)nScriptSetVarV },
-{"rsnScriptSetVarVE",                "(III[BI[I)V",                           (void*)nScriptSetVarVE },
-{"rsnScriptSetVarObj",               "(IIII)V",                               (void*)nScriptSetVarObj },
+{"rsnScriptBindAllocation",          "(JJJIZ)V",                              (void*)nScriptBindAllocation },
+{"rsnScriptSetTimeZone",             "(JJ[BZ)V",                              (void*)nScriptSetTimeZone },
+{"rsnScriptInvoke",                  "(JJIZ)V",                               (void*)nScriptInvoke },
+{"rsnScriptInvokeV",                 "(JJI[BZ)V",                             (void*)nScriptInvokeV },
+{"rsnScriptForEach",                 "(JJJIJJZ)V",                            (void*)nScriptForEach },
+{"rsnScriptForEach",                 "(JJJIJJ[BZ)V",                          (void*)nScriptForEachV },
+{"rsnScriptForEachClipped",          "(JJJIJJIIIIIIZ)V",                      (void*)nScriptForEachClipped },
+{"rsnScriptForEachClipped",          "(JJJIJJ[BIIIIIIZ)V",                    (void*)nScriptForEachClippedV },
+{"rsnScriptSetVarI",                 "(JJIIZ)V",                              (void*)nScriptSetVarI },
+{"rsnScriptSetVarJ",                 "(JJIJZ)V",                              (void*)nScriptSetVarJ },
+{"rsnScriptSetVarF",                 "(JJIFZ)V",                              (void*)nScriptSetVarF },
+{"rsnScriptSetVarD",                 "(JJIDZ)V",                              (void*)nScriptSetVarD },
+{"rsnScriptSetVarV",                 "(JJI[BZ)V",                             (void*)nScriptSetVarV },
+{"rsnScriptSetVarVE",                "(JJI[BJ[IZ)V",                          (void*)nScriptSetVarVE },
+{"rsnScriptSetVarObj",               "(JJIJZ)V",                              (void*)nScriptSetVarObj },
 
-{"rsnScriptCCreate",                 "(ILjava/lang/String;Ljava/lang/String;[BI)I",  (void*)nScriptCCreate },
-{"rsnScriptIntrinsicCreate",         "(III)I",                                (void*)nScriptIntrinsicCreate },
-{"rsnScriptKernelIDCreate",          "(IIII)I",                               (void*)nScriptKernelIDCreate },
-{"rsnScriptFieldIDCreate",           "(III)I",                                (void*)nScriptFieldIDCreate },
-{"rsnScriptGroupCreate",             "(I[I[I[I[I[I)I",                        (void*)nScriptGroupCreate },
-{"rsnScriptGroupSetInput",           "(IIII)V",                               (void*)nScriptGroupSetInput },
-{"rsnScriptGroupSetOutput",          "(IIII)V",                               (void*)nScriptGroupSetOutput },
-{"rsnScriptGroupExecute",            "(II)V",                                 (void*)nScriptGroupExecute },
+{"rsnScriptCCreate",                 "(JLjava/lang/String;Ljava/lang/String;[BI)J",  (void*)nScriptCCreate },
+{"rsnScriptIntrinsicCreate",         "(JIJZ)J",                               (void*)nScriptIntrinsicCreate },
+{"rsnScriptKernelIDCreate",          "(JJIIZ)J",                              (void*)nScriptKernelIDCreate },
+{"rsnScriptInvokeIDCreate",          "(JJI)J",                                (void*)nScriptInvokeIDCreate },
+{"rsnScriptFieldIDCreate",           "(JJIZ)J",                               (void*)nScriptFieldIDCreate },
+{"rsnScriptGroupCreate",             "(J[J[J[J[J[J)J",                        (void*)nScriptGroupCreate },
+//{"rsnScriptGroup2Create",            "(J[J)J",                                (void*)nScriptGroup2Create },
+{"rsnScriptGroupSetInput",           "(JJJJ)V",                               (void*)nScriptGroupSetInput },
+{"rsnScriptGroupSetOutput",          "(JJJJ)V",                               (void*)nScriptGroupSetOutput },
+{"rsnScriptGroupExecute",            "(JJ)V",                                 (void*)nScriptGroupExecute },
 
-{"rsnSamplerCreate",                 "(IIIIIIF)I",                            (void*)nSamplerCreate },
+{"rsnSamplerCreate",                 "(JIIIIIF)J",                            (void*)nSamplerCreate },
 
+{"rsnSystemGetPointerSize",          "()I",                                   (void*)nSystemGetPointerSize },
+
+// Entry points for Inc libRSSupport
+{"nIncLoadSO",                       "()Z",                                   (bool*)nIncLoadSO },
+{"nIncDeviceCreate",                 "()J",                                   (void*)nIncDeviceCreate },
+{"nIncDeviceDestroy",                "(J)V",                                  (void*)nIncDeviceDestroy },
+{"rsnIncContextCreate",              "(JIII)J",                               (void*)nIncContextCreate },
+{"rsnIncContextFinish",              "(J)V",                                  (void*)nIncContextFinish },
+{"rsnIncContextDestroy",             "(J)V",                                  (void*)nIncContextDestroy },
+{"rsnIncObjDestroy",                 "(JJ)V",                                 (void*)nIncObjDestroy },
+{"rsnIncElementCreate",              "(JJIZI)J",                              (void*)nIncElementCreate },
+{"rsnIncTypeCreate",                 "(JJIIIZZI)J",                           (void*)nIncTypeCreate },
+{"rsnIncAllocationCreateTyped",      "(JJJJ)J",                               (void*)nIncAllocationCreateTyped },
 };
 
 // ---------------------------------------------------------------------------
diff --git a/v8/renderscript/jni/android_rscompat_usage_io.cpp b/v8/renderscript/jni/android_rscompat_usage_io.cpp
new file mode 100644
index 0000000..e29be1a
--- /dev/null
+++ b/v8/renderscript/jni/android_rscompat_usage_io.cpp
@@ -0,0 +1,20 @@
+#include <android/log.h>
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+
+#include <rsEnv.h>
+#include "rsDispatch.h"
+#define LOG_API(...)
+
+extern "C" void AllocationSetSurface(JNIEnv *_env, jobject _this, RsContext con, RsAllocation alloc, jobject sur, dispatchTable dispatchTab)
+{
+    LOG_API("nAllocationSetSurface, con(%p), alloc(%p), surface(%p)",
+            con, alloc, sur);
+
+    ANativeWindow* s = NULL;
+    if (sur != 0) {
+        s = ANativeWindow_fromSurface(_env, sur);
+    }
+    dispatchTab.AllocationSetSurface(con, alloc, s);
+}
+
diff --git a/v8/renderscript/jni/android_rscompat_usage_io_driver.cpp b/v8/renderscript/jni/android_rscompat_usage_io_driver.cpp
new file mode 100644
index 0000000..96eb19a
--- /dev/null
+++ b/v8/renderscript/jni/android_rscompat_usage_io_driver.cpp
@@ -0,0 +1,113 @@
+#include <android/native_window.h>
+#include <android/log.h>
+
+#include "rsCompatibilityLib.h"
+
+#include "rsdCore.h"
+#include "rsdAllocation.h"
+#include "rsAllocation.h"
+
+#define LOG_API(...)
+
+using namespace android;
+using namespace android::renderscript;
+
+static bool IoGetBuffer(const Context *rsc, Allocation *alloc, ANativeWindow *nw) {
+    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
+    // Must lock the whole surface
+    if(drv->wndBuffer == NULL) {
+        drv->wndBuffer = new ANativeWindow_Buffer;
+    }
+    int32_t r = ANativeWindow_lock(nw, drv->wndBuffer, NULL);
+    if (r) {
+        LOG_API("Error Locking IO output buffer.");
+        return false;
+    }
+
+    void *dst = drv->wndBuffer->bits;
+    alloc->mHal.drvState.lod[0].mallocPtr = dst;
+    alloc->mHal.drvState.lod[0].stride = drv->wndBuffer->stride * alloc->mHal.state.elementSizeBytes;
+    return true;
+}
+
+extern "C" void rscAllocationSetSurface(RsContext rscR, RsAllocation allocR, ANativeWindow *nw) {
+    Context *rsc = (Context *)rscR;
+    Allocation *alloc = (Allocation *)allocR;
+    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
+
+    // Cleanup old surface if there is one.
+    if (drv->wndSurface) {
+        ANativeWindow *old = drv->wndSurface;
+        ANativeWindow_unlockAndPost(old);
+        drv->wndSurface = NULL;
+        ANativeWindow_release(old);
+        old = NULL;
+    }
+
+    if (nw != NULL) {
+        int32_t r;
+        r = ANativeWindow_setBuffersGeometry(nw, alloc->mHal.drvState.lod[0].dimX,
+                                                 alloc->mHal.drvState.lod[0].dimY,
+                                                 WINDOW_FORMAT_RGBA_8888);
+        if (r) {
+            LOG_API("Error setting IO output buffer geometry.");
+            goto errorcmp;
+        }
+
+        IoGetBuffer(rsc, alloc, nw);
+        drv->wndSurface = nw;
+    }
+
+    return;
+
+ errorcmp:
+
+    if (nw) {
+        nw = NULL;
+    }
+
+}
+
+extern "C" void rscAllocationDestroy(const Context *rsc, Allocation *alloc) {
+    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
+    if (alloc->mHal.drvState.lod[0].mallocPtr) {
+        // don't free user-allocated ptrs or IO_OUTPUT buffers
+        if (!(drv->useUserProvidedPtr) &&
+            !(alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_INPUT) &&
+            !(alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT)) {
+                free(alloc->mHal.drvState.lod[0].mallocPtr);
+        }
+        alloc->mHal.drvState.lod[0].mallocPtr = NULL;
+    }
+
+    if ((alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT) &&
+        (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_SCRIPT)) {
+        ANativeWindow *nw = drv->wndSurface;
+        if (nw) {
+            //If we have an attached surface, need to release it.
+            ANativeWindow_unlockAndPost(nw);
+            drv->wndSurface = NULL;
+            ANativeWindow_release(nw);
+            nw = NULL;
+        }
+    }
+}
+
+extern "C" void rscAllocationIoSend(const Context *rsc, Allocation *alloc) {
+    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
+    ANativeWindow *nw = drv->wndSurface;
+    if (nw) {
+        if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_SCRIPT) {
+            int32_t r = ANativeWindow_unlockAndPost(nw);
+            if (r) {
+                LOG_API("Error sending IO output buffer.");
+                return;
+            }
+            IoGetBuffer(rsc, alloc, nw);
+        }
+    } else {
+        LOG_API("Sent IO buffer with no attached surface.");
+        return;
+    }
+}
+
diff --git a/v8/renderscript/rs_support/Android.mk b/v8/renderscript/rs_support/Android.mk
index df236ec..43952d5 100644
--- a/v8/renderscript/rs_support/Android.mk
+++ b/v8/renderscript/rs_support/Android.mk
@@ -1,7 +1,7 @@
 
 LOCAL_PATH:=frameworks/rs
 rs_base_CFLAGS := -Werror -Wall -Wno-unused-parameter -Wno-unused-variable \
-		  -Wno-overloaded-virtual -DRS_COMPATIBILITY_LIB
+		  -Wno-overloaded-virtual -DRS_COMPATIBILITY_LIB -std=c++11
 
 ifeq ($(ARCH_ARM_HAVE_NEON),true)
 rs_base_CFLAGS += -DARCH_ARM_HAVE_NEON
@@ -28,6 +28,9 @@
     spec.l \
     rsg_generator.c
 
+LOCAL_CXX_STL := none
+LOCAL_ADDRESS_SANITIZER := false
+
 include $(BUILD_HOST_EXECUTABLE)
 
 # TODO: This should go into build/core/config.mk
@@ -38,8 +41,6 @@
 LOCAL_MODULE := libRSSupport
 LOCAL_SDK_VERSION := 8
 
-# TODO: remove this once we have 64-bit NDK libraries.
-LOCAL_32_BIT_ONLY := true
 
 LOCAL_MODULE_CLASS := SHARED_LIBRARIES
 generated_sources_dir := $(call local-generated-sources-dir)
@@ -82,11 +83,13 @@
 LOCAL_SRC_FILES:= \
 	rsAdapter.cpp \
 	rsAllocation.cpp \
+	rsClosure.cpp \
 	rsCompatibilityLib.cpp \
 	rsComponent.cpp \
 	rsContext.cpp \
 	rsCppUtils.cpp \
 	rsDevice.cpp \
+	rsDriverLoader.cpp \
 	rsElement.cpp \
 	rsFifoSocket.cpp \
 	rsObjectBase.cpp \
@@ -99,6 +102,7 @@
 	rsScriptC.cpp \
 	rsScriptC_Lib.cpp \
 	rsScriptGroup.cpp \
+	rsScriptGroup2.cpp \
 	rsScriptIntrinsic.cpp \
 	rsSignal.cpp \
 	rsStream.cpp \
@@ -113,10 +117,12 @@
 	driver/rsdScriptGroup.cpp \
 	driver/rsdType.cpp \
 	cpu_ref/rsCpuCore.cpp \
+	cpu_ref/rsCpuExecutable.cpp \
 	cpu_ref/rsCpuScript.cpp \
 	cpu_ref/rsCpuRuntimeMath.cpp \
 	cpu_ref/rsCpuRuntimeStubs.cpp \
 	cpu_ref/rsCpuScriptGroup.cpp \
+	cpu_ref/rsCpuScriptGroup2.cpp \
 	cpu_ref/rsCpuIntrinsic.cpp \
 	cpu_ref/rsCpuIntrinsic3DLUT.cpp \
 	cpu_ref/rsCpuIntrinsicBlend.cpp \
@@ -133,6 +139,8 @@
 ifeq ($(ARCH_ARM_HAVE_ARMV7A),true)
 LOCAL_CFLAGS_arm := -DARCH_ARM_HAVE_VFP -DARCH_ARM_USE_INTRINSICS
 LOCAL_ASFLAGS_arm := -mfpu=neon
+# frameworks/rs/cpu_ref/rsCpuIntrinsics_neon_3DLUT.S does not compile.
+LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
 LOCAL_SRC_FILES_arm := \
         cpu_ref/rsCpuIntrinsics_neon_3DLUT.S \
 	cpu_ref/rsCpuIntrinsics_neon_ColorMatrix.S \