am 1e526b15: am 6ef35156: am 727f8307: Add proguard rules to v7/preference

* commit '1e526b15b8bd2b9c067688e143ab38748adbd008':
  Add proguard rules to v7/preference
diff --git a/customtabs/api/current.txt b/customtabs/api/current.txt
index 0e1745d..9f67246 100644
--- a/customtabs/api/current.txt
+++ b/customtabs/api/current.txt
@@ -8,6 +8,8 @@
     field public static final int NAVIGATION_FAILED = 3; // 0x3
     field public static final int NAVIGATION_FINISHED = 2; // 0x2
     field public static final int NAVIGATION_STARTED = 1; // 0x1
+    field public static final int TAB_HIDDEN = 6; // 0x6
+    field public static final int TAB_SHOWN = 5; // 0x5
   }
 
   public class CustomTabsClient {
@@ -21,9 +23,11 @@
     method public void launchUrl(android.app.Activity, android.net.Uri);
     field public static final java.lang.String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
     field public static final java.lang.String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
+    field public static final java.lang.String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
     field public static final java.lang.String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
     field public static final java.lang.String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
     field public static final java.lang.String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
+    field public static final java.lang.String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
     field public static final java.lang.String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
     field public static final java.lang.String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
     field public static final java.lang.String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
@@ -41,6 +45,8 @@
     ctor public CustomTabsIntent.Builder(android.support.customtabs.CustomTabsSession);
     method public android.support.customtabs.CustomTabsIntent.Builder addMenuItem(java.lang.String, android.app.PendingIntent);
     method public android.support.customtabs.CustomTabsIntent build();
+    method public android.support.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
+    method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent, boolean);
     method public android.support.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, java.lang.String, android.app.PendingIntent);
     method public android.support.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
     method public android.support.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, int, int);
diff --git a/customtabs/src/android/support/customtabs/CustomTabsCallback.java b/customtabs/src/android/support/customtabs/CustomTabsCallback.java
index ea5f3e3..d7fdd39 100644
--- a/customtabs/src/android/support/customtabs/CustomTabsCallback.java
+++ b/customtabs/src/android/support/customtabs/CustomTabsCallback.java
@@ -44,6 +44,16 @@
     public static final int NAVIGATION_ABORTED = 4;
 
     /**
+     * Sent when the tab becomes visible.
+     */
+    public static final int TAB_SHOWN = 5;
+
+    /**
+     * Sent when the tab becomes hidden.
+     */
+    public static final int TAB_HIDDEN = 6;
+
+    /**
      * To be called when a navigation event happens.
      *
      * @param navigationEvent The code corresponding to the navigation event.
diff --git a/customtabs/src/android/support/customtabs/CustomTabsIntent.java b/customtabs/src/android/support/customtabs/CustomTabsIntent.java
index 4a91500..9a85738 100644
--- a/customtabs/src/android/support/customtabs/CustomTabsIntent.java
+++ b/customtabs/src/android/support/customtabs/CustomTabsIntent.java
@@ -60,6 +60,12 @@
             "android.support.customtabs.extra.TOOLBAR_COLOR";
 
     /**
+     * Boolean extra that enables the url bar to hide as the user scrolls down the page
+     */
+    public static final String EXTRA_ENABLE_URLBAR_HIDING =
+            "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
+
+    /**
      * Extra bitmap that specifies the icon of the back button on the toolbar. If the client chooses
      * not to customize it, a default close button will be used.
      */
@@ -112,6 +118,13 @@
             "android.support.customtabs.customaction.PENDING_INTENT";
 
     /**
+     * Extra boolean that specifies whether the custom action button should be tinted. Default is
+     * false and the action button will not be tinted.
+     */
+    public static final String EXTRA_TINT_ACTION_BUTTON =
+            "android.support.customtabs.extra.TINT_ACTION_BUTTON";
+
+    /**
      * Use an {@code ArrayList<Bundle>} for specifying menu related params. There should be a
      * separate {@link Bundle} for each custom menu item.
      */
@@ -204,6 +217,14 @@
         }
 
         /**
+         * Enables the url bar to hide as the user scrolls down on the page.
+         */
+        public Builder enableUrlBarHiding() {
+            mIntent.putExtra(EXTRA_ENABLE_URLBAR_HIDING, true);
+            return this;
+        }
+
+        /**
          * Sets the Close button icon for the custom tab.
          *
          * @param icon The icon {@link Bitmap}
@@ -245,18 +266,29 @@
          * @param icon The icon.
          * @param description The description for the button. To be used for accessibility.
          * @param pendingIntent pending intent delivered when the button is clicked.
+         * @param shouldTint Whether the action button should be tinted.
          */
-        public Builder setActionButton(@NonNull Bitmap icon,
-                @NonNull String description, @NonNull PendingIntent pendingIntent) {
+        public Builder setActionButton(@NonNull Bitmap icon, @NonNull String description,
+                @NonNull PendingIntent pendingIntent, boolean shouldTint) {
             Bundle bundle = new Bundle();
             bundle.putParcelable(KEY_ICON, icon);
             bundle.putString(KEY_DESCRIPTION, description);
             bundle.putParcelable(KEY_PENDING_INTENT, pendingIntent);
             mIntent.putExtra(EXTRA_ACTION_BUTTON_BUNDLE, bundle);
+            mIntent.putExtra(EXTRA_TINT_ACTION_BUTTON, shouldTint);
             return this;
         }
 
         /**
+         * See {@link CustomTabsIntent.Builder#setActionButton(
+         * Bitmap, String, PendingIntent, boolean)}
+         */
+        public Builder setActionButton(@NonNull Bitmap icon, @NonNull String description,
+                @NonNull PendingIntent pendingIntent) {
+            return setActionButton(icon, description, pendingIntent, false);
+        }
+
+        /**
          * Sets the start animations,
          *
          * @param context Application context.
diff --git a/customtabs/src/android/support/customtabs/CustomTabsService.java b/customtabs/src/android/support/customtabs/CustomTabsService.java
index 13c737e..f0e7ea7 100644
--- a/customtabs/src/android/support/customtabs/CustomTabsService.java
+++ b/customtabs/src/android/support/customtabs/CustomTabsService.java
@@ -50,7 +50,7 @@
      public static final String KEY_URL =
              "android.support.customtabs.otherurls.URL";
 
-     private Map<IBinder, DeathRecipient> mDeathRecipientMap = new ArrayMap<>();
+     private final Map<IBinder, DeathRecipient> mDeathRecipientMap = new ArrayMap<>();
 
      private ICustomTabsService.Stub mBinder = new ICustomTabsService.Stub() {
 
diff --git a/design/api/current.txt b/design/api/current.txt
index de4e09b..30da82a 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -61,6 +61,7 @@
     method public int getOverlayTop();
     method public boolean layoutDependsOn(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
     method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
+    method public boolean onLayoutChild(android.support.design.widget.CoordinatorLayout, android.view.View, int);
     method public boolean onMeasureChild(android.support.design.widget.CoordinatorLayout, android.view.View, int, int, int, int);
     method public void setOverlayTop(int);
   }
@@ -84,6 +85,8 @@
     method public void setExpandedTitleColor(int);
     method public void setExpandedTitleGravity(int);
     method public void setExpandedTitleTextAppearance(int);
+    method public void setScrimsShown(boolean);
+    method public void setScrimsShown(boolean, boolean);
     method public void setStatusBarScrim(android.graphics.drawable.Drawable);
     method public void setStatusBarScrimColor(int);
     method public void setStatusBarScrimResource(int);
@@ -178,7 +181,7 @@
     field public static final android.os.Parcelable.Creator<android.support.design.widget.CoordinatorLayout.SavedState> CREATOR;
   }
 
-  public class FloatingActionButton extends android.widget.ImageView {
+  public class FloatingActionButton extends android.widget.ImageButton {
     ctor public FloatingActionButton(android.content.Context);
     ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
     ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
diff --git a/design/base/android/support/design/widget/StateListAnimator.java b/design/base/android/support/design/widget/StateListAnimator.java
index c937b0b..2de5dba 100644
--- a/design/base/android/support/design/widget/StateListAnimator.java
+++ b/design/base/android/support/design/widget/StateListAnimator.java
@@ -124,8 +124,11 @@
         if (mLastMatch != null) {
             cancel();
         }
+
         mLastMatch = match;
-        if (match != null) {
+
+        View view = mViewRef.get();
+        if (match != null && view != null && view.getVisibility() == View.VISIBLE ) {
             start(match);
         }
     }
diff --git a/design/base/android/support/design/widget/ValueAnimatorCompat.java b/design/base/android/support/design/widget/ValueAnimatorCompat.java
index 1e33f66..20bebb6 100644
--- a/design/base/android/support/design/widget/ValueAnimatorCompat.java
+++ b/design/base/android/support/design/widget/ValueAnimatorCompat.java
@@ -104,6 +104,7 @@
         abstract void cancel();
         abstract float getAnimatedFraction();
         abstract void end();
+        abstract long getDuration();
     }
 
     private final Impl mImpl;
@@ -191,4 +192,8 @@
     public void end() {
         mImpl.end();
     }
+
+    public long getDuration() {
+        return mImpl.getDuration();
+    }
 }
diff --git a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
index 6fb3126..fd0b045 100644
--- a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
+++ b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
@@ -23,6 +23,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.LayerDrawable;
+import android.os.Build;
 import android.support.design.R;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.view.View;
@@ -68,7 +69,7 @@
             PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth) {
         // Now we need to tint the original background with the tint, using
         // an InsetDrawable if we have a border width
-        mShapeDrawable = DrawableCompat.wrap(originalBackground.mutate());
+        mShapeDrawable = DrawableCompat.wrap(mutateDrawable(originalBackground));
         DrawableCompat.setTintList(mShapeDrawable, backgroundTint);
         if (backgroundTintMode != null) {
             DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode);
@@ -110,6 +111,15 @@
         updatePadding();
     }
 
+    private static Drawable mutateDrawable(Drawable drawable) {
+        if (Build.VERSION.SDK_INT < 14 && drawable instanceof GradientDrawable) {
+            // GradientDrawable pre-ICS does not copy over it's color when mutated. We just skip
+            // the mutate and hope for the best.
+            return drawable;
+        }
+        return drawable.mutate();
+    }
+
     @Override
     void setBackgroundTintList(ColorStateList tint) {
         DrawableCompat.setTintList(mShapeDrawable, tint);
diff --git a/design/eclair-mr1/android/support/design/widget/ValueAnimatorCompatImplEclairMr1.java b/design/eclair-mr1/android/support/design/widget/ValueAnimatorCompatImplEclairMr1.java
index 42cf086..1c708f5 100644
--- a/design/eclair-mr1/android/support/design/widget/ValueAnimatorCompatImplEclairMr1.java
+++ b/design/eclair-mr1/android/support/design/widget/ValueAnimatorCompatImplEclairMr1.java
@@ -147,6 +147,11 @@
         }
     }
 
+    @Override
+    public long getDuration() {
+        return mDuration;
+    }
+
     private void update() {
         if (mIsRunning) {
             // Update the animated fraction
diff --git a/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java b/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
index 4f9ea39..5ee272b 100644
--- a/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
+++ b/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
@@ -113,4 +113,9 @@
     public void end() {
         mValueAnimator.end();
     }
+
+    @Override
+    public long getDuration() {
+        return mValueAnimator.getDuration();
+    }
 }
diff --git a/design/res/layout/design_layout_tab_icon.xml b/design/res/layout/design_layout_tab_icon.xml
index 6464d1f..5dcfa11 100644
--- a/design/res/layout/design_layout_tab_icon.xml
+++ b/design/res/layout/design_layout_tab_icon.xml
@@ -16,6 +16,6 @@
   -->
 
 <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
-           android:layout_width="wrap_content"
-           android:layout_height="wrap_content"
-           android:layout_gravity="center"/>
\ No newline at end of file
+           android:layout_width="24dp"
+           android:layout_height="24dp"
+           android:scaleType="centerInside"/>
\ No newline at end of file
diff --git a/design/res/layout/design_layout_tab_text.xml b/design/res/layout/design_layout_tab_text.xml
index a83bb3d..89681c5 100644
--- a/design/res/layout/design_layout_tab_text.xml
+++ b/design/res/layout/design_layout_tab_text.xml
@@ -16,7 +16,7 @@
 -->
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
-          android:layout_width="wrap_content"
+          android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:ellipsize="end"
           android:gravity="center"
diff --git a/design/res/layout/design_menu_item_action_area.xml b/design/res/layout/design_menu_item_action_area.xml
new file mode 100644
index 0000000..ba8141d
--- /dev/null
+++ b/design/res/layout/design_menu_item_action_area.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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="wrap_content"
+             android:layout_height="match_parent"/>
diff --git a/design/res/layout/design_navigation_item.xml b/design/res/layout/design_navigation_item.xml
index 3fcd74a..14c1be3 100644
--- a/design/res/layout/design_navigation_item.xml
+++ b/design/res/layout/design_navigation_item.xml
@@ -19,8 +19,4 @@
         android:layout_width="match_parent"
         android:layout_height="?attr/listPreferredItemHeightSmall"
         android:paddingLeft="?attr/listPreferredItemPaddingLeft"
-        android:paddingRight="?attr/listPreferredItemPaddingRight"
-        android:drawablePadding="@dimen/design_navigation_icon_padding"
-        android:gravity="center_vertical|start"
-        android:maxLines="1"
-        android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>
+        android:paddingRight="?attr/listPreferredItemPaddingRight"/>
diff --git a/design/res/layout/design_navigation_menu_item.xml b/design/res/layout/design_navigation_menu_item.xml
new file mode 100644
index 0000000..91104bb1
--- /dev/null
+++ b/design/res/layout/design_navigation_menu_item.xml
@@ -0,0 +1,36 @@
+<?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">
+
+    <CheckedTextView
+            android:id="@+id/design_menu_item_text"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:drawablePadding="@dimen/design_navigation_icon_padding"
+            android:gravity="center_vertical|start"
+            android:maxLines="1"
+            android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>
+
+    <ViewStub
+            android:id="@+id/design_menu_item_action_area_stub"
+            android:inflatedId="@+id/design_menu_item_action_area"
+            android:layout="@layout/design_menu_item_action_area"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"/>
+
+</merge>
diff --git a/design/res/values-sw600dp/dimens.xml b/design/res/values-sw600dp/dimens.xml
index ebbc20e..8dada20 100644
--- a/design/res/values-sw600dp/dimens.xml
+++ b/design/res/values-sw600dp/dimens.xml
@@ -17,7 +17,7 @@
 
 <resources>
 
-    <dimen name="design_tab_min_width">160dp</dimen>
+    <dimen name="design_tab_scrollable_min_width">160dp</dimen>
 
     <dimen name="design_snackbar_min_width">320dp</dimen>
     <dimen name="design_snackbar_max_width">576dp</dimen>
diff --git a/design/res/values/dimens.xml b/design/res/values/dimens.xml
index c7f8cef..59d2f1f 100644
--- a/design/res/values/dimens.xml
+++ b/design/res/values/dimens.xml
@@ -31,8 +31,10 @@
     <dimen name="design_navigation_padding_top_default">0dp</dimen>
     <dimen name="design_navigation_padding_bottom">8dp</dimen>
 
-    <dimen name="design_tab_min_width">72dp</dimen>
+    <dimen name="design_tab_scrollable_min_width">72dp</dimen>
     <dimen name="design_tab_max_width">264dp</dimen>
+    <dimen name="design_tab_text_size">14sp</dimen>
+    <dimen name="design_tab_text_size_2line">12sp</dimen>
 
     <dimen name="design_snackbar_min_width">-1px</dimen>
     <dimen name="design_snackbar_max_width">-1px</dimen>
diff --git a/design/res/values/styles.xml b/design/res/values/styles.xml
index e75d805..fa770aa 100644
--- a/design/res/values/styles.xml
+++ b/design/res/values/styles.xml
@@ -54,7 +54,7 @@
     </style>
 
     <style name="TextAppearance.Design.Tab" parent="TextAppearance.AppCompat.Button">
-        <item name="android:textSize">14sp</item>
+        <item name="android:textSize">@dimen/design_tab_text_size</item>
         <item name="android:textColor">?android:textColorSecondary</item>
         <item name="textAllCaps">true</item>
     </style>
diff --git a/design/src/android/support/design/internal/NavigationMenuItemView.java b/design/src/android/support/design/internal/NavigationMenuItemView.java
index 7813163..1d819df 100644
--- a/design/src/android/support/design/internal/NavigationMenuItemView.java
+++ b/design/src/android/support/design/internal/NavigationMenuItemView.java
@@ -27,19 +27,30 @@
 import android.support.v4.widget.TextViewCompat;
 import android.support.v7.internal.view.menu.MenuItemImpl;
 import android.support.v7.internal.view.menu.MenuView;
+import android.support.v7.widget.LinearLayoutCompat;
 import android.util.AttributeSet;
 import android.util.TypedValue;
-import android.widget.TextView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.CheckedTextView;
+import android.widget.FrameLayout;
 
 /**
  * @hide
  */
-public class NavigationMenuItemView extends TextView implements MenuView.ItemView {
+public class NavigationMenuItemView extends LinearLayoutCompat implements MenuView.ItemView {
 
     private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
 
-    private int mIconSize;
+    private final int mIconSize;
+
+    private final CheckedTextView mTextView;
+
+    private FrameLayout mActionArea;
+
     private MenuItemImpl mItemData;
+
     private ColorStateList mIconTintList;
 
     public NavigationMenuItemView(Context context) {
@@ -52,8 +63,14 @@
 
     public NavigationMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        setOrientation(HORIZONTAL);
+        LayoutInflater.from(context).inflate(R.layout.design_navigation_menu_item, this, true);
         mIconSize = context.getResources().getDimensionPixelSize(
                 R.dimen.design_navigation_icon_size);
+        mTextView = (CheckedTextView) findViewById(R.id.design_menu_item_text);
+        mTextView.setDuplicateParentStateEnabled(true);
+        // Prevent the action view from stealing the event on the item row.
+        setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
     }
 
     @Override
@@ -71,6 +88,18 @@
         setEnabled(itemData.isEnabled());
         setTitle(itemData.getTitle());
         setIcon(itemData.getIcon());
+        setActionView(itemData.getActionView());
+    }
+
+    private void setActionView(View actionView) {
+        if (mActionArea == null) {
+            mActionArea = (FrameLayout) ((ViewStub) findViewById(
+                    R.id.design_menu_item_action_area_stub)).inflate();
+        }
+        mActionArea.removeAllViews();
+        if (actionView != null) {
+            mActionArea.addView(actionView);
+        }
     }
 
     private StateListDrawable createDefaultBackground() {
@@ -91,7 +120,7 @@
 
     @Override
     public void setTitle(CharSequence title) {
-        setText(title);
+        mTextView.setText(title);
     }
 
     @Override
@@ -102,6 +131,7 @@
     @Override
     public void setChecked(boolean checked) {
         refreshDrawableState();
+        mTextView.setChecked(checked);
     }
 
     @Override
@@ -115,7 +145,7 @@
             icon.setBounds(0, 0, mIconSize, mIconSize);
             DrawableCompat.setTintList(icon, mIconTintList);
         }
-        TextViewCompat.setCompoundDrawablesRelative(this, icon, null, null, null);
+        TextViewCompat.setCompoundDrawablesRelative(mTextView, icon, null, null, null);
     }
 
     @Override
@@ -144,4 +174,13 @@
             setIcon(mItemData.getIcon());
         }
     }
+
+    public void setTextAppearance(Context context, int textAppearance) {
+        mTextView.setTextAppearance(context, textAppearance);
+    }
+
+    public void setTextColor(ColorStateList colors) {
+        mTextView.setTextColor(colors);
+    }
+
 }
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
index e523c38..e79a421 100644
--- a/design/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -190,10 +190,10 @@
         if (positionInAdapter >= 0) {
             setUpdateSuspended(true);
             MenuItemImpl item = mAdapter.getItem(positionInAdapter).getMenuItem();
-            if (item != null && item.isCheckable()) {
+            boolean result = mMenu.performItemAction(item, this, 0);
+            if (item != null && item.isCheckable() && result) {
                 mAdapter.setCheckedItem(item);
             }
-            mMenu.performItemAction(item, this, 0);
             setUpdateSuspended(false);
             updateMenuView(false);
         }
@@ -266,6 +266,7 @@
 
         private static final String STATE_CHECKED_ITEM = "android:menu:checked";
 
+        private static final String STATE_ACTION_VIEWS = "android:menu:action_views";
         private static final int VIEW_TYPE_NORMAL = 0;
         private static final int VIEW_TYPE_SUBHEADER = 1;
         private static final int VIEW_TYPE_SEPARATOR = 2;
@@ -470,6 +471,18 @@
             if (mCheckedItem != null) {
                 state.putInt(STATE_CHECKED_ITEM, mCheckedItem.getItemId());
             }
+            // Store the states of the action views.
+            SparseArray<ParcelableSparseArray> actionViewStates = new SparseArray<>();
+            for (NavigationMenuItem navigationMenuItem : mItems) {
+                MenuItemImpl item = navigationMenuItem.getMenuItem();
+                View actionView = item != null ? item.getActionView() : null;
+                if (actionView != null) {
+                    ParcelableSparseArray container = new ParcelableSparseArray();
+                    actionView.saveHierarchyState(container);
+                    actionViewStates.put(item.getItemId(), container);
+                }
+            }
+            state.putSparseParcelableArray(STATE_ACTION_VIEWS, actionViewStates);
             return state;
         }
 
@@ -479,7 +492,7 @@
                 mUpdateSuspended = true;
                 for (NavigationMenuItem item : mItems) {
                     MenuItemImpl menuItem = item.getMenuItem();
-                    if (menuItem !=  null && menuItem.getItemId() == checkedItem) {
+                    if (menuItem != null && menuItem.getItemId() == checkedItem) {
                         setCheckedItem(menuItem);
                         break;
                     }
@@ -487,6 +500,16 @@
                 mUpdateSuspended = false;
                 prepareMenuItems();
             }
+            // Restore the states of the action views.
+            SparseArray<ParcelableSparseArray> actionViewStates = state
+                    .getSparseParcelableArray(STATE_ACTION_VIEWS);
+            for (NavigationMenuItem navigationMenuItem : mItems) {
+                MenuItemImpl item = navigationMenuItem.getMenuItem();
+                View actionView = item != null ? item.getActionView() : null;
+                if (actionView != null) {
+                    actionView.restoreHierarchyState(actionViewStates.get(item.getItemId()));
+                }
+            }
         }
 
         public void setUpdateSuspended(boolean updateSuspended) {
diff --git a/design/src/android/support/design/internal/ParcelableSparseArray.java b/design/src/android/support/design/internal/ParcelableSparseArray.java
new file mode 100644
index 0000000..74abcc2
--- /dev/null
+++ b/design/src/android/support/design/internal/ParcelableSparseArray.java
@@ -0,0 +1,76 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+/**
+ * @hide
+ */
+public class ParcelableSparseArray extends SparseArray<Parcelable> implements Parcelable {
+
+    public ParcelableSparseArray() {
+        super();
+    }
+
+    public ParcelableSparseArray(Parcel source) {
+        super();
+        int size = source.readInt();
+        int[] keys = new int[size];
+        source.readIntArray(keys);
+        Parcelable[] values = source.readParcelableArray(
+                ParcelableSparseArray.class.getClassLoader());
+        for (int i = 0; i < size; ++i) {
+            put(keys[i], values[i]);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        int size = size();
+        int[] keys = new int[size];
+        Parcelable[] values = new Parcelable[size];
+        for (int i = 0; i < size; ++i) {
+            keys[i] = keyAt(i);
+            values[i] = valueAt(i);
+        }
+        parcel.writeInt(size);
+        parcel.writeIntArray(keys);
+        parcel.writeParcelableArray(values, flags);
+    }
+
+    public static final Parcelable.Creator<ParcelableSparseArray> CREATOR
+            = new Creator<ParcelableSparseArray>() {
+        @Override
+        public ParcelableSparseArray createFromParcel(Parcel source) {
+            return new ParcelableSparseArray(source);
+        }
+
+        @Override
+        public ParcelableSparseArray[] newArray(int size) {
+            return new ParcelableSparseArray[size];
+        }
+    };
+
+}
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index 53e96ba..262caa0 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -142,6 +142,8 @@
         super(context, attrs);
         setOrientation(VERTICAL);
 
+        ThemeUtils.checkAppCompatTheme(context);
+
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout,
                 0, R.style.Widget_Design_AppBarLayout);
         mTargetElevation = a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0);
@@ -194,13 +196,15 @@
     }
 
     @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        invalidateScrollRanges();
+    }
+
+    @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
-
-        // Invalidate the scroll ranges
-        mTotalScrollRange = INVALID_SCROLL_RANGE;
-        mDownPreScrollRange = INVALID_SCROLL_RANGE;
-        mDownPreScrollRange = INVALID_SCROLL_RANGE;
+        invalidateScrollRanges();
 
         mHaveChildWithInterpolator = false;
         for (int i = 0, z = getChildCount(); i < z; i++) {
@@ -215,6 +219,13 @@
         }
     }
 
+    private void invalidateScrollRanges() {
+        // Invalidate the scroll ranges
+        mTotalScrollRange = INVALID_SCROLL_RANGE;
+        mDownPreScrollRange = INVALID_SCROLL_RANGE;
+        mDownScrollRange = INVALID_SCROLL_RANGE;
+    }
+
     @Override
     public void setOrientation(int orientation) {
         if (orientation != VERTICAL) {
@@ -283,7 +294,7 @@
         return new LayoutParams(p);
     }
 
-    final boolean hasChildWithInterpolator() {
+    private boolean hasChildWithInterpolator() {
         return mHaveChildWithInterpolator;
     }
 
@@ -301,9 +312,7 @@
         for (int i = 0, z = getChildCount(); i < z; i++) {
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            final int childHeight = ViewCompat.isLaidOut(child)
-                    ? child.getHeight()
-                    : child.getMeasuredHeight();
+            final int childHeight = child.getMeasuredHeight();
             final int flags = lp.mScrollFlags;
 
             if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
@@ -323,25 +332,24 @@
                 break;
             }
         }
-        final int top = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
-        return mTotalScrollRange = (range - top);
+        return mTotalScrollRange = (range - getTopInset());
     }
 
-    final boolean hasScrollableChildren() {
+    private boolean hasScrollableChildren() {
         return getTotalScrollRange() != 0;
     }
 
     /**
      * Return the scroll range when scrolling up from a nested pre-scroll.
      */
-    final int getUpNestedPreScrollRange() {
+    private int getUpNestedPreScrollRange() {
         return getTotalScrollRange();
     }
 
     /**
      * Return the scroll range when scrolling down from a nested pre-scroll.
      */
-    final int getDownNestedPreScrollRange() {
+    private int getDownNestedPreScrollRange() {
         if (mDownPreScrollRange != INVALID_SCROLL_RANGE) {
             // If we already have a valid value, return it
             return mDownPreScrollRange;
@@ -351,9 +359,7 @@
         for (int i = getChildCount() - 1; i >= 0; i--) {
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            final int childHeight = ViewCompat.isLaidOut(child)
-                    ? child.getHeight()
-                    : child.getMeasuredHeight();
+            final int childHeight = child.getMeasuredHeight();
             final int flags = lp.mScrollFlags;
 
             if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) {
@@ -379,7 +385,7 @@
     /**
      * Return the scroll range when scrolling down from a nested scroll.
      */
-    final int getDownNestedScrollRange() {
+    private int getDownNestedScrollRange() {
         if (mDownScrollRange != INVALID_SCROLL_RANGE) {
             // If we already have a valid value, return it
             return mDownScrollRange;
@@ -389,9 +395,7 @@
         for (int i = 0, z = getChildCount(); i < z; i++) {
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            int childHeight = ViewCompat.isLaidOut(child)
-                    ? child.getHeight()
-                    : child.getMeasuredHeight();
+            int childHeight = child.getMeasuredHeight();
             childHeight += lp.topMargin + lp.bottomMargin;
 
             final int flags = lp.mScrollFlags;
@@ -404,7 +408,7 @@
                     // For a collapsing exit scroll, we to take the collapsed height into account.
                     // We also return the range straight away since later views can't scroll
                     // beneath us
-                    return range - ViewCompat.getMinimumHeight(child);
+                    return mDownScrollRange = (range - ViewCompat.getMinimumHeight(child));
                 }
             } else {
                 // As soon as a view doesn't have the scroll flag, we end the range calculation.
@@ -454,14 +458,18 @@
         return mTargetElevation;
     }
 
-    int getPendingAction() {
+    private int getPendingAction() {
         return mPendingAction;
     }
 
-    void resetPendingAction() {
+    private void resetPendingAction() {
         mPendingAction = PENDING_ACTION_NONE;
     }
 
+    private int getTopInset() {
+        return mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
+    }
+
     private void setWindowInsets(WindowInsetsCompat insets) {
         // Invalidate the total scroll range...
         mTotalScrollRange = INVALID_SCROLL_RANGE;
@@ -957,8 +965,6 @@
                         setAppBarTopBottomOffset(parent, abl, 0);
                     }
                 }
-                // Finally reset the pending state
-                abl.resetPendingAction();
             } else if (mOffsetToChildIndexOnLayout >= 0) {
                 View child = abl.getChildAt(mOffsetToChildIndexOnLayout);
                 int offset = -child.getBottom();
@@ -968,9 +974,12 @@
                     offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc);
                 }
                 setTopAndBottomOffset(offset);
-                mOffsetToChildIndexOnLayout = INVALID_POSITION;
             }
 
+            // Finally reset any pending states
+            abl.resetPendingAction();
+            mOffsetToChildIndexOnLayout = INVALID_POSITION;
+
             // Make sure we update the elevation
             dispatchOffsetUpdates(abl);
 
@@ -1072,6 +1081,10 @@
                             }
                         }
 
+                        if (ViewCompat.getFitsSystemWindows(child)) {
+                            childScrollableHeight -= layout.getTopInset();
+                        }
+
                         if (childScrollableHeight > 0) {
                             final int offsetForView = absOffset - child.getTop();
                             final int interpolatedDiff = Math.round(childScrollableHeight *
@@ -1199,7 +1212,7 @@
         }
 
         @Override
-        public boolean onMeasureChild(CoordinatorLayout parent, View child,
+        public boolean onMeasureChild(CoordinatorLayout parent, final View child,
                 int parentWidthMeasureSpec, int widthUsed,
                 int parentHeightMeasureSpec, int heightUsed) {
             final int childLpHeight = child.getLayoutParams().height;
@@ -1215,7 +1228,19 @@
                 }
 
                 final AppBarLayout appBar = findFirstAppBarLayout(dependencies);
-                if (appBar != null && ViewCompat.isLaidOut(appBar)) {
+                if (appBar != null && appBar.getVisibility() == View.VISIBLE) {
+                    if (appBar.getMeasuredHeight() <= 0 && appBar.getMeasuredWidth() <= 0) {
+                        // The AppBar hasn't been measured yet, so we'll post a Runnable to delay
+                        // our measure until after the ABLs has happened
+                        child.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                child.requestLayout();
+                            }
+                        });
+                        return false;
+                    }
+
                     if (ViewCompat.getFitsSystemWindows(appBar)) {
                         // If the AppBarLayout is fitting system windows then we need to also,
                         // otherwise we'll get CoL's compatible layout functionality
@@ -1227,6 +1252,7 @@
                         // If the measure spec doesn't specify a size, use the current height
                         availableHeight = parent.getHeight();
                     }
+
                     final int height = availableHeight - appBar.getMeasuredHeight()
                             + appBar.getTotalScrollRange();
                     final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
@@ -1245,32 +1271,64 @@
         }
 
         @Override
+        public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
+            // First lay out the child as normal
+            super.onLayoutChild(parent, child, layoutDirection);
+
+            // Now offset us correctly to be in the correct position. This is important for things
+            // like activity transitions which rely on accurate positioning after the first layout.
+            final List<View> dependencies = parent.getDependencies(child);
+            for (int i = 0, z = dependencies.size(); i < z; i++) {
+                if (updateOffset(parent, child, dependencies.get(i))) {
+                    // If we updated the offset, break out of the loop now
+                    break;
+                }
+            }
+            return true;
+        }
+
+        @Override
         public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
                 View dependency) {
+            updateOffset(parent, child, dependency);
+            return false;
+        }
+
+        private boolean updateOffset(CoordinatorLayout parent, View child, View dependency) {
             final CoordinatorLayout.Behavior behavior =
                     ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
             if (behavior instanceof Behavior) {
                 // Offset the child so that it is below the app-bar (with any overlap)
-
-                final int appBarOffset = ((Behavior) behavior)
-                        .getTopBottomOffsetForScrollingSibling();
-                final int expandedMax = dependency.getHeight() - mOverlayTop;
-                final int collapsedMin = parent.getHeight() - child.getHeight();
-
-                if (mOverlayTop != 0 && dependency instanceof AppBarLayout) {
-                    // If we have an overlap top, and the dependency is an AppBarLayout, we control
-                    // the offset ourselves based on the appbar's scroll progress. This is so that
-                    // the scroll happens sequentially rather than linearly
-                    final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
-                    setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin,
-                            Math.abs(appBarOffset) / (float) scrollRange));
-                } else {
-                    setTopAndBottomOffset(dependency.getHeight() - mOverlayTop + appBarOffset);
-                }
+                final int offset = ((Behavior) behavior).getTopBottomOffsetForScrollingSibling();
+                setTopAndBottomOffset(dependency.getHeight() + offset
+                        - getOverlapForOffset(dependency, offset));
+                return true;
             }
             return false;
         }
 
+        private int getOverlapForOffset(final View dependency, final int offset) {
+            if (mOverlayTop != 0 && dependency instanceof AppBarLayout) {
+                final AppBarLayout abl = (AppBarLayout) dependency;
+                final int totalScrollRange = abl.getTotalScrollRange();
+                final int preScrollDown = abl.getDownNestedPreScrollRange();
+
+                if (preScrollDown != 0 && (totalScrollRange + offset) <= preScrollDown) {
+                    // If we're in a pre-scroll down. Don't use the offset at all.
+                    return 0;
+                } else {
+                    final int availScrollRange = totalScrollRange - preScrollDown;
+                    if (availScrollRange == 0) {
+                        // Else we'll use a interpolated ratio of the overlap, depending on offset
+                        final float percScrolled = offset / (float) availScrollRange;
+                        return MathUtils.constrain(
+                                Math.round((1f + percScrolled) * mOverlayTop), 0, mOverlayTop);
+                    }
+                }
+            }
+            return mOverlayTop;
+        }
+
         /**
          * Set the distance that this view should overlap any {@link AppBarLayout}.
          *
diff --git a/design/src/android/support/design/widget/CollapsingTextHelper.java b/design/src/android/support/design/widget/CollapsingTextHelper.java
index 082c316..9dc60d4 100644
--- a/design/src/android/support/design/widget/CollapsingTextHelper.java
+++ b/design/src/android/support/design/widget/CollapsingTextHelper.java
@@ -93,6 +93,12 @@
     private Interpolator mPositionInterpolator;
     private Interpolator mTextSizeInterpolator;
 
+    private float mCollapsedShadowRadius, mCollapsedShadowDx, mCollapsedShadowDy;
+    private int mCollapsedShadowColor;
+
+    private float mExpandedShadowRadius, mExpandedShadowDx, mExpandedShadowDy;
+    private int mExpandedShadowColor;
+
     public CollapsingTextHelper(View view) {
         mView = view;
 
@@ -195,6 +201,10 @@
             mCollapsedTextSize = a.getDimensionPixelSize(
                     R.styleable.TextAppearance_android_textSize, (int) mCollapsedTextSize);
         }
+        mCollapsedShadowColor = a.getInt(R.styleable.TextAppearance_android_shadowColor, 0);
+        mCollapsedShadowDx = a.getFloat(R.styleable.TextAppearance_android_shadowDx, 0);
+        mCollapsedShadowDy = a.getFloat(R.styleable.TextAppearance_android_shadowDy, 0);
+        mCollapsedShadowRadius = a.getFloat(R.styleable.TextAppearance_android_shadowRadius, 0);
         a.recycle();
 
         recalculate();
@@ -210,6 +220,10 @@
             mExpandedTextSize = a.getDimensionPixelSize(
                     R.styleable.TextAppearance_android_textSize, (int) mExpandedTextSize);
         }
+        mExpandedShadowColor = a.getInt(R.styleable.TextAppearance_android_shadowColor, 0);
+        mExpandedShadowDx = a.getFloat(R.styleable.TextAppearance_android_shadowDx, 0);
+        mExpandedShadowDy = a.getFloat(R.styleable.TextAppearance_android_shadowDy, 0);
+        mExpandedShadowRadius = a.getFloat(R.styleable.TextAppearance_android_shadowRadius, 0);
         a.recycle();
 
         recalculate();
@@ -258,8 +272,10 @@
     }
 
     private void calculateCurrentOffsets() {
-        final float fraction = mExpandedFraction;
+        calculateOffsets(mExpandedFraction);
+    }
 
+    private void calculateOffsets(final float fraction) {
         interpolateBounds(fraction);
         mCurrentDrawX = lerp(mExpandedDrawX, mCollapsedDrawX, fraction,
                 mPositionInterpolator);
@@ -277,6 +293,12 @@
             mTextPaint.setColor(mCollapsedTextColor);
         }
 
+        mTextPaint.setShadowLayer(
+                lerp(mExpandedShadowRadius, mCollapsedShadowRadius, fraction, null),
+                lerp(mExpandedShadowDx, mCollapsedShadowDx, fraction, null),
+                lerp(mExpandedShadowDy, mCollapsedShadowDy, fraction, null),
+                blendColors(mExpandedShadowColor, mCollapsedShadowColor, fraction));
+
         ViewCompat.postInvalidateOnAnimation(mView);
     }
 
@@ -476,8 +498,7 @@
             return;
         }
 
-        mTextPaint.setTextSize(mExpandedTextSize);
-        mTextPaint.setColor(mExpandedTextColor);
+        calculateOffsets(0f);
         mTextureAscent = mTextPaint.ascent();
         mTextureDescent = mTextPaint.descent();
 
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index 230a1d0..3f5382e 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -126,6 +126,8 @@
     public CollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
+        ThemeUtils.checkAppCompatTheme(context);
+
         mCollapsingTextHelper = new CollapsingTextHelper(this);
         mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
 
@@ -360,6 +362,22 @@
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
+        // Update the collapsed bounds by getting it's transformed bounds. This needs to be done
+        // before the children are offset below
+        if (mCollapsingTitleEnabled && mDummyView != null) {
+            ViewGroupUtils.getDescendantRect(this, mDummyView, mTmpRect);
+            mCollapsingTextHelper.setCollapsedBounds(mTmpRect.left, bottom - mTmpRect.height(),
+                    mTmpRect.right, bottom);
+            // Update the expanded bounds
+            mCollapsingTextHelper.setExpandedBounds(
+                    mExpandedMarginLeft,
+                    mTmpRect.bottom + mExpandedMarginTop,
+                    right - left - mExpandedMarginRight,
+                    bottom - top - mExpandedMarginBottom);
+
+            mCollapsingTextHelper.recalculate();
+        }
+
         // Update our child view offset helpers
         for (int i = 0, z = getChildCount(); i < z; i++) {
             final View child = getChildAt(i);
@@ -376,21 +394,6 @@
             getViewOffsetHelper(child).onViewLayout();
         }
 
-        // Update the collapsed bounds by getting it's transformed bounds
-        if (mCollapsingTitleEnabled && mDummyView != null) {
-            ViewGroupUtils.getDescendantRect(this, mDummyView, mTmpRect);
-            mCollapsingTextHelper.setCollapsedBounds(mTmpRect.left, bottom - mTmpRect.height(),
-                    mTmpRect.right, bottom);
-            // Update the expanded bounds
-            mCollapsingTextHelper.setExpandedBounds(
-                    mExpandedMarginLeft,
-                    mTmpRect.bottom + mExpandedMarginTop,
-                    right - left - mExpandedMarginRight,
-                    bottom - top - mExpandedMarginBottom);
-
-            mCollapsingTextHelper.recalculate();
-        }
-
         // Finally, set our minimum height to enable proper AppBarLayout collapsing
         if (mToolbar != null) {
             if (mCollapsingTitleEnabled && TextUtils.isEmpty(mCollapsingTextHelper.getText())) {
@@ -462,25 +465,38 @@
         return mCollapsingTitleEnabled;
     }
 
-    private void showScrim() {
-        if (!mScrimsAreShown) {
-            if (ViewCompat.isLaidOut(this) && !isInEditMode()) {
-                animateScrim(255);
-            } else {
-                setScrimAlpha(255);
-            }
-            mScrimsAreShown = true;
-        }
+    /**
+     * Set whether the content scrim and/or status bar scrim should be shown or not. Any change
+     * in the vertical scroll may overwrite this value. Any visibility change will be animated if
+     * this view has already been laid out.
+     *
+     * @param shown whether the scrims should be shown
+     *
+     * @see #getStatusBarScrim()
+     * @see #getContentScrim()
+     */
+    public void setScrimsShown(boolean shown) {
+        setScrimsShown(shown, ViewCompat.isLaidOut(this) && !isInEditMode());
     }
 
-    private void hideScrim() {
-        if (mScrimsAreShown) {
-            if (ViewCompat.isLaidOut(this) && !isInEditMode()) {
-                animateScrim(0);
+    /**
+     * Set whether the content scrim and/or status bar scrim should be shown or not. Any change
+     * in the vertical scroll may overwrite this value.
+     *
+     * @param shown whether the scrims should be shown
+     * @param animate whether to animate the visibility change
+     *
+     * @see #getStatusBarScrim()
+     * @see #getContentScrim()
+     */
+    public void setScrimsShown(boolean shown, boolean animate) {
+        if (mScrimsAreShown != shown) {
+            if (animate) {
+                animateScrim(shown ? 0xFF : 0x0);
             } else {
-                setScrimAlpha(0);
+                setScrimAlpha(shown ? 0xFF : 0x0);
             }
-            mScrimsAreShown = false;
+            mScrimsAreShown = shown;
         }
     }
 
@@ -529,11 +545,14 @@
             if (mContentScrim != null) {
                 mContentScrim.setCallback(null);
             }
-
-            mContentScrim = drawable;
-            drawable.setBounds(0, 0, getWidth(), getHeight());
-            drawable.setCallback(this);
-            drawable.mutate().setAlpha(mScrimAlpha);
+            if (drawable != null) {
+                mContentScrim = drawable.mutate();
+                drawable.setBounds(0, 0, getWidth(), getHeight());
+                drawable.setCallback(this);
+                drawable.setAlpha(mScrimAlpha);
+            } else {
+                mContentScrim = null;
+            }
             ViewCompat.postInvalidateOnAnimation(this);
         }
     }
@@ -878,11 +897,7 @@
 
             // Show or hide the scrims if needed
             if (mContentScrim != null || mStatusBarScrim != null) {
-                if (getHeight() + verticalOffset < getScrimTriggerOffset() + insetTop) {
-                    showScrim();
-                } else {
-                    hideScrim();
-                }
+                setScrimsShown(getHeight() + verticalOffset < getScrimTriggerOffset() + insetTop);
             }
 
             if (mStatusBarScrim != null && insetTop > 0) {
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index 656ce566..5c798dd 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -168,6 +168,8 @@
     public CoordinatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
+        ThemeUtils.checkAppCompatTheme(context);
+
         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CoordinatorLayout,
                 defStyleAttr, R.style.Widget_Design_CoordinatorLayout);
         final int keylineArrayRes = a.getResourceId(R.styleable.CoordinatorLayout_keylines, 0);
@@ -441,7 +443,7 @@
         if (mBehaviorTouchView == null) {
             handled |= super.onTouchEvent(ev);
         } else if (cancelSuper) {
-            if (cancelEvent != null) {
+            if (cancelEvent == null) {
                 final long now = SystemClock.uptimeMillis();
                 cancelEvent = MotionEvent.obtain(now, now,
                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index 9a7b727..fda99852 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -31,6 +31,7 @@
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.view.View;
+import android.widget.ImageButton;
 import android.widget.ImageView;
 
 import java.util.List;
@@ -53,7 +54,7 @@
  * @attr ref android.support.design.R.styleable#FloatingActionButton_fabSize
  */
 @CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
-public class FloatingActionButton extends ImageView {
+public class FloatingActionButton extends ImageButton {
 
     // These values must match those in the attrs declaration
     private static final int SIZE_MINI = 1;
@@ -82,6 +83,8 @@
     public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
+        ThemeUtils.checkAppCompatTheme(context);
+
         mShadowPadding = new Rect();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
diff --git a/design/src/android/support/design/widget/NavigationView.java b/design/src/android/support/design/widget/NavigationView.java
index 4e325fa..45fd088 100644
--- a/design/src/android/support/design/widget/NavigationView.java
+++ b/design/src/android/support/design/widget/NavigationView.java
@@ -95,6 +95,8 @@
     public NavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
+        ThemeUtils.checkAppCompatTheme(context);
+
         // Create the menu
         mMenu = new NavigationMenu(context);
 
diff --git a/design/src/android/support/design/widget/Snackbar.java b/design/src/android/support/design/widget/Snackbar.java
index b04c706..0fccafe 100644
--- a/design/src/android/support/design/widget/Snackbar.java
+++ b/design/src/android/support/design/widget/Snackbar.java
@@ -179,6 +179,8 @@
         mParent = parent;
         mContext = parent.getContext();
 
+        ThemeUtils.checkAppCompatTheme(mContext);
+
         LayoutInflater inflater = LayoutInflater.from(mContext);
         mView = (SnackbarLayout) inflater.inflate(R.layout.design_layout_snackbar, mParent, false);
     }
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
index 7eee77b..5f38d11 100755
--- a/design/src/android/support/design/widget/TabLayout.java
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -19,6 +19,7 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -38,8 +39,10 @@
 import android.support.v4.view.ViewPager;
 import android.support.v7.app.ActionBar;
 import android.support.v7.internal.widget.TintManager;
+import android.text.Layout;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -59,10 +62,6 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 
-import static android.support.v4.view.ViewPager.SCROLL_STATE_DRAGGING;
-import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE;
-import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING;
-
 /**
  * TabLayout provides a horizontal layout to display tabs.
  *
@@ -95,6 +94,9 @@
  */
 public class TabLayout extends HorizontalScrollView {
 
+    private static final int DEFAULT_HEIGHT_WITH_TEXT_ICON = 72; // dps
+    private static final int DEFAULT_GAP_TEXT_ICON = 8; // dps
+    private static final int INVALID_WIDTH = -1;
     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
@@ -193,12 +195,15 @@
 
     private int mTabTextAppearance;
     private ColorStateList mTabTextColors;
+    private float mTabTextSize;
+    private float mTabTextMultiLineSize;
 
     private final int mTabBackgroundResId;
 
-    private final int mTabMinWidth;
     private int mTabMaxWidth = Integer.MAX_VALUE;
+    private final int mRequestedTabMinWidth;
     private final int mRequestedTabMaxWidth;
+    private final int mScrollableTabMinWidth;
 
     private int mContentInsetStart;
 
@@ -222,10 +227,10 @@
     public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
+        ThemeUtils.checkAppCompatTheme(context);
+
         // Disable the Scroll Bar
         setHorizontalScrollBarEnabled(false);
-        // Set us to fill the View port
-        setFillViewport(true);
 
         // Add the TabStrip
         mTabStrip = new SlidingTabStrip(context);
@@ -238,9 +243,6 @@
                 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,
@@ -252,8 +254,18 @@
         mTabPaddingBottom = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingBottom,
                 mTabPaddingBottom);
 
-        // Text colors come from the text appearance first
-        mTabTextColors = loadTextColorFromTextAppearance(mTabTextAppearance);
+        mTabTextAppearance = a.getResourceId(R.styleable.TabLayout_tabTextAppearance,
+                R.style.TextAppearance_Design_Tab);
+
+        // Text colors/sizes come from the text appearance first
+        final TypedArray ta = context.obtainStyledAttributes(mTabTextAppearance,
+                R.styleable.TextAppearance);
+        try {
+            mTabTextSize = ta.getDimensionPixelSize(R.styleable.TextAppearance_android_textSize, 0);
+            mTabTextColors = ta.getColorStateList(R.styleable.TextAppearance_android_textColor);
+        } finally {
+            ta.recycle();
+        }
 
         if (a.hasValue(R.styleable.TabLayout_tabTextColor)) {
             // If we have an explicit text color set, use it instead
@@ -268,14 +280,21 @@
             mTabTextColors = createColorStateList(mTabTextColors.getDefaultColor(), selected);
         }
 
-        mTabMinWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMinWidth, 0);
-        mRequestedTabMaxWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMaxWidth, 0);
+        mRequestedTabMinWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMinWidth,
+                INVALID_WIDTH);
+        mRequestedTabMaxWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMaxWidth,
+                INVALID_WIDTH);
         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();
 
+        // TODO add attr for these
+        final Resources res = getResources();
+        mTabTextMultiLineSize = res.getDimensionPixelSize(R.dimen.design_tab_text_size_2line);
+        mScrollableTabMinWidth = res.getDimensionPixelSize(R.dimen.design_tab_scrollable_min_width);
+
         // Now apply the tab mode and gravity
         applyModeAndGravity();
     }
@@ -634,6 +653,7 @@
     private TabView createTabView(Tab tab) {
         final TabView tabView = new TabView(getContext(), tab);
         tabView.setFocusable(true);
+        tabView.setMinimumWidth(getTabMinWidth());
 
         if (mTabClickListener == null) {
             mTabClickListener = new View.OnClickListener() {
@@ -706,7 +726,7 @@
     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
-        final int idealHeight = dpToPx(DEFAULT_HEIGHT) + getPaddingTop() + getPaddingBottom();
+        final int idealHeight = dpToPx(getDefaultHeight()) + getPaddingTop() + getPaddingBottom();
         switch (MeasureSpec.getMode(heightMeasureSpec)) {
             case MeasureSpec.AT_MOST:
                 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
@@ -718,39 +738,45 @@
                 break;
         }
 
+        final int specWidth = MeasureSpec.getSize(widthMeasureSpec);
+        if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
+            // If we don't have an unspecified width spec, use the given size to calculate
+            // the max tab width
+            mTabMaxWidth = mRequestedTabMaxWidth > 0
+                    ? mRequestedTabMaxWidth
+                    : specWidth - dpToPx(TAB_MIN_WIDTH_MARGIN);
+        }
+
         // Now super measure itself using the (possibly) modified height spec
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        if (mMode == MODE_FIXED && getChildCount() == 1) {
+        if (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();
+            boolean remeasure = false;
 
-            if (child.getMeasuredWidth() > width) {
-                // If the child is wider than us, re-measure it with a widthSpec set to exact our
-                // measure width
+            switch (mMode) {
+                case MODE_SCROLLABLE:
+                    // We only need to resize the child if it's smaller than us. This is similar
+                    // to fillViewport
+                    remeasure = child.getMeasuredWidth() < getMeasuredWidth();
+                    break;
+                case MODE_FIXED:
+                    // Resize the child so that it doesn't scroll
+                    remeasure = child.getMeasuredWidth() != getMeasuredWidth();
+                    break;
+            }
+
+            if (remeasure) {
+                // Re-measure the child with a widthSpec set to be exactly our measure width
                 int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop()
                         + getPaddingBottom(), child.getLayoutParams().height);
-                int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
+                int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+                        getMeasuredWidth(), 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;
-        }
-
-        if (mTabMaxWidth != maxTabWidth) {
-            // If the tab max width has changed, re-measure
-            mTabMaxWidth = maxTabWidth;
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
     }
 
     private void removeTabViewAt(int position) {
@@ -819,7 +845,9 @@
             }
         } else {
             final int newPosition = tab != null ? tab.getPosition() : Tab.INVALID_POSITION;
-            setSelectedTabView(newPosition);
+            if (newPosition != Tab.INVALID_POSITION) {
+                setSelectedTabView(newPosition);
+            }
             if (updateIndicator) {
                 if ((mSelectedTab == null || mSelectedTab.getPosition() == Tab.INVALID_POSITION)
                         && newPosition != Tab.INVALID_POSITION) {
@@ -873,14 +901,17 @@
                 break;
         }
 
-        updateTabViewsLayoutParams();
+        updateTabViews(true);
     }
 
-    private void updateTabViewsLayoutParams() {
+    private void updateTabViews(final boolean requestLayout) {
         for (int i = 0; i < mTabStrip.getChildCount(); i++) {
             View child = mTabStrip.getChildAt(i);
+            child.setMinimumWidth(getTabMinWidth());
             updateTabViewLayoutParams((LinearLayout.LayoutParams) child.getLayoutParams());
-            child.requestLayout();
+            if (requestLayout) {
+                child.requestLayout();
+            }
         }
     }
 
@@ -1138,6 +1169,8 @@
         private TextView mCustomTextView;
         private ImageView mCustomIconView;
 
+        private int mDefaultMaxLines = 2;
+
         public TabView(Context context, Tab tab) {
             super(context);
             mTab = tab;
@@ -1147,6 +1180,7 @@
             ViewCompat.setPaddingRelative(this, mTabPaddingStart, mTabPaddingTop,
                     mTabPaddingEnd, mTabPaddingBottom);
             setGravity(Gravity.CENTER);
+            setOrientation(VERTICAL);
             update();
         }
 
@@ -1184,16 +1218,58 @@
 
         @Override
         public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-            final int measuredWidth = getMeasuredWidth();
-            if (measuredWidth < mTabMinWidth || measuredWidth > mTabMaxWidth) {
-                // Re-measure if we are outside our min or max width
-                widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                        MathUtils.constrain(measuredWidth, mTabMinWidth, mTabMaxWidth),
-                        MeasureSpec.EXACTLY);
+            final int specWidth = MeasureSpec.getSize(widthMeasureSpec);
+            final int maxWidth = getTabMaxWidth();
+            if (maxWidth > 0 && (specWidth == 0 || specWidth > maxWidth)) {
+                // If we have a max width and a given spec size which is either unspecified or
+                // larger than the spec size, use a AT_MOST spec
+                super.onMeasure(MeasureSpec.makeMeasureSpec(mTabMaxWidth, MeasureSpec.AT_MOST),
+                        heightMeasureSpec);
+            } else {
+                // Else, use the original specs
                 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
             }
+
+            // We need to switch the text size based on whether the text is spanning 2 lines or not
+            if (mTextView != null) {
+                final Resources res = getResources();
+                float textSize = mTabTextSize;
+                int maxLines = mDefaultMaxLines;
+
+                if (mIconView != null && mIconView.getVisibility() == VISIBLE) {
+                    // If the icon view is being displayed, we limit the text to 1 line
+                    maxLines = 1;
+                } else if (mTextView != null && mTextView.getLineCount() > 1) {
+                    // Otherwise when we have text which wraps we reduce the text size
+                    textSize = mTabTextMultiLineSize;
+                }
+
+                final float curTextSize = mTextView.getTextSize();
+                final int curLineCount = mTextView.getLineCount();
+
+                if (textSize != curTextSize || maxLines != mTextView.getMaxLines()) {
+                    // We've got a new text size and/or max lines...
+                    boolean updateTextView = true;
+
+                    if (mMode == MODE_FIXED && textSize > curTextSize && curLineCount == 1) {
+                        // If we're in fixed mode, going up in text size and currently have 1 line
+                        // then it's very easy to get into an infinite recursion.
+                        // To combat that we check to see if the change in text size
+                        // will cause a line count change. If so, abort the size change.
+                        final Layout layout = mTextView.getLayout();
+                        if (layout == null
+                                || approximateLineWidth(layout, 0, textSize) > layout.getWidth()) {
+                            updateTextView = false;
+                        }
+                    }
+
+                    if (updateTextView) {
+                        mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+                        mTextView.setMaxLines(maxLines);
+                        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+                    }
+                }
+            }
         }
 
         final void update() {
@@ -1217,6 +1293,9 @@
                 }
 
                 mCustomTextView = (TextView) custom.findViewById(android.R.id.text1);
+                if (mCustomTextView != null) {
+                    mDefaultMaxLines = mCustomTextView.getMaxLines();
+                }
                 mCustomIconView = (ImageView) custom.findViewById(android.R.id.icon);
             } else {
                 // We do not have a custom view. Remove one if it already exists
@@ -1241,6 +1320,7 @@
                             .inflate(R.layout.design_layout_tab_text, this, false);
                     addView(textView);
                     mTextView = textView;
+                    mDefaultMaxLines = mTextView.getMaxLines();
                 }
                 mTextView.setTextAppearance(getContext(), mTabTextAppearance);
                 if (mTabTextColors != null) {
@@ -1284,6 +1364,19 @@
                 }
             }
 
+            if (iconView != null) {
+                MarginLayoutParams lp = ((MarginLayoutParams) iconView.getLayoutParams());
+                int bottomMargin = 0;
+                if (hasText && iconView.getVisibility() == VISIBLE) {
+                    // If we're showing both text and icon, add some margin bottom to the icon
+                    bottomMargin = dpToPx(DEFAULT_GAP_TEXT_ICON);
+                }
+                if (bottomMargin != lp.bottomMargin) {
+                    lp.bottomMargin = bottomMargin;
+                    iconView.requestLayout();
+                }
+            }
+
             if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
                 setOnLongClickListener(this);
             } else {
@@ -1315,6 +1408,13 @@
         public Tab getTab() {
             return mTab;
         }
+
+        /**
+         * Approximates a given lines width with the new provided text size.
+         */
+        private float approximateLineWidth(Layout layout, int line, float textSize) {
+            return layout.getLineWidth(line) * (textSize / layout.getPaint().getTextSize());
+        }
     }
 
     private class SlidingTabStrip extends LinearLayout {
@@ -1327,6 +1427,8 @@
         private int mIndicatorLeft = -1;
         private int mIndicatorRight = -1;
 
+        private ValueAnimatorCompat mCurrentAnimator;
+
         SlidingTabStrip(Context context) {
             super(context);
             setWillNotDraw(false);
@@ -1380,14 +1482,13 @@
             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
+                // First we'll find the widest 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());
+                    View child = getChildAt(i);
+                    if (child.getVisibility() == VISIBLE) {
+                        largestTabWidth = Math.max(largestTabWidth, child.getMeasuredWidth());
+                    }
                 }
 
                 if (largestTabWidth <= 0) {
@@ -1396,32 +1497,50 @@
                 }
 
                 final int gutter = dpToPx(FIXED_WRAP_GUTTER_MIN);
+                boolean remeasure = false;
+
                 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;
+                        final LinearLayout.LayoutParams lp =
+                                (LayoutParams) getChildAt(i).getLayoutParams();
+                        if (lp.width != largestTabWidth || lp.weight != 0) {
+                            lp.width = largestTabWidth;
+                            lp.weight = 0;
+                            remeasure = true;
+                        }
                     }
                 } 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();
+                    updateTabViews(false);
+                    remeasure = true;
                 }
 
-                // Now re-measure after our changes
-                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+                if (remeasure) {
+                    // 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 we've been layed out, update the indicator position
-            updateIndicatorPosition();
+
+            if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+                // If we're currently running an animation, lets cancel it and start a
+                // new animation with the remaining duration
+                mCurrentAnimator.cancel();
+                final long duration = mCurrentAnimator.getDuration();
+                animateIndicatorToPosition(mSelectedPosition,
+                        Math.round((1f - mCurrentAnimator.getAnimatedFraction()) * duration));
+            } else {
+                // If we've been layed out, update the indicator position
+                updateIndicatorPosition();
+            }
         }
 
         private void updateIndicatorPosition() {
@@ -1518,6 +1637,7 @@
                     }
                 });
                 animator.start();
+                mCurrentAnimator = animator;
             }
         }
 
@@ -1550,14 +1670,29 @@
         return new ColorStateList(states, colors);
     }
 
-    private ColorStateList loadTextColorFromTextAppearance(int textAppearanceResId) {
-        TypedArray a = getContext().obtainStyledAttributes(textAppearanceResId,
-                R.styleable.TextAppearance);
-        try {
-            return a.getColorStateList(R.styleable.TextAppearance_android_textColor);
-        } finally {
-            a.recycle();
+    private int getDefaultHeight() {
+        boolean hasIconAndText = false;
+        for (int i = 0, count = mTabs.size(); i < count; i++) {
+            Tab tab = mTabs.get(i);
+            if (tab != null && tab.getIcon() != null && !TextUtils.isEmpty(tab.getText())) {
+                hasIconAndText = true;
+                break;
+            }
         }
+        return hasIconAndText ? DEFAULT_HEIGHT_WITH_TEXT_ICON : DEFAULT_HEIGHT;
+    }
+
+    private int getTabMinWidth() {
+        if (mRequestedTabMinWidth != INVALID_WIDTH) {
+            // If we have been given a min width, use it
+            return mRequestedTabMinWidth;
+        }
+        // Else, we'll use the default value
+        return mMode == MODE_SCROLLABLE ? mScrollableTabMinWidth : 0;
+    }
+
+    private int getTabMaxWidth() {
+        return mTabMaxWidth;
     }
 
     /**
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 16ebc30..00ad669 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -86,6 +86,8 @@
         // Can't call through to super(Context, AttributeSet, int) since it doesn't exist on API 10
         super(context, attrs);
 
+        ThemeUtils.checkAppCompatTheme(context);
+
         setOrientation(VERTICAL);
         setWillNotDraw(false);
         setAddStatesFromChildren(true);
@@ -96,7 +98,7 @@
 
         final TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);
-        mHint = a.getText(R.styleable.TextInputLayout_android_hint);
+        setHint(a.getText(R.styleable.TextInputLayout_android_hint));
         mHintAnimationEnabled = a.getBoolean(
                 R.styleable.TextInputLayout_hintAnimationEnabled, true);
 
diff --git a/design/src/android/support/design/widget/ThemeUtils.java b/design/src/android/support/design/widget/ThemeUtils.java
new file mode 100644
index 0000000..327a44d
--- /dev/null
+++ b/design/src/android/support/design/widget/ThemeUtils.java
@@ -0,0 +1,38 @@
+/*
+ * 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.support.design.R;
+
+class ThemeUtils {
+
+    private static final int[] APPCOMPAT_CHECK_ATTRS = { R.attr.colorPrimary };
+
+    static void checkAppCompatTheme(Context context) {
+        TypedArray a = context.obtainStyledAttributes(APPCOMPAT_CHECK_ATTRS);
+        final boolean failed = !a.hasValue(0);
+        if (a != null) {
+            a.recycle();
+        }
+        if (failed) {
+            throw new IllegalArgumentException("You need to use a Theme.AppCompat theme "
+                    + "(or descendant) with the design library.");
+        }
+    }
+}
diff --git a/design/src/android/support/design/widget/ViewOffsetHelper.java b/design/src/android/support/design/widget/ViewOffsetHelper.java
index 6bde40b..a76ca9a 100644
--- a/design/src/android/support/design/widget/ViewOffsetHelper.java
+++ b/design/src/android/support/design/widget/ViewOffsetHelper.java
@@ -67,8 +67,8 @@
 
     private static void tickleInvalidationFlag(View view) {
         final float x = ViewCompat.getTranslationX(view);
-        ViewCompat.setTranslationX(view, x + 1);
-        ViewCompat.setTranslationX(view, x);
+        ViewCompat.setTranslationY(view, x + 1);
+        ViewCompat.setTranslationY(view, x);
     }
 
     /**
diff --git a/recommendation/build.gradle b/recommendation/build.gradle
index 0a9ca47..a9db906 100644
--- a/recommendation/build.gradle
+++ b/recommendation/build.gradle
@@ -16,16 +16,19 @@
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = ['src']
-        main.res.srcDirs = ['res']
+        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/settings.gradle b/settings.gradle
index 4c4ac52..d33c2ac 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -48,3 +48,6 @@
 
 include ':support-customtabs'
 project(':support-customtabs').projectDir = new File(rootDir, 'customtabs')
+
+include ':support-recommendation'
+project(':support-recommendation').projectDir = new File(rootDir, 'recommendation')
diff --git a/v17/leanback/Android.mk b/v17/leanback/Android.mk
index dd03d6a..57b54e0 100644
--- a/v17/leanback/Android.mk
+++ b/v17/leanback/Android.mk
@@ -40,6 +40,16 @@
 
 # -----------------------------------------------------------------------
 
+#  A helper sub-library that makes direct use of API 23.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v17-leanback-api23
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, api23)
+LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# -----------------------------------------------------------------------
+
 #  A helper sub-library that makes direct use of API 21.
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v17-leanback-api21
@@ -79,6 +89,7 @@
 LOCAL_SDK_VERSION := 17
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v17-leanback-kitkat android-support-v17-leanback-jbmr2 \
+        android-support-v17-leanback-api23 \
         android-support-v17-leanback-api21 android-support-v17-leanback-common
 LOCAL_JAVA_LIBRARIES := \
         android-support-v4 \
diff --git a/v17/leanback/api/current.txt b/v17/leanback/api/current.txt
index fb2832d..76ff250 100644
--- a/v17/leanback/api/current.txt
+++ b/v17/leanback/api/current.txt
@@ -189,6 +189,7 @@
     method public android.support.v17.leanback.widget.GuidanceStylist.Guidance onCreateGuidance(android.os.Bundle);
     method public android.support.v17.leanback.widget.GuidanceStylist onCreateGuidanceStylist();
     method public void onGuidedActionClicked(android.support.v17.leanback.widget.GuidedAction);
+    method public void onGuidedActionEdited(android.support.v17.leanback.widget.GuidedAction);
     method public void onGuidedActionFocused(android.support.v17.leanback.widget.GuidedAction);
     method public int onProvideTheme();
     method public void setActions(java.util.List<android.support.v17.leanback.widget.GuidedAction>);
@@ -426,7 +427,7 @@
     method public abstract boolean onQueryTextSubmit(java.lang.String);
   }
 
-  public class VerticalGridFragment extends android.app.Fragment {
+  public class VerticalGridFragment extends android.support.v17.leanback.app.BrandedFragment {
     ctor public VerticalGridFragment();
     method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
     method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
@@ -438,7 +439,7 @@
     method public void setSelectedPosition(int);
   }
 
-  public class VerticalGridSupportFragment extends android.support.v4.app.Fragment {
+  public class VerticalGridSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
     ctor public VerticalGridSupportFragment();
     method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
     method public android.support.v17.leanback.widget.VerticalGridPresenter getGridPresenter();
@@ -728,6 +729,8 @@
     method public abstract void onFragmentExit(java.util.List<android.animation.Animator>);
     method public abstract void onFragmentReenter(java.util.List<android.animation.Animator>);
     method public abstract void onFragmentReturn(java.util.List<android.animation.Animator>);
+    method public abstract void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public abstract void onImeDisappearing(java.util.List<android.animation.Animator>);
   }
 
   public class FullWidthDetailsOverviewRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
@@ -805,6 +808,8 @@
     method public void onFragmentExit(java.util.List<android.animation.Animator>);
     method public void onFragmentReenter(java.util.List<android.animation.Animator>);
     method public void onFragmentReturn(java.util.List<android.animation.Animator>);
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
     method public int onProvideLayoutId();
   }
 
@@ -825,9 +830,11 @@
     method public boolean hasNext();
     method public boolean infoOnly();
     method public boolean isChecked();
+    method public boolean isEditable();
     method public boolean isEnabled();
     method public void setChecked(boolean);
     method public void setEnabled(boolean);
+    method public void setTitle(java.lang.CharSequence);
     field public static final int DEFAULT_CHECK_SET_ID = 1; // 0x1
     field public static final int NO_CHECK_SET = 0; // 0x0
     field public static final int NO_DRAWABLE = 0; // 0x0
@@ -839,6 +846,7 @@
     method public android.support.v17.leanback.widget.GuidedAction.Builder checkSetId(int);
     method public android.support.v17.leanback.widget.GuidedAction.Builder checked(boolean);
     method public android.support.v17.leanback.widget.GuidedAction.Builder description(java.lang.String);
+    method public android.support.v17.leanback.widget.GuidedAction.Builder editable(boolean);
     method public android.support.v17.leanback.widget.GuidedAction.Builder enabled(boolean);
     method public android.support.v17.leanback.widget.GuidedAction.Builder hasNext(boolean);
     method public android.support.v17.leanback.widget.GuidedAction.Builder icon(android.graphics.drawable.Drawable);
@@ -850,6 +858,13 @@
     method public android.support.v17.leanback.widget.GuidedAction.Builder title(java.lang.String);
   }
 
+  public class GuidedActionEditText extends android.widget.EditText implements android.support.v17.leanback.widget.ImeKeyMonitor {
+    ctor public GuidedActionEditText(android.content.Context);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet);
+    ctor public GuidedActionEditText(android.content.Context, android.util.AttributeSet, int);
+    method public void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
   public class GuidedActionsStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
     ctor public GuidedActionsStylist();
     method public android.support.v17.leanback.widget.VerticalGridView getActionsGridView();
@@ -865,6 +880,8 @@
     method public void onFragmentExit(java.util.List<android.animation.Animator>);
     method public void onFragmentReenter(java.util.List<android.animation.Animator>);
     method public void onFragmentReturn(java.util.List<android.animation.Animator>);
+    method public void onImeAppearing(java.util.List<android.animation.Animator>);
+    method public void onImeDisappearing(java.util.List<android.animation.Animator>);
     method public int onProvideItemLayoutId();
     method public int onProvideLayoutId();
     field protected android.support.v17.leanback.widget.VerticalGridView mActionsGridView;
@@ -878,6 +895,7 @@
     method public android.widget.ImageView getChevronView();
     method public android.view.View getContentView();
     method public android.widget.TextView getDescriptionView();
+    method public android.widget.EditText getEditableTitleView();
     method public android.widget.ImageView getIconView();
     method public android.widget.TextView getTitleView();
     field public final android.view.View view;
@@ -918,9 +936,10 @@
   }
 
   public class ImageCardView extends android.support.v17.leanback.widget.BaseCardView {
+    ctor public ImageCardView(android.content.Context, int);
+    ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int);
     ctor public ImageCardView(android.content.Context);
     ctor public ImageCardView(android.content.Context, android.util.AttributeSet);
-    ctor public ImageCardView(android.content.Context, android.util.AttributeSet, int);
     method public android.graphics.drawable.Drawable getBadgeImage();
     method public java.lang.CharSequence getContentText();
     method public android.graphics.drawable.Drawable getInfoAreaBackground();
@@ -937,6 +956,19 @@
     method public void setMainImageDimensions(int, int);
     method public void setMainImageScaleType(android.widget.ImageView.ScaleType);
     method public void setTitleText(java.lang.CharSequence);
+    field public static final int CARD_TYPE_FLAG_CONTENT = 2; // 0x2
+    field public static final int CARD_TYPE_FLAG_ICON_LEFT = 8; // 0x8
+    field public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4; // 0x4
+    field public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0; // 0x0
+    field public static final int CARD_TYPE_FLAG_TITLE = 1; // 0x1
+  }
+
+  public abstract interface ImeKeyMonitor {
+    method public abstract void setImeKeyListener(android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener);
+  }
+
+  public static abstract interface ImeKeyMonitor.ImeKeyListener {
+    method public abstract boolean onKeyPreIme(android.widget.EditText, int, android.view.KeyEvent);
   }
 
   public final class ItemAlignmentFacet {
@@ -1447,10 +1479,12 @@
     ctor public ShadowOverlayContainer(android.content.Context);
     ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet);
     ctor public ShadowOverlayContainer(android.content.Context, android.util.AttributeSet, int);
+    ctor public ShadowOverlayContainer(android.content.Context, int, boolean, boolean);
     method public int getShadowType();
     method public android.view.View getWrappedView();
     method public deprecated void initialize(boolean, boolean);
-    method public void initialize(boolean, boolean, boolean);
+    method public deprecated void initialize(boolean, boolean, boolean);
+    method public void initialize(int, boolean, boolean);
     method protected void onLayout(boolean, int, int, int, int);
     method public static void prepareParentForShadow(android.view.ViewGroup);
     method public void setOverlayColor(int);
@@ -1551,6 +1585,7 @@
     method public void onBindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder, java.lang.Object);
     method public final android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
     method public void onUnbindViewHolder(android.support.v17.leanback.widget.Presenter.ViewHolder);
+    method public void setEntranceTransitionState(android.support.v17.leanback.widget.VerticalGridPresenter.ViewHolder, boolean);
     method public void setNumberOfColumns(int);
     method public final void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
     method public final void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
diff --git a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
index ab4d179..cd4ff3e 100644
--- a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.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. You may obtain a copy of the License at
@@ -19,14 +19,14 @@
 import android.graphics.Outline;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.view.ViewGroup;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 
 class ShadowHelperApi21 {
 
     static class ShadowImpl {
-        ViewGroup mShadowContainer;
+        View mShadowContainer;
         float mNormalZ;
         float mFocusedZ;
     }
@@ -41,7 +41,7 @@
 
     /* add shadows and return a implementation detail object */
     public static Object addDynamicShadow(
-            ViewGroup shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners) {
+            View shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners) {
         if (roundedCorners) {
             RoundedRectHelperApi21.setClipToRoundedOutline(shadowContainer, true);
         } else {
@@ -52,7 +52,9 @@
         impl.mNormalZ = unfocusedZ;
         impl.mFocusedZ = focusedZ;
         shadowContainer.setZ(impl.mNormalZ);
-        shadowContainer.setTransitionGroup(true);
+        if (shadowContainer instanceof ViewGroup) {
+            ((ViewGroup) shadowContainer).setTransitionGroup(true);
+        }
         return impl;
     }
 
diff --git a/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java b/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
new file mode 100644
index 0000000..c4760d4
--- /dev/null
+++ b/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
@@ -0,0 +1,35 @@
+/*
+ * 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.v17.leanback.R;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.ViewGroup;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+class ForegroundHelperApi23 {
+
+    public static Drawable getForeground(View view) {
+        return view.getForeground();
+    }
+
+    public static void setForeground(View view, Drawable drawable) {
+        view.setForeground(drawable);
+    }
+}
diff --git a/v17/leanback/build.gradle b/v17/leanback/build.gradle
index 8dc79e8..401a5c4 100644
--- a/v17/leanback/build.gradle
+++ b/v17/leanback/build.gradle
@@ -19,8 +19,8 @@
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'src']
-        main.aidl.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'src']
+        main.java.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'api23', 'src']
+        main.aidl.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'api23', 'src']
         main.res.srcDirs = ['res']
 
         androidTest.setRoot('tests')
diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v17/leanback/res/animator/lb_guidedstep_slide_down.xml
similarity index 62%
copy from v17/leanback/res/layout/lb_card_color_overlay.xml
copy to v17/leanback/res/animator/lb_guidedstep_slide_down.xml
index 45a40e1..b31421f 100644
--- a/v17/leanback/res/layout/lb_card_color_overlay.xml
+++ b/v17/leanback/res/animator/lb_guidedstep_slide_down.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     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,8 +14,9 @@
      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="match_parent" />
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@android:integer/config_shortAnimTime"
+    android:propertyName="translationY"
+    android:valueFrom="@dimen/lb_guidedstep_slide_ime_distance"
+    android:valueTo="0.0"
+    android:valueType="floatType" />
diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v17/leanback/res/animator/lb_guidedstep_slide_up.xml
similarity index 62%
copy from v17/leanback/res/layout/lb_card_color_overlay.xml
copy to v17/leanback/res/animator/lb_guidedstep_slide_up.xml
index 45a40e1..165fe18 100644
--- a/v17/leanback/res/layout/lb_card_color_overlay.xml
+++ b/v17/leanback/res/animator/lb_guidedstep_slide_up.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     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,8 +14,9 @@
      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="match_parent" />
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@android:integer/config_shortAnimTime"
+    android:propertyName="translationY"
+    android:valueFrom="0.0"
+    android:valueTo="@dimen/lb_guidedstep_slide_ime_distance"
+    android:valueType="floatType" />
diff --git a/v17/leanback/res/layout/lb_guidedactions_item.xml b/v17/leanback/res/layout/lb_guidedactions_item.xml
index 4e41454..3babaa9 100644
--- a/v17/leanback/res/layout/lb_guidedactions_item.xml
+++ b/v17/leanback/res/layout/lb_guidedactions_item.xml
@@ -34,7 +34,7 @@
         android:id="@+id/guidedactions_item_content"
         style="?attr/guidedActionItemContentStyle" >
 
-        <TextView
+        <android.support.v17.leanback.widget.GuidedActionEditText
             android:id="@+id/guidedactions_item_title"
             style="?attr/guidedActionItemTitleStyle" />
 
diff --git a/v17/leanback/res/layout/lb_image_card_view.xml b/v17/leanback/res/layout/lb_image_card_view.xml
index 2261965..1bc23f8 100644
--- a/v17/leanback/res/layout/lb_image_card_view.xml
+++ b/v17/leanback/res/layout/lb_image_card_view.xml
@@ -15,59 +15,16 @@
      limitations under the License.
 -->
 
-<merge
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:lb="http://schemas.android.com/apk/res-auto">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:lb="http://schemas.android.com/apk/res-auto" >
 
     <ImageView
         android:id="@+id/main_image"
-        lb:layout_viewType="main"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:scaleType="centerCrop"
-        android:adjustViewBounds="true"
-        android:contentDescription="@null" />
-        <android.support.v17.leanback.widget.NonOverlappingRelativeLayout
-            lb:layout_viewType="info"
-            android:id="@+id/info_field"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/lb_basic_card_info_height"
-            android:paddingStart="@dimen/lb_basic_card_info_padding_horizontal"
-            android:paddingEnd="@dimen/lb_basic_card_info_padding_horizontal"
-            android:paddingTop="@dimen/lb_basic_card_info_padding_top"
-            android:layout_centerHorizontal="true" >
-            <TextView
-                android:id="@+id/title_text"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_alignParentStart="true"
-                android:layout_marginBottom="@dimen/lb_basic_card_info_text_margin"
-                android:maxLines="1"
-                android:fontFamily="sans-serif-condensed"
-                android:textColor="@color/lb_basic_card_title_text_color"
-                android:textSize="@dimen/lb_basic_card_title_text_size"
-                android:ellipsize="end" />
-            <TextView
-                android:id="@+id/content_text"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/title_text"
-                android:layout_alignParentStart="true"
-                android:layout_toStartOf="@+id/extra_badge"
-                android:maxLines="1"
-                android:fontFamily="sans-serif-condensed"
-                android:textColor="@color/lb_basic_card_content_text_color"
-                android:textSize="@dimen/lb_basic_card_content_text_size"
-                android:ellipsize="none" />
-            <ImageView
-                android:id="@+id/extra_badge"
-                android:layout_width="@dimen/lb_basic_card_info_badge_size"
-                android:layout_height="@dimen/lb_basic_card_info_badge_size"
-                android:layout_marginStart="@dimen/lb_basic_card_info_badge_margin"
-                android:layout_alignBottom="@id/content_text"
-                android:layout_alignParentEnd="true"
-                android:scaleType="fitCenter"
-                android:visibility="gone"
-                android:contentDescription="@null" />
-        </android.support.v17.leanback.widget.NonOverlappingRelativeLayout>
-</merge>
+        style="?attr/lbImageCardViewImageStyle" />
+
+    <android.support.v17.leanback.widget.NonOverlappingRelativeLayout
+        android:id="@+id/info_field"
+        style="?attr/lbImageCardViewInfoAreaStyle">
+    </android.support.v17.leanback.widget.NonOverlappingRelativeLayout>
+
+</merge>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v17/leanback/res/layout/lb_image_card_view_themed_badge_left.xml
similarity index 68%
copy from v17/leanback/res/layout/lb_card_color_overlay.xml
copy to v17/leanback/res/layout/lb_image_card_view_themed_badge_left.xml
index 45a40e1..35d2da6 100644
--- a/v17/leanback/res/layout/lb_card_color_overlay.xml
+++ b/v17/leanback/res/layout/lb_image_card_view_themed_badge_left.xml
@@ -15,7 +15,9 @@
      limitations under the License.
 -->
 
-<View
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent" />
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/extra_badge"
+    android:layout_alignBottom="@+id/content_text"
+    android:layout_alignParentStart="true"
+    android:layout_marginEnd="@dimen/lb_basic_card_info_badge_margin"
+    style="?attr/lbImageCardViewBadgeStyle" />
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v17/leanback/res/layout/lb_image_card_view_themed_badge_right.xml
similarity index 68%
copy from v17/leanback/res/layout/lb_card_color_overlay.xml
copy to v17/leanback/res/layout/lb_image_card_view_themed_badge_right.xml
index 45a40e1..02dd917 100644
--- a/v17/leanback/res/layout/lb_card_color_overlay.xml
+++ b/v17/leanback/res/layout/lb_image_card_view_themed_badge_right.xml
@@ -15,7 +15,9 @@
      limitations under the License.
 -->
 
-<View
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent" />
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/extra_badge"
+    android:layout_alignBottom="@+id/content_text"
+    android:layout_alignParentEnd="true"
+    android:layout_marginStart="@dimen/lb_basic_card_info_badge_margin"
+    style="?attr/lbImageCardViewBadgeStyle" />
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v17/leanback/res/layout/lb_image_card_view_themed_content.xml
similarity index 80%
rename from v17/leanback/res/layout/lb_card_color_overlay.xml
rename to v17/leanback/res/layout/lb_image_card_view_themed_content.xml
index 45a40e1..5592371 100644
--- a/v17/leanback/res/layout/lb_card_color_overlay.xml
+++ b/v17/leanback/res/layout/lb_image_card_view_themed_content.xml
@@ -15,7 +15,6 @@
      limitations under the License.
 -->
 
-<View
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent" />
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content_text"
+    style="?attr/lbImageCardViewContentStyle" />
diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v17/leanback/res/layout/lb_image_card_view_themed_title.xml
similarity index 80%
copy from v17/leanback/res/layout/lb_card_color_overlay.xml
copy to v17/leanback/res/layout/lb_image_card_view_themed_title.xml
index 45a40e1..67e2493 100644
--- a/v17/leanback/res/layout/lb_card_color_overlay.xml
+++ b/v17/leanback/res/layout/lb_image_card_view_themed_title.xml
@@ -15,7 +15,6 @@
      limitations under the License.
 -->
 
-<View
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent" />
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/title_text"
+    style="?attr/lbImageCardViewTitleStyle" />
diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v17/leanback/res/transition-v21/lb_vertical_grid_enter_transition.xml
similarity index 70%
copy from v17/leanback/res/layout/lb_card_color_overlay.xml
copy to v17/leanback/res/transition-v21/lb_vertical_grid_enter_transition.xml
index 45a40e1..00466cb 100644
--- a/v17/leanback/res/layout/lb_card_color_overlay.xml
+++ b/v17/leanback/res/transition-v21/lb_vertical_grid_enter_transition.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     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.
@@ -15,7 +15,8 @@
      limitations under the License.
 -->
 
-<View
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent" />
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" >
+  <fade
+      android:interpolator="@android:interpolator/linear_out_slow_in"
+      android:duration="150"/>
+</transitionSet>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v17/leanback/res/transition-v21/lb_vertical_grid_entrance_transition.xml
similarity index 66%
copy from v17/leanback/res/layout/lb_card_color_overlay.xml
copy to v17/leanback/res/transition-v21/lb_vertical_grid_entrance_transition.xml
index 45a40e1..ee06953 100644
--- a/v17/leanback/res/layout/lb_card_color_overlay.xml
+++ b/v17/leanback/res/transition-v21/lb_vertical_grid_entrance_transition.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     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.
@@ -15,7 +15,10 @@
      limitations under the License.
 -->
 
-<View
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent" />
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" >
+  <slide
+      android:duration="350"
+      android:interpolator="@android:interpolator/linear_out_slow_in"
+      android:slideEdge="bottom">
+  </slide>
+</transitionSet>
\ No newline at end of file
diff --git a/v17/leanback/res/transition-v21/lb_vertical_grid_return_transition.xml b/v17/leanback/res/transition-v21/lb_vertical_grid_return_transition.xml
new file mode 100644
index 0000000..edb3816
--- /dev/null
+++ b/v17/leanback/res/transition-v21/lb_vertical_grid_return_transition.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.
+-->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" >
+  <slide
+      android:interpolator="@android:interpolator/fast_out_linear_in"
+      android:duration="350"
+      android:slideEdge="bottom">
+      <targets>
+          <target android:excludeId="@+id/title_orb" />
+          <target android:excludeId="@+id/title_text" />
+          <target android:excludeId="@+id/title_badge" />
+      </targets>
+  </slide>
+  <fade
+      android:interpolator="@android:interpolator/fast_out_linear_in"
+      android:duration="350">
+  </fade>
+</transitionSet>
\ No newline at end of file
diff --git a/v17/leanback/res/values-az-rAZ/strings.xml b/v17/leanback/res/values-az-rAZ/strings.xml
deleted file mode 100644
index d1e685f..0000000
--- a/v17/leanback/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="orb_search_action" msgid="5651268540267663887">"Axtarış Fəaliyyəti"</string>
-    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Axtarış"</string>
-    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Axtarış üçün danışın"</string>
-    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Axtarış: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Axtarış üçün danışın: <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
-    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
-    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
-    <string name="lb_playback_controls_play" msgid="731953341987346903">"Oyun"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pauza"</string>
-    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"İrəli Ötürmə"</string>
-    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"İrəli sarı %1$dX"</string>
-    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Geri ötürmə"</string>
-    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Geri sarı %1$dX"</string>
-    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Növbətini atlayın"</string>
-    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Öncəkini atlayın"</string>
-    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Digər fəaliyyətlər"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Bəyənməkdən imtina edin"</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Bəyənin"</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Bəyənməməkdən imtina edin"</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Bəyənməyin"</string>
-    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Təkrarlanmasın"</string>
-    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Hamısını təkrarlayın"</string>
-    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Biri təkrarlansın"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Qarışdırma aktiv edilsin"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Qarışdırma deaktiv edilsin"</string>
-    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Yüksək keyfiyyəti aktiv edin"</string>
-    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yüksək keyfiyyəti deaktiv edin"</string>
-    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Qapalı çəkilişi aktiv edin"</string>
-    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Qapalı çəkilişi deaktiv edin"</string>
-</resources>
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index e7da321..10ee282 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -49,6 +49,10 @@
     </declare-styleable>
 
     <declare-styleable name="lbBaseCardView">
+        <!-- Defines the background of card -->
+        <attr name="cardForeground" format="reference|color"/>
+        <!-- Defines the background of card -->
+        <attr name="cardBackground" format="reference|color"/>
         <!-- Defines the type of the card layout -->
         <attr name="cardType" format="enum">
             <!-- A simple card layout with a single layout region. -->
@@ -116,7 +120,23 @@
     </declare-styleable>
 
     <declare-styleable name="lbImageCardView">
+        <!-- Deprecated. Use 'lbImageCardViewInfoAreaStyle' instead. -->
         <attr name="infoAreaBackground" format="reference|color"/>
+        <!-- Use these attributes to override a ImageCardView's component style. -->
+        <attr name="lbImageCardViewImageStyle" format="reference" />
+        <attr name="lbImageCardViewTitleStyle" format="reference" />
+        <attr name="lbImageCardViewContentStyle" format="reference" />
+        <attr name="lbImageCardViewBadgeStyle" format="reference" />
+        <attr name="lbImageCardViewInfoAreaStyle" format="reference" />
+        <!-- Defines what components the ImageCardView will use. -->
+        <attr name="lbImageCardViewType">
+            <flag name="Title" value="1" />
+            <flag name="Content" value="2" />
+            <flag name="IconOnRight" value="4" />
+            <flag name="IconOnLeft" value="8" />
+            <!-- Only display the main image. -->
+            <flag name="ImageOnly" value="0" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="lbSearchOrbView">
@@ -308,6 +328,14 @@
              fragment stack pop. Default is {@link
              android.support.v17.leanback.R.animator#lb_guidedstep_slide_out_to_end}. -->
         <attr name="guidedStepReturnAnimation" format="reference" />
+        <!-- Theme attribute for the animation used when a guided step element is animated in
+             response to the IME appearing. Default is {@link
+             android.support.v17.leanback.R.animator#lb_guidedstep_slide_up}. -->
+        <attr name="guidedStepImeAppearingAnimation" format="reference" />
+        <!-- Theme attribute for the animation used when a guided step element is animated in
+             response to the IME disappearing. Default is {@link
+             android.support.v17.leanback.R.animator#lb_guidedstep_slide_down}. -->
+        <attr name="guidedStepImeDisappearingAnimation" format="reference" />
 
         <!-- Theme attribute for the animation used when the guidance is animated in at activity
              start. Default is {@link android.support.v17.leanback.R.animator#lb_guidance_entry}.
@@ -431,4 +459,4 @@
 
     </declare-styleable>
 
-</resources>
+</resources>
\ No newline at end of file
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index 275612e..a05a6fc 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -203,6 +203,7 @@
     <dimen name="lb_basic_card_info_height">52dp</dimen>
     <dimen name="lb_basic_card_info_height_no_content">34dp</dimen>
     <dimen name="lb_basic_card_info_padding_top">7dp</dimen>
+    <dimen name="lb_basic_card_info_padding_bottom">8dp</dimen>
     <dimen name="lb_basic_card_info_padding_horizontal">11dp</dimen>
     <dimen name="lb_basic_card_info_text_margin">1dp</dimen>
     <dimen name="lb_basic_card_title_text_size">14sp</dimen>
@@ -226,6 +227,7 @@
     <dimen name="lb_guidedstep_guidance_section_width">576dp</dimen>
     <dimen name="lb_guidedstep_slide_start_distance">-200dp</dimen>
     <dimen name="lb_guidedstep_slide_end_distance">200dp</dimen>
+    <dimen name="lb_guidedstep_slide_ime_distance">-100dp</dimen>
 
     <dimen name="lb_guidance_entry_translationX">-120dp</dimen>
 
diff --git a/v17/leanback/res/values/ids.xml b/v17/leanback/res/values/ids.xml
index 6b67919..d4ba288 100644
--- a/v17/leanback/res/values/ids.xml
+++ b/v17/leanback/res/values/ids.xml
@@ -16,6 +16,7 @@
 -->
  <resources>
      <item type="id" name="lb_focus_animator" />
+     <item type="id" name="lb_shadow_impl" />
      <item type="id" name="lb_slide_transition_value" />
 
      <item type="id" name="lb_control_play_pause" />
@@ -30,5 +31,4 @@
      <item type="id" name="lb_control_shuffle" />
      <item type="id" name="lb_control_high_quality" />
      <item type="id" name="lb_control_closed_captioning" />
-
  </resources>
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index 3ee2821..204c0ee 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -87,14 +87,8 @@
     <style name="Widget.Leanback" parent="Widget.LeanbackBase" />
 
     <style name="Widget.Leanback.BaseCardViewStyle">
-        <item name="android:foreground">@drawable/lb_card_foreground</item>
-    </style>
-
-    <style name="Widget.Leanback.ImageCardViewStyle" parent="Widget.Leanback.BaseCardViewStyle">
-        <item name="cardType">infoUnder</item>
-        <item name="infoVisibility">activated</item>
-        <item name="android:background">@color/lb_basic_card_bg_color</item>
-        <item name="infoAreaBackground">@color/lb_basic_card_info_bg_color</item>
+        <item name="cardForeground">@drawable/lb_card_foreground</item>
+        <item name="cardBackground">@color/lb_basic_card_bg_color</item>
     </style>
 
     <style name="Widget.Leanback.TitleView" >
@@ -104,6 +98,77 @@
         <item name="android:paddingEnd">?attr/browsePaddingEnd</item>
     </style>
 
+    <style name="Widget.Leanback.ImageCardViewStyle" parent="Widget.Leanback.BaseCardViewStyle">
+        <item name="cardType">infoUnder</item>
+        <item name="infoVisibility">activated</item>
+        <!-- In order to keep backward compatibility we have to create an icon on right. -->
+        <item name="lbImageCardViewType">Title|Content|IconOnRight</item>
+        <item name="lbImageCardViewImageStyle">@style/Widget.Leanback.ImageCardView.ImageStyle</item>
+        <item name="lbImageCardViewTitleStyle">@style/Widget.Leanback.ImageCardView.TitleStyle</item>
+        <item name="lbImageCardViewContentStyle">@style/Widget.Leanback.ImageCardView.ContentStyle</item>
+        <item name="lbImageCardViewBadgeStyle">@style/Widget.Leanback.ImageCardView.BadgeStyle</item>
+        <item name="lbImageCardViewInfoAreaStyle">@style/Widget.Leanback.ImageCardView.InfoAreaStyle</item>
+        <!-- Deprecated. Use 'Widget.Leanback.ImageCardView.InfoAreaStyle' instead. -->
+        <item name="infoAreaBackground">@null</item>
+    </style>
+
+    <style name="Widget.Leanback.ImageCardView" />
+
+    <style name="Widget.Leanback.ImageCardView.ImageStyle">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:adjustViewBounds">true</item>
+        <item name="android:contentDescription">@null</item>
+        <item name="android:scaleType">centerCrop</item>
+        <item name="layout_viewType">main</item>
+    </style>
+
+    <style name="Widget.Leanback.ImageCardView.InfoAreaStyle">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_centerHorizontal">true</item>
+        <item name="layout_viewType">info</item>
+        <item name="android:paddingBottom">@dimen/lb_basic_card_info_padding_bottom</item>
+        <item name="android:paddingEnd">@dimen/lb_basic_card_info_padding_horizontal</item>
+        <item name="android:paddingStart">@dimen/lb_basic_card_info_padding_horizontal</item>
+        <item name="android:paddingTop">@dimen/lb_basic_card_info_padding_top</item>
+        <item name="android:background">@color/lb_basic_card_info_bg_color</item>
+    </style>
+	
+    <style name="Widget.Leanback.ImageCardView.TitleStyle">
+        <item name="android:id">@id/title_text</item>
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:maxLines">1</item>
+        <item name="android:layout_marginBottom">@dimen/lb_basic_card_info_text_margin</item>
+        <item name="android:fontFamily">sans-serif-condensed</item>
+        <item name="android:textColor">@color/lb_basic_card_title_text_color</item>
+        <item name="android:textSize">@dimen/lb_basic_card_title_text_size</item>
+        <item name="android:ellipsize">end</item>
+    </style>
+
+    <style name="Widget.Leanback.ImageCardView.ContentStyle">
+        <item name="android:id">@id/content_text</item>
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_alignParentStart">true</item>
+        <item name="android:layout_below">@+id/title_text</item>
+        <item name="android:layout_toStartOf">@+id/extra_badge</item>
+        <item name="android:maxLines">1</item>
+        <item name="android:fontFamily">sans-serif-condensed</item>
+        <item name="android:textColor">@color/lb_basic_card_content_text_color</item>
+        <item name="android:textSize">@dimen/lb_basic_card_content_text_size</item>
+        <item name="android:ellipsize">none</item>
+    </style>
+
+    <style name="Widget.Leanback.ImageCardView.BadgeStyle">
+        <item name="android:id">@id/extra_badge</item>
+        <item name="android:layout_width">@dimen/lb_basic_card_info_badge_size</item>
+        <item name="android:layout_height">@dimen/lb_basic_card_info_badge_size</item>
+        <item name="android:contentDescription">@null</item>
+        <item name="android:scaleType">fitCenter</item>
+    </style>
+
     <style name="Widget.Leanback.Title" />
 
     <style name="Widget.Leanback.Title.Text">
@@ -430,11 +495,13 @@
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
         <item name="android:alpha">@string/lb_guidedactions_item_unselected_text_alpha</item>
-        <item name="android:ellipsize">marquee</item>
+        <item name="android:ellipsize">end</item>
         <item name="android:fontFamily">sans-serif-condensed</item>
         <item name="android:maxLines">@integer/lb_guidedactions_item_title_min_lines</item>
         <item name="android:textColor">@color/lb_guidedactions_item_unselected_text_color</item>
         <item name="android:textSize">@dimen/lb_guidedactions_item_title_font_size</item>
+        <item name="android:background">@null</item>
+        <item name="android:inputType">text</item>
     </style>
 
     <!-- Style for an action's description in a GuidedActionsStylist's default item layout. -->
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index 8178c50..d994b3f 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -95,6 +95,11 @@
         <item name="android:windowReturnTransition">@transition/lb_browse_return_transition</item>
     </style>
 
+    <style name="Theme.Leanback.VerticalGrid" parent="Theme.Leanback">
+        <item name="android:windowEnterTransition">@transition/lb_vertical_grid_enter_transition</item>
+        <item name="android:windowReturnTransition">@transition/lb_vertical_grid_return_transition</item>
+    </style>
+
     <style name="Theme.Leanback.Details" parent="Theme.Leanback">
         <item name="android:windowEnterTransition">@transition/lb_details_enter_transition</item>
         <item name="android:windowReturnTransition">@transition/lb_details_return_transition</item>
@@ -115,6 +120,8 @@
         <item name="guidedStepExitAnimation">@animator/lb_guidedstep_slide_out_to_start</item>
         <item name="guidedStepReentryAnimation">@animator/lb_guidedstep_slide_in_from_start</item>
         <item name="guidedStepReturnAnimation">@animator/lb_guidedstep_slide_out_to_end</item>
+        <item name="guidedStepImeAppearingAnimation">@animator/lb_guidedstep_slide_up</item>
+        <item name="guidedStepImeDisappearingAnimation">@animator/lb_guidedstep_slide_down</item>
         <item name="guidanceEntryAnimation">@animator/lb_guidance_entry</item>
         <item name="guidedActionsEntryAnimation">@animator/lb_guidedactions_entry</item>
 
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 d415de0..c786040 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
@@ -394,11 +394,7 @@
         super.onStart();
         setupChildFragmentLayout();
         setupFocusSearchListener();
-        mRowsFragment.getView().requestFocus();
         if (isEntranceTransitionEnabled()) {
-            // make sure recycler view animation is disabled
-            mRowsFragment.onTransitionPrepare();
-            mRowsFragment.onTransitionStart();
             mRowsFragment.setEntranceTransitionState(false);
         }
     }
@@ -420,4 +416,13 @@
         mRowsFragment.onTransitionEnd();
     }
 
+    @Override
+    protected void onEntranceTransitionPrepare() {
+        mRowsFragment.onTransitionPrepare();
+    }
+
+    @Override
+    protected void onEntranceTransitionStart() {
+        mRowsFragment.onTransitionStart();
+    }
 }
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 afaf5a8..fbce207 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
@@ -396,11 +396,7 @@
         super.onStart();
         setupChildFragmentLayout();
         setupFocusSearchListener();
-        mRowsSupportFragment.getView().requestFocus();
         if (isEntranceTransitionEnabled()) {
-            // make sure recycler view animation is disabled
-            mRowsSupportFragment.onTransitionPrepare();
-            mRowsSupportFragment.onTransitionStart();
             mRowsSupportFragment.setEntranceTransitionState(false);
         }
     }
@@ -422,4 +418,13 @@
         mRowsSupportFragment.onTransitionEnd();
     }
 
+    @Override
+    protected void onEntranceTransitionPrepare() {
+        mRowsSupportFragment.onTransitionPrepare();
+    }
+
+    @Override
+    protected void onEntranceTransitionStart() {
+        mRowsSupportFragment.onTransitionStart();
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java
index 41ecac9..975357e 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java
@@ -19,6 +19,7 @@
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.widget.GuidedAction;
 import android.support.v17.leanback.widget.GuidedActionsStylist;
+import android.support.v17.leanback.widget.ImeKeyMonitor;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.util.Log;
@@ -26,9 +27,14 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -43,6 +49,9 @@
     private static final String TAG = "GuidedActionAdapter";
     private static final boolean DEBUG = false;
 
+    private static final String TAG_EDIT = "EditableAction";
+    private static final boolean DEBUG_EDIT = false;
+
     /**
      * Object listening for click events within a {@link GuidedActionAdapter}.
      */
@@ -66,6 +75,17 @@
     }
 
     /**
+     * Object listening for edit events within a {@link GuidedActionAdapter}.
+     */
+    public interface EditListener {
+
+        /**
+         * Called when the user enters or exits edit mode on an action.
+         */
+        public void onGuidedActionEdited(GuidedAction action, boolean entering);
+    }
+
+    /**
      * View holder containing a {@link GuidedAction}.
      */
     private static class ActionViewHolder extends ViewHolder {
@@ -101,6 +121,7 @@
     private RecyclerView mRecyclerView;
     private final ActionOnKeyListener mActionOnKeyListener;
     private final ActionOnFocusListener mActionOnFocusListener;
+    private final ActionEditListener mActionEditListener;
     private final List<GuidedAction> mActions;
     private ClickListener mClickListener;
     private GuidedActionsStylist mStylist;
@@ -126,13 +147,15 @@
      * @param presenter The presenter that will manage the display of items in this adapter.
      */
     public GuidedActionAdapter(List<GuidedAction> actions, ClickListener clickListener,
-            FocusListener focusListener, GuidedActionsStylist presenter) {
+            FocusListener focusListener, EditListener editListener,
+            GuidedActionsStylist presenter) {
         super();
         mActions = new ArrayList<GuidedAction>(actions);
         mClickListener = clickListener;
         mStylist = presenter;
-        mActionOnKeyListener = new ActionOnKeyListener(clickListener, mActions);
+        mActionOnKeyListener = new ActionOnKeyListener();
         mActionOnFocusListener = new ActionOnFocusListener(focusListener);
+        mActionEditListener = new ActionEditListener(editListener);
     }
 
     /**
@@ -169,7 +192,6 @@
      */
     public void setClickListener(ClickListener clickListener) {
         mClickListener = clickListener;
-        mActionOnKeyListener.setListener(clickListener);
     }
 
     /**
@@ -215,6 +237,16 @@
         v.setOnClickListener(mOnClickListener);
         v.setOnFocusChangeListener(mActionOnFocusListener);
 
+        final EditText edit = vh.getEditableTitleView();
+        if (edit != null) {
+            edit.setPrivateImeOptions("EscapeNorth=1;");
+            edit.setOnEditorActionListener(mActionEditListener);
+            if (edit instanceof ImeKeyMonitor) {
+                ImeKeyMonitor monitor = (ImeKeyMonitor)edit;
+                monitor.setImeKeyListener(mActionEditListener);
+            }
+        }
+
         return new ActionViewHolder(v, vh);
     }
 
@@ -230,6 +262,13 @@
         GuidedAction action = mActions.get(position);
         avh.setAction(action);
         mStylist.onBindViewHolder(avh.mStylistViewHolder, action);
+
+        final EditText edit = avh.mStylistViewHolder.getEditableTitleView();
+        if (edit != null) {
+            int next = getNextEditableActionIndex(action);
+            int flag = (next == -1) ? EditorInfo.IME_ACTION_DONE : EditorInfo.IME_ACTION_NEXT;
+            edit.setImeOptions(flag);
+        }
     }
 
     /**
@@ -240,6 +279,25 @@
         return mActions.size();
     }
 
+    private int getNextEditableActionIndex(GuidedAction action) {
+        int i, size = mActions.size();
+        for (i = 0; i < size; i++) {
+            GuidedAction a = mActions.get(i);
+            if (mActions.get(i) == action) {
+                i++;
+                break;
+            }
+        }
+        for (; i < size; i++) {
+            GuidedAction a = mActions.get(i);
+            if (a.isEditable()) {
+                break;
+            }
+        }
+        int result = (i == size) ? -1 : i;
+        return result;
+    }
+
     private class ActionOnFocusListener implements View.OnFocusChangeListener {
 
         private FocusListener mFocusListener;
@@ -287,19 +345,7 @@
 
     private class ActionOnKeyListener implements View.OnKeyListener {
 
-        private final List<GuidedAction> mActions;
         private boolean mKeyPressed = false;
-        private ClickListener mClickListener;
-
-        public ActionOnKeyListener(ClickListener listener,
-                List<GuidedAction> actions) {
-            mClickListener = listener;
-            mActions = actions;
-        }
-
-        public void setListener(ClickListener listener) {
-            mClickListener = listener;
-        }
 
         private void playSound(View v, int soundEffect) {
             if (v.isSoundEffectsEnabled()) {
@@ -360,10 +406,14 @@
                                     Log.d(TAG, "Enter Key up");
                                 }
 
-                                mStylist.onAnimateItemPressed(avh.mStylistViewHolder,
-                                            mKeyPressed);
-                                handleCheckedActions(avh, action);
-                                mClickListener.onGuidedActionClicked(action);
+                                mStylist.onAnimateItemPressed(avh.mStylistViewHolder, mKeyPressed);
+                                if (action.isEditable()) {
+                                    if (DEBUG_EDIT) Log.v(TAG_EDIT, "openIme click");
+                                    mActionEditListener.openIme(avh, true);
+                                } else {
+                                    handleCheckedActions(avh, action);
+                                    mClickListener.onGuidedActionClicked(action);
+                                }
                                 handled = true;
                             }
                             break;
@@ -403,4 +453,97 @@
             }
         }
     }
+
+    private class ActionEditListener implements OnEditorActionListener,
+            ImeKeyMonitor.ImeKeyListener {
+
+        private EditListener mEditListener;
+
+        public ActionEditListener(EditListener listener) {
+            mEditListener = listener;
+        }
+
+        @Override
+        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+            if (DEBUG_EDIT) Log.v(TAG_EDIT, "IME action: " + actionId);
+            boolean handled = false;
+            if (actionId == EditorInfo.IME_ACTION_NEXT ||
+                actionId == EditorInfo.IME_ACTION_DONE) {
+
+                ActionViewHolder avh = findSubChildViewHolder(v);
+                GuidedAction action = avh.getAction();
+                action.setTitle(v.getText());
+                mClickListener.onGuidedActionClicked(action);
+                int next = getNextEditableActionIndex(action);
+                if (next != -1) {
+                    ViewHolder vh = mRecyclerView.findViewHolderForPosition(next);
+                    if (vh != null) {
+                        if (DEBUG_EDIT) Log.v(TAG_EDIT, "openIme next/done");
+                        handled = true;
+                        openIme((ActionViewHolder)vh, false);
+                    }
+                }
+                if (!handled) {
+                    if (DEBUG_EDIT) Log.v(TAG_EDIT, "closeIme no next");
+                    handled = true;
+                    closeIme(avh);
+                }
+            } else if (actionId == EditorInfo.IME_ACTION_NONE) {
+                if (DEBUG_EDIT) Log.v(TAG_EDIT, "closeIme escape north");
+                // Escape north handling: stay on current item, but close editor
+                handled = true;
+                ActionViewHolder avh = findSubChildViewHolder(v);
+                closeIme(avh);
+            }
+            return handled;
+        }
+
+        @Override
+        public boolean onKeyPreIme(EditText editText, int keyCode, KeyEvent event) {
+            if (DEBUG_EDIT) Log.v(TAG_EDIT, "IME key: " + keyCode);
+            if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
+                ActionViewHolder avh = findSubChildViewHolder(editText);
+                GuidedAction action = avh.getAction();
+                action.setTitle(editText.getText());
+                editText.clearFocus();
+                mEditListener.onGuidedActionEdited(action, false);
+            }
+            return false;
+        }
+
+        public void openIme(ActionViewHolder avh, boolean notify) {
+            View v = avh.mStylistViewHolder.getTitleView();
+            InputMethodManager mgr = (InputMethodManager)
+                    v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+            v.requestFocus();
+            mgr.showSoftInput(v, 0);
+            if (notify) {
+                mEditListener.onGuidedActionEdited(avh.getAction(), true);
+            }
+        }
+
+        public void closeIme(ActionViewHolder avh) {
+            View v = avh.mStylistViewHolder.getTitleView();
+            InputMethodManager mgr = (InputMethodManager)
+                    v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+            v.clearFocus();
+            mgr.hideSoftInputFromWindow(v.getWindowToken(), 0);
+            mEditListener.onGuidedActionEdited(avh.getAction(), false);
+        }
+
+        private ActionViewHolder findSubChildViewHolder(View v) {
+            // Needed because RecyclerView.getChildViewHolder does not traverse the hierarchy
+            ActionViewHolder result = null;
+            ViewParent parent = v.getParent();
+            while (parent != mRecyclerView && parent != null && v != null) {
+                v = (View)parent;
+                parent = parent.getParent();
+            }
+            if (parent != null && v != null) {
+                result = (ActionViewHolder)mRecyclerView.getChildViewHolder(v);
+            }
+            return result;
+        }
+    }
+
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
index b1e87a1..170aa63 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
@@ -211,6 +211,12 @@
     }
 
     /**
+     * Callback invoked when an action's title has been edited.
+     */
+    public void onGuidedActionEdited(GuidedAction action) {
+    }
+
+    /**
      * Adds the specified GuidedStepFragment to the fragment stack, replacing any existing
      * GuidedStepFragments in the stack, and configuring the fragment-to-fragment custom animations.
      * <p>
@@ -354,7 +360,17 @@
         View actionsView = mActionsStylist.onCreateView(inflater, actionContainer);
         actionContainer.addView(actionsView);
 
-        mAdapter = new GuidedActionAdapter(mActions, this, this, mActionsStylist);
+        GuidedActionAdapter.EditListener editListener = new GuidedActionAdapter.EditListener() {
+                @Override
+                public void onGuidedActionEdited(GuidedAction action, boolean entering) {
+                    runImeAnimations(entering);
+                    if (!entering) {
+                        GuidedStepFragment.this.onGuidedActionEdited(action);
+                    }
+                }
+        };
+
+        mAdapter = new GuidedActionAdapter(mActions, this, this, editListener, mActionsStylist);
 
         mListView = mActionsStylist.getActionsGridView();
         mListView.setAdapter(mAdapter);
@@ -522,6 +538,20 @@
                 });
     }
 
+    private void runImeAnimations(boolean entering) {
+        ArrayList<Animator> animators = new ArrayList<Animator>();
+        if (entering) {
+            mGuidanceStylist.onImeAppearing(animators);
+            mActionsStylist.onImeAppearing(animators);
+        } else {
+            mGuidanceStylist.onImeDisappearing(animators);
+            mActionsStylist.onImeDisappearing(animators);
+        }
+        AnimatorSet set = new AnimatorSet();
+        set.playTogether(animators);
+        set.start();
+    }
+
     private Animator createDummyAnimator(final View v, ArrayList<Animator> animators) {
         final AnimatorSet animatorSet = new AnimatorSet();
         animatorSet.playTogether(animators);
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 6b6cc2e..c7d55b9 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
@@ -38,7 +38,7 @@
  * <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and
  * an {@link ObjectAdapter}.
  */
-public class VerticalGridFragment extends BrandedFragment {
+public class VerticalGridFragment extends BaseFragment {
     private static final String TAG = "VerticalGridFragment";
     private static boolean DEBUG = false;
 
@@ -47,6 +47,7 @@
     private VerticalGridPresenter.ViewHolder mGridViewHolder;
     private OnItemViewSelectedListener mOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
+    private Object mSceneAfterEntranceTransition;
     private int mSelectedPosition = -1;
 
     /**
@@ -170,6 +171,13 @@
         gridDock.addView(mGridViewHolder.view);
         mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener);
 
+        mSceneAfterEntranceTransition = sTransitionHelper.createScene(gridDock, new Runnable() {
+            @Override
+            public void run() {
+                setEntranceTransitionState(true);
+            }
+        });
+
         updateAdapter();
     }
 
@@ -183,7 +191,9 @@
     public void onStart() {
         super.onStart();
         setupFocusSearchListener();
-        mGridViewHolder.getGridView().requestFocus();
+        if (isEntranceTransitionEnabled()) {
+            setEntranceTransitionState(false);
+        }
     }
 
     @Override
@@ -210,4 +220,20 @@
             }
         }
     }
+
+    @Override
+    protected Object createEntranceTransition() {
+        return sTransitionHelper.loadTransition(getActivity(),
+                R.transition.lb_vertical_grid_entrance_transition);
+    }
+
+    @Override
+    protected void runEntranceTransition(Object entranceTransition) {
+        sTransitionHelper.runTransition(mSceneAfterEntranceTransition,
+                entranceTransition);
+    }
+
+    void setEntranceTransitionState(boolean afterTransition) {
+        mGridPresenter.setEntranceTransitionState(mGridViewHolder, afterTransition);
+    }
 }
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 0770761..33fe6fc 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
@@ -40,7 +40,7 @@
  * <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and
  * an {@link ObjectAdapter}.
  */
-public class VerticalGridSupportFragment extends BrandedSupportFragment {
+public class VerticalGridSupportFragment extends BaseSupportFragment {
     private static final String TAG = "VerticalGridSupportFragment";
     private static boolean DEBUG = false;
 
@@ -49,6 +49,7 @@
     private VerticalGridPresenter.ViewHolder mGridViewHolder;
     private OnItemViewSelectedListener mOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
+    private Object mSceneAfterEntranceTransition;
     private int mSelectedPosition = -1;
 
     /**
@@ -172,6 +173,13 @@
         gridDock.addView(mGridViewHolder.view);
         mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener);
 
+        mSceneAfterEntranceTransition = sTransitionHelper.createScene(gridDock, new Runnable() {
+            @Override
+            public void run() {
+                setEntranceTransitionState(true);
+            }
+        });
+
         updateAdapter();
     }
 
@@ -185,7 +193,9 @@
     public void onStart() {
         super.onStart();
         setupFocusSearchListener();
-        mGridViewHolder.getGridView().requestFocus();
+        if (isEntranceTransitionEnabled()) {
+            setEntranceTransitionState(false);
+        }
     }
 
     @Override
@@ -212,4 +222,20 @@
             }
         }
     }
+
+    @Override
+    protected Object createEntranceTransition() {
+        return sTransitionHelper.loadTransition(getActivity(),
+                R.transition.lb_vertical_grid_entrance_transition);
+    }
+
+    @Override
+    protected void runEntranceTransition(Object entranceTransition) {
+        sTransitionHelper.runTransition(mSceneAfterEntranceTransition,
+                entranceTransition);
+    }
+
+    void setEntranceTransitionState(boolean afterTransition) {
+        mGridPresenter.setEntranceTransitionState(mGridViewHolder, afterTransition);
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
index 614f12b..085aac3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
 import android.support.v17.leanback.R;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -154,6 +155,14 @@
 
         try {
             mCardType = a.getInteger(R.styleable.lbBaseCardView_cardType, CARD_TYPE_MAIN_ONLY);
+            Drawable cardForeground = a.getDrawable(R.styleable.lbBaseCardView_cardForeground);
+            if (cardForeground != null) {
+                setForeground(cardForeground);
+            }
+            Drawable cardBackground = a.getDrawable(R.styleable.lbBaseCardView_cardBackground);
+            if (cardBackground != null) {
+                setBackground(cardBackground);
+            }
             mInfoVisibility = a.getInteger(R.styleable.lbBaseCardView_infoVisibility,
                     CARD_REGION_VISIBLE_ACTIVATED);
             mExtraVisibility = a.getInteger(R.styleable.lbBaseCardView_extraVisibility,
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 48829eb..4f32ff2 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
@@ -85,7 +85,7 @@
                 mWrapper = null;
             }
             mAnimator.setTimeListener(this);
-            if (mWrapper != null && useDimmer) {
+            if (useDimmer) {
                 mDimmer = ColorOverlayDimmer.createDefault(view.getContext());
             } else {
                 mDimmer = null;
@@ -99,9 +99,16 @@
             mView.setScaleY(scale);
             if (mWrapper != null) {
                 mWrapper.setShadowFocusLevel(level);
-                if (mDimmer != null) {
-                    mDimmer.setActiveLevel(level);
-                    mWrapper.setOverlayColor(mDimmer.getPaint().getColor());
+            } else {
+                ShadowOverlayHelper.setShadowFocusLevel(mView, level);
+            }
+            if (mDimmer != null) {
+                mDimmer.setActiveLevel(level);
+                int color = mDimmer.getPaint().getColor();
+                if (mWrapper != null) {
+                    mWrapper.setOverlayColor(color);
+                } else {
+                    ShadowOverlayHelper.setForegroundColor(mView, color);
                 }
             }
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
new file mode 100644
index 0000000..c9bed58
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
@@ -0,0 +1,78 @@
+package android.support.v17.leanback.widget;
+
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewGroup;
+
+final class ForegroundHelper {
+
+    final static ForegroundHelper sInstance = new ForegroundHelper();
+    ForegroundHelperVersionImpl mImpl;
+
+    /**
+     * Interface implemented by classes that support Shadow.
+     */
+    static interface ForegroundHelperVersionImpl {
+
+        public void setForeground(View view, Drawable drawable);
+
+        public Drawable getForeground(View view);
+    }
+
+    /**
+     * Implementation used on api 23 (and above).
+     */
+    private static final class ForegroundHelperApi23Impl implements ForegroundHelperVersionImpl {
+        @Override
+        public void setForeground(View view, Drawable drawable) {
+            ForegroundHelperApi23.setForeground(view, drawable);
+        }
+
+        @Override
+        public Drawable getForeground(View view) {
+            return ForegroundHelperApi23.getForeground(view);
+        }
+    }
+
+    /**
+     * Stub implementation
+     */
+    private static final class ForegroundHelperStubImpl implements ForegroundHelperVersionImpl {
+        @Override
+        public void setForeground(View view, Drawable drawable) {
+        }
+
+        @Override
+        public Drawable getForeground(View view) {
+            return null;
+        }
+    }
+
+    private ForegroundHelper() {
+        if (supportsForeground()) {
+            mImpl = new ForegroundHelperApi23Impl();
+        } else {
+            mImpl = new ForegroundHelperStubImpl();
+        }
+    }
+
+    public static ForegroundHelper getInstance() {
+        return sInstance;
+    }
+
+    /**
+     * Returns true if view.setForeground() is supported.
+     */
+    public static boolean supportsForeground() {
+        return Build.VERSION.SDK_INT >= 23;
+    }
+
+    public Drawable getForeground(View view) {
+        return mImpl.getForeground(view);
+    }
+
+    public void setForeground(View view, Drawable drawable) {
+        mImpl.setForeground(view, drawable);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FragmentAnimationProvider.java b/v17/leanback/src/android/support/v17/leanback/widget/FragmentAnimationProvider.java
index 8bd0007..b1f6169 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FragmentAnimationProvider.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FragmentAnimationProvider.java
@@ -69,4 +69,16 @@
      */
     public abstract void onFragmentReturn(@NonNull List<Animator> animators);
 
+    /**
+     * Animates the fragment in response to the IME appearing.
+     * @param animators A list of animations to which this provider's animations should be added.
+     */
+    public abstract void onImeAppearing(@NonNull List<Animator> animators);
+
+    /**
+     * Animates the fragment in response to the IME disappearing.
+     * @param animators A list of animations to which this provider's animations should be added.
+     */
+    public abstract void onImeDisappearing(@NonNull List<Animator> animators);
+
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
index 8d12510..b95c114 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
@@ -59,6 +59,8 @@
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepExitAnimation
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepReentryAnimation
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepReturnAnimation
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceContainerStyle
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceTitleStyle
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceDescriptionStyle
@@ -280,6 +282,28 @@
         addAnimator(animators, mIconView, R.attr.guidedStepReturnAnimation);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onImeAppearing(@NonNull List<Animator> animators) {
+        addAnimator(animators, mTitleView, R.attr.guidedStepImeAppearingAnimation);
+        addAnimator(animators, mBreadcrumbView, R.attr.guidedStepImeAppearingAnimation);
+        addAnimator(animators, mDescriptionView, R.attr.guidedStepImeAppearingAnimation);
+        addAnimator(animators, mIconView, R.attr.guidedStepImeAppearingAnimation);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onImeDisappearing(@NonNull List<Animator> animators) {
+        addAnimator(animators, mTitleView, R.attr.guidedStepImeDisappearingAnimation);
+        addAnimator(animators, mBreadcrumbView, R.attr.guidedStepImeDisappearingAnimation);
+        addAnimator(animators, mDescriptionView, R.attr.guidedStepImeDisappearingAnimation);
+        addAnimator(animators, mIconView, R.attr.guidedStepImeDisappearingAnimation);
+    }
+
     private void addAnimator(List<Animator> animators, View v, int attrId) {
         if (v != null) {
             Context ctx = v.getContext();
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
index e4db2eb..30edc2f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
@@ -50,6 +50,7 @@
         private boolean mMultilineDescription;
         private boolean mHasNext;
         private boolean mInfoOnly;
+        private boolean mEditable = false;
         private int mCheckSetId = NO_CHECK_SET;
         private boolean mEnabled = true;
         private Intent mIntent;
@@ -68,6 +69,7 @@
 
             // Subclass values
             action.mIntent = mIntent;
+            action.mEditable = mEditable;
             action.mChecked = mChecked;
             action.mCheckSetId = mCheckSetId;
             action.mMultilineDescription = mMultilineDescription;
@@ -138,11 +140,27 @@
         }
 
         /**
+         * Indicates whether this action is editable. Note: Editable actions cannot also be
+         * checked, or belong to a check set.
+         * @param editable Whether this action is editable.
+         */
+        public Builder editable(boolean editable) {
+            mEditable = editable;
+            if (mChecked || mCheckSetId != NO_CHECK_SET) {
+                throw new IllegalArgumentException("Editable actions cannot also be checked");
+            }
+            return this;
+        }
+
+        /**
          * Indicates whether this action is initially checked.
          * @param checked Whether this action is checked.
          */
         public Builder checked(boolean checked) {
             mChecked = checked;
+            if (mEditable) {
+                throw new IllegalArgumentException("Editable actions cannot also be checked");
+            }
             return this;
         }
 
@@ -154,6 +172,9 @@
          */
         public Builder checkSetId(int checkSetId) {
             mCheckSetId = checkSetId;
+            if (mEditable) {
+                throw new IllegalArgumentException("Editable actions cannot also be in check sets");
+            }
             return this;
         }
 
@@ -195,9 +216,10 @@
         }
     }
 
-    private boolean mChecked;
+    private boolean mEditable;
     private boolean mMultilineDescription;
     private boolean mHasNext;
+    private boolean mChecked;
     private boolean mInfoOnly;
     private int mCheckSetId;
     private boolean mEnabled;
@@ -217,6 +239,14 @@
     }
 
     /**
+     * Returns the title of this action.
+     * @return The title set when this action was built.
+     */
+    public void setTitle(CharSequence title) {
+        setLabel1(title);
+    }
+
+    /**
      * Returns the description of this action.
      * @return The description set when this action was built.
      */
@@ -233,6 +263,14 @@
     }
 
     /**
+     * Returns whether this action is editable.
+     * @return true if the action is editable, false otherwise.
+     */
+    public boolean isEditable() {
+        return mEditable;
+    }
+
+    /**
      * Returns whether this action is checked.
      * @return true if the action is currently checked, false otherwise.
      */
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionEditText.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionEditText.java
new file mode 100644
index 0000000..8e052fb
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionEditText.java
@@ -0,0 +1,58 @@
+/*
+ * 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.content.Context;
+import android.support.v17.leanback.widget.ImeKeyMonitor.ImeKeyListener;
+import android.util.AttributeSet;
+import android.widget.EditText;
+import android.view.KeyEvent;
+
+/**
+ * A custom EditText that satisfies the IME key monitoring requirements of GuidedStepFragment.
+ */
+public class GuidedActionEditText extends EditText implements ImeKeyMonitor {
+
+    private ImeKeyListener mKeyListener;
+
+    public GuidedActionEditText(Context ctx) {
+        this(ctx, null);
+    }
+
+    public GuidedActionEditText(Context ctx, AttributeSet attrs) {
+        this(ctx, attrs, android.R.attr.editTextStyle);
+    }
+
+    public GuidedActionEditText(Context ctx, AttributeSet attrs, int defStyleAttr) {
+        super(ctx, attrs, defStyleAttr);
+    }
+
+    @Override
+    public void setImeKeyListener(ImeKeyListener listener) {
+        mKeyListener = listener;
+    }
+
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        boolean result = false;
+        if (mKeyListener != null) {
+            result = mKeyListener.onKeyPreIme(this, keyCode, event);
+        }
+        if (!result) {
+            result = super.onKeyPreIme(keyCode, event);
+        }
+        return result;
+    }
+
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
index 15943b4..07dd4c9 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
@@ -40,6 +40,7 @@
 import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
+import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -81,7 +82,17 @@
  * </ul><p>
  * These view IDs are allowed to be missing, in which case the corresponding views in {@link
  * GuidedActionsStylist.ViewHolder} will be null.
+ * <p>
+ * In order to support editable actions, the view associated with guidedactions_item_title should
+ * be a subclass of {@link android.widget.EditText}, and should satisfy the {@link
+ * ImeKeyMonitor} interface.
  *
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepEntryAnimation
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepExitAnimation
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepReentryAnimation
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepReturnAnimation
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsEntryAnimation
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorShowAnimation
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorHideAnimation
@@ -158,6 +169,14 @@
         }
 
         /**
+         * Convenience method to return an editable version of the title, if possible,
+         * or null if the title view isn't an EditText.
+         */
+        public EditText getEditableTitleView() {
+            return (mTitleView instanceof EditText) ? (EditText)mTitleView : null;
+        }
+
+        /**
          * Returns the description view within this view holder's view.
          */
         public TextView getDescriptionView() {
@@ -300,7 +319,10 @@
      * Subclasses may override to provide their own customized layouts. The base implementation
      * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions_item}. If overridden,
      * the substituted layout should contain matching IDs for any views that should be managed by
-     * the base class; this can be achieved by starting with a copy of the base layout file.
+     * the base class; this can be achieved by starting with a copy of the base layout file. Note
+     * that in order for the item to support editing, the title view should both subclass {@link
+     * android.widget.EditText} and implement {@link ImeKeyMonitor}; see {@link
+     * GuidedActionEditText}.
      * @return The resource ID of the layout to be inflated to define the view to display an
      * individual GuidedAction.
      */
@@ -483,6 +505,24 @@
         animators.add(createAnimator(mSelectorView, R.attr.guidedStepReturnAnimation));
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onImeAppearing(@NonNull List<Animator> animators) {
+        animators.add(createAnimator(mActionsGridView, R.attr.guidedStepImeAppearingAnimation));
+        animators.add(createAnimator(mSelectorView, R.attr.guidedStepImeAppearingAnimation));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onImeDisappearing(@NonNull List<Animator> animators) {
+        animators.add(createAnimator(mActionsGridView, R.attr.guidedStepImeDisappearingAnimation));
+        animators.add(createAnimator(mSelectorView, R.attr.guidedStepImeDisappearingAnimation));
+    }
+
     /*
      * ==========================================
      * Private methods
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 2c1c7e0..08eb617 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ImageCardView.java
@@ -18,60 +18,288 @@
 import android.graphics.drawable.Drawable;
 import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
-import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 /**
- * A subclass of {@link BaseCardView} with an {@link ImageView} as its main region.
+ * A subclass of {@link BaseCardView} with an {@link ImageView} as its main
+ * region. The {@link ImageCardView} is highly customizable and can be used for
+ * various use-cases by adjusting the ImageViewCard's type to any combination of
+ * Title, Content, Badge or ImageOnly.
+ * <p>
+ * <h3>Styling</h3> There are three different ways to style the ImageCardView.
+ * <br>
+ * No matter what way you use, all your styles applied to an ImageCardView have
+ * to extend the style {@link R.style#Widget_Leanback_ImageCardViewStyle}.
+ * <p>
+ * <u>Example:</u><br>
+ * 
+ * <pre>
+ * {@code <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle">
+        <item name="cardBackground">#F0F</item>
+        <item name="lbImageCardViewType">Title|Content</item>
+        <item name="lbImageCardViewInfoAreaStyle">@style/ImageCardViewColoredInfoArea</item>
+        <item name="lbImageCardViewTitleStyle">@style/ImageCardViewColoredTitle</item>
+    </style>}
+ * </pre>
+ * <p>
+ * The first possibility is to set a custom Style in the Leanback Theme's
+ * attribute <code>imageCardViewStyle</code>. The style set here, is the default
+ * style for all ImageCardViews. The other two possibilities allow you to style
+ * a particular ImageCardView. This is usefull if you want to create multiple
+ * types of cards. E.g. you might want to display a card with only a title and
+ * another one with title and content. Thus you need to define two different
+ * <code>ImageCardViewStyles</code> and apply them to the ImageCardViews. You
+ * can do this by either using a the {@link #ImageCardView(Context, int)}
+ * constructor and passing a style as second argument or by setting the style in
+ * a layout.
+ * <p>
+ * <u>Example (using constructor):</u><br>
+ * 
+ * <pre>
+ * {@code
+ *     new ImageCardView(context, R.style.CustomImageCardViewStyle);
+ * }
+ * </pre>
+ * 
+ * <u>Example (using style attribute in a layout):</u><br>
+ * 
+ * <pre>
+ * {@code     <android.support.v17.leanback.widget.ImageCardView
+        android:id="@+id/imageCardView"
+        style="@style/CustomImageCardViewStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+    </android.support.v17.leanback.widget.ImageCardView>}
+ * </pre>
+ * <p>
+ * You can style all ImageCardView's components such as the title, content,
+ * badge, infoArea and the image itself by extending the corresponding style and
+ * overriding the specific attribute in your custom
+ * <code>ImageCardViewStyle</code>.
+ * 
+ * <h3>Components</h3> The ImageCardView contains three components which can be
+ * combined in any combination:
+ * <ul>
+ * <li>Title: The card's title</li>
+ * <li>Content: A short description</li>
+ * <li>Badge: An icon which can be displayed on the right or left side of the
+ * card.</li>
+ * </ul>
+ * In order to choose the components you want to use in your ImageCardView, you
+ * have to specify them in the <code>lbImageCardViewType</code> attribute of
+ * your custom <code>ImageCardViewStyle</code>. You can combine the following
+ * values: <code>Title, Content, IconOnRight, IconOnLeft, ImageOnly</code>.
+ * <p>
+ * <u>Examples:</u><br>
+ * 
+ * <pre>
+ * {@code <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle">
+        ...
+        <item name="lbImageCardViewType">Title|Content|IconOnLeft</item>
+        ...
+    </style>}
+ * </pre>
+ * 
+ * <pre>
+ * {@code <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle">
+        ...
+        <item name="lbImageCardViewType">ImageOnly</item>
+        ...
+    </style>}
+ * </pre>
+ * 
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackTheme_imageCardViewStyle
+ * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewType
+ * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewTitleStyle
+ * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewContentStyle
+ * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewBadgeStyle
+ * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewImageStyle
+ * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewInfoAreaStyle
  */
 public class ImageCardView extends BaseCardView {
 
+    public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0;
+    public static final int CARD_TYPE_FLAG_TITLE = 1;
+    public static final int CARD_TYPE_FLAG_CONTENT = 2;
+    public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4;
+    public static final int CARD_TYPE_FLAG_ICON_LEFT = 8;
+
     private ImageView mImageView;
-    private View mInfoArea;
+    private ViewGroup mInfoArea;
     private TextView mTitleView;
     private TextView mContentView;
     private ImageView mBadgeImage;
     private boolean mAttachedToWindow;
 
+    /**
+     * Create an ImageCardView using a given style for customization.
+     * 
+     * @param context
+     *            The Context the view is running in, through which it can
+     *            access the current theme, resources, etc.
+     * @param styleResId
+     *            The resourceId of the style you want to apply to the
+     *            ImageCardView. The style has to extend
+     *            {@link R.style#Widget_Leanback_ImageCardViewStyle}.
+     */
+    public ImageCardView(Context context, int styleResId) {
+        super(new ContextThemeWrapper(context, styleResId), null, 0);
+        buildImageCardView(styleResId);
+    }
+
+    /**
+     * @see #View(Context, AttributeSet, int)
+     */
+    public ImageCardView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(getStyledContext(context, attrs, defStyleAttr), attrs, defStyleAttr);
+        buildImageCardView(getImageCardViewStyle(context, attrs, defStyleAttr));
+    }
+
+    private void buildImageCardView(int styleResId) {
+        // Make sure the ImageCardView is focusable.
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+
+        LayoutInflater inflater = LayoutInflater.from(getContext());
+        inflater.inflate(R.layout.lb_image_card_view, this);
+        TypedArray cardAttrs = getContext().obtainStyledAttributes(styleResId, R.styleable.lbImageCardView);
+        int cardType = cardAttrs.getInt(R.styleable.lbImageCardView_lbImageCardViewType, CARD_TYPE_FLAG_IMAGE_ONLY);
+        boolean hasImageOnly = cardType == CARD_TYPE_FLAG_IMAGE_ONLY;
+        boolean hasTitle = (cardType & CARD_TYPE_FLAG_TITLE) == CARD_TYPE_FLAG_TITLE;
+        boolean hasContent = (cardType & CARD_TYPE_FLAG_CONTENT) == CARD_TYPE_FLAG_CONTENT;
+        boolean hasIconRight = (cardType & CARD_TYPE_FLAG_ICON_RIGHT) == CARD_TYPE_FLAG_ICON_RIGHT;
+        boolean hasIconLeft = !hasIconRight && (cardType & CARD_TYPE_FLAG_ICON_LEFT) == CARD_TYPE_FLAG_ICON_LEFT;
+
+        mImageView = (ImageView) findViewById(R.id.main_image);
+        if (mImageView.getDrawable() == null) {
+            mImageView.setVisibility(View.INVISIBLE);
+        }
+
+        mInfoArea = (ViewGroup) findViewById(R.id.info_field);
+        if (hasImageOnly) {
+            removeView(mInfoArea);
+            cardAttrs.recycle();
+            return;
+        }
+        // Create children
+        if (hasTitle) {
+            mTitleView = (TextView) inflater.inflate(R.layout.lb_image_card_view_themed_title, mInfoArea, false);
+            mInfoArea.addView(mTitleView);
+        }
+
+        if (hasContent) {
+            mContentView = (TextView) inflater.inflate(R.layout.lb_image_card_view_themed_content, mInfoArea, false);
+            mInfoArea.addView(mContentView);
+        }
+
+        if (hasIconRight || hasIconLeft) {
+            int layoutId = R.layout.lb_image_card_view_themed_badge_right;
+            if (hasIconLeft) {
+                layoutId = R.layout.lb_image_card_view_themed_badge_left;
+            }
+            mBadgeImage = (ImageView) inflater.inflate(layoutId, mInfoArea, false);
+            mInfoArea.addView(mBadgeImage);
+        }
+
+        // Set up LayoutParams for children
+        if (hasTitle && !hasContent && mBadgeImage != null) {
+            RelativeLayout.LayoutParams relativeLayoutParams = (RelativeLayout.LayoutParams) mTitleView
+                    .getLayoutParams();
+            // Adjust title TextView if there is an icon but no content
+            if (hasIconLeft) {
+                relativeLayoutParams.addRule(RelativeLayout.END_OF, mBadgeImage.getId());
+            } else {
+                relativeLayoutParams.addRule(RelativeLayout.START_OF, mBadgeImage.getId());
+            }
+            mTitleView.setLayoutParams(relativeLayoutParams);
+        }
+
+        // Set up LayoutParams for children
+        if (hasContent) {
+            RelativeLayout.LayoutParams relativeLayoutParams = (RelativeLayout.LayoutParams) mContentView
+                    .getLayoutParams();
+            if (!hasTitle) {
+                relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+            }
+            // Adjust content TextView if icon is on the left
+            if (hasIconLeft) {
+                relativeLayoutParams.removeRule(RelativeLayout.START_OF);
+                relativeLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
+                relativeLayoutParams.addRule(RelativeLayout.END_OF, mBadgeImage.getId());
+            }
+            mContentView.setLayoutParams(relativeLayoutParams);
+        }
+
+        if (mBadgeImage != null) {
+            RelativeLayout.LayoutParams relativeLayoutParams = (RelativeLayout.LayoutParams) mBadgeImage
+                    .getLayoutParams();
+            if (hasContent) {
+                relativeLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mContentView.getId());
+            } else if (hasTitle) {
+                relativeLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mTitleView.getId());
+            }
+            mBadgeImage.setLayoutParams(relativeLayoutParams);
+        }
+
+        // Backward compatibility: Newly created ImageCardViews should change
+        // the InfoArea's background color in XML using the corresponding style.
+        // However, since older implementations might make use of the
+        // 'infoAreaBackground' attribute, we have to make sure to support it.
+        // If the user has set a specific value here, it will differ from null.
+        // In this case, we do want to override the value set in the style.
+        Drawable background = cardAttrs.getDrawable(R.styleable.lbImageCardView_infoAreaBackground);
+        if (null != background) {
+            setInfoAreaBackground(background);
+        }
+        // Backward compatibility: There has to be an icon in the default
+        // version. If there is one, we have to set it's visibility to 'GONE'.
+        // Disabling 'adjustIconVisibility' allows the user to set the icon's
+        // visibility state in XML rather than code.
+        if (mBadgeImage != null && mBadgeImage.getDrawable() == null) {
+            mBadgeImage.setVisibility(View.GONE);
+        }
+        cardAttrs.recycle();
+    }
+
+    private static Context getStyledContext(Context context, AttributeSet attrs, int defStyleAttr) {
+        int style = getImageCardViewStyle(context, attrs, defStyleAttr);
+        return new ContextThemeWrapper(context, style);
+    }
+
+    private static int getImageCardViewStyle(Context context, AttributeSet attrs, int defStyleAttr) {
+        // Read style attribute defined in XML layout.
+        int style = null == attrs ? 0 : attrs.getStyleAttribute();
+        if (0 == style) {
+            // Not found? Read global ImageCardView style from Theme attribute.
+            TypedArray styledAttrs = context.obtainStyledAttributes(R.styleable.LeanbackTheme);
+            style = styledAttrs.getResourceId(R.styleable.LeanbackTheme_imageCardViewStyle, 0);
+            styledAttrs.recycle();
+        }
+        return style;
+    }
+
+    /**
+     * @see #View(Context)
+     */
     public ImageCardView(Context context) {
         this(context, null);
     }
 
+    /**
+     * @see #View(Context, AttributeSet)
+     */
     public ImageCardView(Context context, AttributeSet attrs) {
         this(context, attrs, R.attr.imageCardViewStyle);
     }
 
-    public ImageCardView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        LayoutInflater inflater = LayoutInflater.from(context);
-        View v = inflater.inflate(R.layout.lb_image_card_view, this);
-
-        mImageView = (ImageView) v.findViewById(R.id.main_image);
-        mImageView.setVisibility(View.INVISIBLE);
-        mInfoArea = v.findViewById(R.id.info_field);
-        mTitleView = (TextView) v.findViewById(R.id.title_text);
-        mContentView = (TextView) v.findViewById(R.id.content_text);
-        mBadgeImage = (ImageView) v.findViewById(R.id.extra_badge);
-
-        if (mInfoArea != null) {
-            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbImageCardView,
-                    defStyle, 0);
-            try {
-                setInfoAreaBackground(
-                        a.getDrawable(R.styleable.lbImageCardView_infoAreaBackground));
-            } finally {
-                a.recycle();
-            }
-        }
-    }
-
     /**
      * Returns the main image view.
      */
@@ -170,7 +398,7 @@
 
     /**
      * Sets the info area background color.
-     */    
+     */
     public void setInfoAreaBackgroundColor(@ColorInt int color) {
         if (mInfoArea != null) {
             mInfoArea.setBackgroundColor(color);
@@ -184,7 +412,6 @@
         if (mTitleView == null) {
             return;
         }
-
         mTitleView.setText(text);
     }
 
@@ -206,7 +433,6 @@
         if (mContentView == null) {
             return;
         }
-
         mContentView.setText(text);
     }
 
@@ -229,7 +455,7 @@
             return;
         }
         mBadgeImage.setImageDrawable(drawable);
-        if (drawable != null && mContentView!= null && mContentView.getVisibility() != GONE) {
+        if (drawable != null) {
             mBadgeImage.setVisibility(View.VISIBLE);
         } else {
             mBadgeImage.setVisibility(View.GONE);
@@ -250,8 +476,8 @@
     private void fadeIn() {
         mImageView.setAlpha(0f);
         if (mAttachedToWindow) {
-            mImageView.animate().alpha(1f).setDuration(mImageView.getResources().getInteger(
-                    android.R.integer.config_shortAnimTime));
+            mImageView.animate().alpha(1f)
+                    .setDuration(mImageView.getResources().getInteger(android.R.integer.config_shortAnimTime));
         }
     }
 
@@ -276,4 +502,5 @@
         mImageView.setAlpha(1f);
         super.onDetachedFromWindow();
     }
+
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ImeKeyMonitor.java b/v17/leanback/src/android/support/v17/leanback/widget/ImeKeyMonitor.java
new file mode 100644
index 0000000..4691ad2
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ImeKeyMonitor.java
@@ -0,0 +1,47 @@
+/*
+ * 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.widget.EditText;
+import android.view.KeyEvent;
+
+/**
+ * Interface for an EditText subclass that can delegate calls to onKeyPreIme up to a registered
+ * listener.
+ * <p>
+ * Used in editable actions within {@link android.support.v17.leanback.app.GuidedStepFragment} to
+ * allow for custom back key handling. Specifically, this is used to implement the behavior that
+ * dismissing the IME also clears edit text focus. Clients who need to supply custom layouts for
+ * {@link GuidedActionsStylist} with their own EditText classes should satisfy this interface in
+ * order to inherit this behavior.
+ */
+public interface ImeKeyMonitor {
+
+    /**
+     * Listener interface for key events intercepted pre-IME by edit text objects.
+     */
+    public interface ImeKeyListener {
+        /**
+         * Callback invoked from EditText's onKeyPreIme method override. Returning true tells the
+         * caller that the key event is handled and should not be propagated.
+         */
+        public abstract boolean onKeyPreIme(EditText editText, int keyCode, KeyEvent event);
+    }
+
+    /**
+     * Set the listener for this edit text object. The listener's onKeyPreIme method will be
+     * invoked from the host edit text's onKeyPreIme method.
+     */
+    public void setImeKeyListener(ImeKeyListener listener);
+}
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 9e588eb..1c5c1f9 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
@@ -15,6 +15,7 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.os.Build;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.system.Settings;
 import android.util.Log;
@@ -96,6 +97,13 @@
         }
 
         @Override
+        protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
+            if (mShadowOverlayHelper != null) {
+                mShadowOverlayHelper.onViewCreated(viewHolder.itemView);
+            }
+        }
+
+        @Override
         public void onBind(final ItemBridgeAdapter.ViewHolder viewHolder) {
             // Only when having an OnItemClickListner, we will attach the OnClickListener.
             if (mRowViewHolder.getOnItemViewClickedListener() != null) {
@@ -122,9 +130,13 @@
 
         @Override
         public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-            if (needsDefaultListSelectEffect()) {
+            if (mShadowOverlayHelper != null && mShadowOverlayHelper.needsOverlay()) {
                 int dimmedColor = mRowViewHolder.mColorDimmer.getPaint().getColor();
-                ((ShadowOverlayContainer) viewHolder.itemView).setOverlayColor(dimmedColor);
+                if (mShadowOverlayHelper.getWrapper() != null) {
+                    ((ShadowOverlayContainer) viewHolder.itemView).setOverlayColor(dimmedColor);
+                } else {
+                    ShadowOverlayHelper.setForegroundColor(viewHolder.itemView, dimmedColor);
+                }
             }
             mRowViewHolder.syncActivatedStatus(viewHolder.itemView);
         }
@@ -145,6 +157,7 @@
     private int mBrowseRowsFadingEdgeLength = -1;
     private boolean mRoundedCornersEnabled = true;
     private HashMap<Presenter, Integer> mRecycledPoolSize = new HashMap<Presenter, Integer>();
+    private ShadowOverlayHelper mShadowOverlayHelper;
 
     private static int sSelectedRowTopPadding;
     private static int sExpandedSelectedRowTopPadding;
@@ -253,44 +266,24 @@
         return mUseFocusDimmer;
     }
 
-    private ItemBridgeAdapter.Wrapper mCardWrapper = new ItemBridgeAdapter.Wrapper() {
-        @Override
-        public View createWrapper(View root) {
-            ShadowOverlayContainer wrapper = new ShadowOverlayContainer(root.getContext());
-            wrapper.setLayoutParams(
-                    new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-            if (isUsingZOrder(root.getContext())) {
-                wrapper.useDynamicShadow();
-            } else {
-                wrapper.useStaticShadow();
-            }
-            wrapper.initialize(needsDefaultShadow(),
-                    needsDefaultListSelectEffect(),
-                    areChildRoundedCornersEnabled());
-            return wrapper;
-        }
-        @Override
-        public void wrap(View wrapper, View wrapped) {
-            ((ShadowOverlayContainer) wrapper).wrap(wrapped);
-        }
-    };
-
     @Override
     protected void initializeRowViewHolder(RowPresenter.ViewHolder holder) {
         super.initializeRowViewHolder(holder);
         final ViewHolder rowViewHolder = (ViewHolder) holder;
+        Context context = holder.view.getContext();
+        if (mShadowOverlayHelper == null) {
+            mShadowOverlayHelper = new ShadowOverlayHelper(context, needsDefaultListSelectEffect(),
+                    needsDefaultShadow(), areChildRoundedCornersEnabled(), isUsingZOrder(context));
+        }
         rowViewHolder.mItemBridgeAdapter = new ListRowPresenterItemBridgeAdapter(rowViewHolder);
-        if (needsDefaultListSelectEffect() || needsDefaultShadow()
-                || areChildRoundedCornersEnabled()) {
-            rowViewHolder.mItemBridgeAdapter.setWrapper(mCardWrapper);
-        }
-        if (needsDefaultShadow()) {
-            ShadowOverlayContainer.prepareParentForShadow(rowViewHolder.mGridView);
-        }
+        // set wrapper if needed
+        rowViewHolder.mItemBridgeAdapter.setWrapper(mShadowOverlayHelper.getWrapper());
+        mShadowOverlayHelper.prepareParentForShadow(rowViewHolder.mGridView);
+
         FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter,
                 mFocusZoomFactor, mUseFocusDimmer);
-        rowViewHolder.mGridView.setFocusDrawingOrderEnabled(
-                !isUsingZOrder(rowViewHolder.getGridView().getContext()));
+        rowViewHolder.mGridView.setFocusDrawingOrderEnabled(mShadowOverlayHelper.getShadowType()
+                == ShadowOverlayHelper.SHADOW_STATIC);
         rowViewHolder.mGridView.setOnChildSelectedListener(
                 new OnChildSelectedListener() {
             @Override
@@ -545,7 +538,7 @@
      * Subclass may return false to disable.
      */
     public boolean isUsingDefaultShadow() {
-        return ShadowOverlayContainer.supportsShadow();
+        return ShadowOverlayHelper.supportsShadow();
     }
 
     /**
@@ -554,8 +547,7 @@
      * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false.
      */
     public boolean isUsingZOrder(Context context) {
-        return ShadowOverlayContainer.supportsDynamicShadow() &&
-                !Settings.getInstance(context).preferStaticShadows();
+        return !Settings.getInstance(context).preferStaticShadows();
     }
 
     /**
@@ -615,12 +607,18 @@
     @Override
     protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) {
         super.onSelectLevelChanged(holder);
-        if (needsDefaultListSelectEffect()) {
+        if (mShadowOverlayHelper != null && mShadowOverlayHelper.needsOverlay()) {
             ViewHolder vh = (ViewHolder) holder;
             int dimmedColor = vh.mColorDimmer.getPaint().getColor();
-            for (int i = 0, count = vh.mGridView.getChildCount(); i < count; i++) {
-                ShadowOverlayContainer wrapper = (ShadowOverlayContainer) vh.mGridView.getChildAt(i);
-                wrapper.setOverlayColor(dimmedColor);
+            if (mShadowOverlayHelper.getWrapper() != null) {
+                for (int i = 0, count = vh.mGridView.getChildCount(); i < count; i++) {
+                    ((ShadowOverlayContainer) vh.mGridView.getChildAt(i))
+                            .setOverlayColor(dimmedColor);
+                }
+            } else {
+                for (int i = 0, count = vh.mGridView.getChildCount(); i < count; i++) {
+                    ShadowOverlayHelper.setForegroundColor(vh.mGridView.getChildAt(i), dimmedColor);
+                }
             }
             if (vh.mGridView.getFadingLeftEdge()) {
                 vh.mGridView.invalidate();
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 f1db00b..17225f8 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
@@ -58,7 +58,7 @@
         final TextView mCurrentTime;
         final TextView mTotalTime;
         final ProgressBar mProgressBar;
-        int mCurrentTimeInSeconds;
+        int mCurrentTimeInSeconds = -1;
         StringBuilder mTotalTimeStringBuilder = new StringBuilder();
         StringBuilder mCurrentTimeStringBuilder = new StringBuilder();
         int mCurrentTimeMarginStart;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java
index e1c5979..f21670c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RoundedRectHelper.java
@@ -33,6 +33,10 @@
         return sInstance;
     }
 
+    public static boolean supportsRoundedCorner() {
+        return Build.VERSION.SDK_INT >= 21;
+    }
+
     /**
      * Sets or removes a rounded rectangle outline on the given view.
      */
@@ -65,7 +69,7 @@
     }
 
     private RoundedRectHelper() {
-        if (Build.VERSION.SDK_INT >= 21) {
+        if (supportsRoundedCorner()) {
             mImpl = new Api21Impl();
         } else {
             mImpl = new StubImpl();
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 4f078f1..6ab830f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
@@ -642,12 +642,16 @@
      * Changes the visibility of views.  The entrance transition will be run against the views that
      * change visibilities.  A subclass may override and begin with calling
      * super.setEntranceTransitionState().  This method is called by the fragment,
-     * it should not call it directly by the application.
+     * it should not be called directly by the application.
+     *
+     * @param holder         The ViewHolder of the row.
+     * @param afterEntrance  true if children of row participating in entrance transition
+     *                       should be set to visible, false otherwise.
      */
-    public void setEntranceTransitionState(ViewHolder holder, boolean afterTransition) {
+    public void setEntranceTransitionState(ViewHolder holder, boolean afterEntrance) {
         if (holder.mHeaderViewHolder != null &&
                 holder.mHeaderViewHolder.view.getVisibility() != View.GONE) {
-            holder.mHeaderViewHolder.view.setVisibility(afterTransition ?
+            holder.mHeaderViewHolder.view.setVisibility(afterEntrance ?
                     View.VISIBLE : View.INVISIBLE);
         }
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
index be70575..9532635 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
@@ -32,7 +32,7 @@
      */
     static interface ShadowHelperVersionImpl {
         public Object addDynamicShadow(
-                ViewGroup shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners);
+                View shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners);
         public void setZ(View view, float z);
         public void setShadowFocusLevel(Object impl, float level);
     }
@@ -43,7 +43,7 @@
     private static final class ShadowHelperStubImpl implements ShadowHelperVersionImpl {
         @Override
         public Object addDynamicShadow(
-                ViewGroup shadowContainer, float focusedZ, float unfocusedZ, boolean roundedCorners) {
+                View shadowContainer, float focusedZ, float unfocusedZ, boolean roundedCorners) {
             // do nothing
             return null;
         }
@@ -65,7 +65,7 @@
     private static final class ShadowHelperApi21Impl implements ShadowHelperVersionImpl {
         @Override
         public Object addDynamicShadow(
-                ViewGroup shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners) {
+                View shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners) {
             return ShadowHelperApi21.addDynamicShadow(
                     shadowContainer, unfocusedZ, focusedZ, roundedCorners);
         }
@@ -102,7 +102,7 @@
     }
 
     public Object addDynamicShadow(
-            ViewGroup shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners) {
+            View shadowContainer, float unfocusedZ, float focusedZ, boolean roundedCorners) {
         return mImpl.addDynamicShadow(shadowContainer, unfocusedZ, focusedZ, roundedCorners);
     }
 
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 e367494..84a30e4 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
@@ -20,6 +20,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
 import android.graphics.Rect;
 
 /**
@@ -51,20 +54,19 @@
     /**
      * No shadow.
      */
-    public static final int SHADOW_NONE = 1;
+    public static final int SHADOW_NONE = ShadowOverlayHelper.SHADOW_NONE;
 
     /**
      * Shadows are fixed.
      */
-    public static final int SHADOW_STATIC = 2;
+    public static final int SHADOW_STATIC = ShadowOverlayHelper.SHADOW_STATIC;
 
     /**
      * Shadows depend on the size, shape, and position of the view.
      */
-    public static final int SHADOW_DYNAMIC = 3;
+    public static final int SHADOW_DYNAMIC = ShadowOverlayHelper.SHADOW_DYNAMIC;
 
     private boolean mInitialized;
-    private View mColorDimOverlay;
     private Object mShadowImpl;
     private View mWrappedView;
     private boolean mRoundedCorners;
@@ -72,15 +74,26 @@
     private float mUnfocusedZ;
     private float mFocusedZ;
     private static final Rect sTempRect = new Rect();
+    private Paint mOverlayPaint;
+    private int mOverlayColor;
 
+    /**
+     * Create ShadowOverlayContainer and auto select shadow type.
+     */
     public ShadowOverlayContainer(Context context) {
         this(context, null, 0);
     }
 
+    /**
+     * Create ShadowOverlayContainer and auto select shadow type.
+     */
     public ShadowOverlayContainer(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
+    /**
+     * Create ShadowOverlayContainer and auto select shadow type.
+     */
     public ShadowOverlayContainer(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         useStaticShadow();
@@ -88,6 +101,17 @@
     }
 
     /**
+     * Create ShadowOverlayContainer with specific shadowType.
+     */
+    public ShadowOverlayContainer(Context context,
+            int shadowType, boolean hasColorDimOverlay, boolean roundedCorners) {
+        super(context);
+        mUnfocusedZ = getResources().getDimension(R.dimen.lb_material_shadow_normal_z);
+        mFocusedZ = getResources().getDimension(R.dimen.lb_material_shadow_focused_z);
+        initialize(shadowType, hasColorDimOverlay, roundedCorners);
+    }
+
+    /**
      * Return true if the platform sdk supports shadow.
      */
     public static boolean supportsShadow() {
@@ -164,29 +188,59 @@
 
     /**
      * Initialize shadows, color overlay, and rounded corners.  All are optional.
+     * Shadow type are auto-selected based on {@link #useStaticShadow()} and
+     * {@link #useDynamicShadow()} call.
+     * @deprecated use {@link #initialize(int, boolean, boolean)} instead.
      */
+    @Deprecated
     public void initialize(boolean hasShadow, boolean hasColorDimOverlay, boolean roundedCorners) {
+        int shadowType;
+        if (!hasShadow) {
+            shadowType = SHADOW_NONE;
+        } else {
+            shadowType = mShadowType;
+        }
+        initialize(shadowType, hasColorDimOverlay, roundedCorners);
+    }
+
+    /**
+     * Initialize shadows, color overlay, and rounded corners.  All are optional.
+     */
+    public void initialize(int shadowType, boolean hasColorDimOverlay, boolean roundedCorners) {
         if (mInitialized) {
             throw new IllegalStateException();
         }
         mInitialized = true;
-        if (hasShadow) {
-            switch (mShadowType) {
-                case SHADOW_DYNAMIC:
-                    mShadowImpl = ShadowHelper.getInstance().addDynamicShadow(
-                            this, mUnfocusedZ, mFocusedZ, roundedCorners);
-                    break;
-                case SHADOW_STATIC:
-                    mShadowImpl = StaticShadowHelper.getInstance().addStaticShadow(
-                            this, roundedCorners);
-                    break;
-            }
+        mShadowType = shadowType;
+        switch (mShadowType) {
+            case SHADOW_DYNAMIC:
+                mShadowImpl = ShadowHelper.getInstance().addDynamicShadow(
+                        this, mUnfocusedZ, mFocusedZ, roundedCorners);
+                break;
+            case SHADOW_STATIC:
+                mShadowImpl = StaticShadowHelper.getInstance().addStaticShadow(this);
+                break;
         }
         mRoundedCorners = roundedCorners;
         if (hasColorDimOverlay) {
-            mColorDimOverlay = LayoutInflater.from(getContext())
-                    .inflate(R.layout.lb_card_color_overlay, this, false);
-            addView(mColorDimOverlay);
+            setWillNotDraw(false);
+            mOverlayColor = Color.TRANSPARENT;
+            mOverlayPaint = new Paint();
+            mOverlayPaint.setColor(mOverlayColor);
+            mOverlayPaint.setStyle(Paint.Style.FILL);
+        } else {
+            setWillNotDraw(true);
+            mOverlayPaint = null;
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        if (mOverlayPaint != null && mOverlayColor != Color.TRANSPARENT) {
+            canvas.drawRect(mWrappedView.getLeft(), mWrappedView.getTop(),
+                    mWrappedView.getRight(), mWrappedView.getBottom(),
+                    mOverlayPaint);
         }
     }
 
@@ -195,19 +249,7 @@
      */
     public void setShadowFocusLevel(float level) {
         if (mShadowImpl != null) {
-            if (level < 0f) {
-                level = 0f;
-            } else if (level > 1f) {
-                level = 1f;
-            }
-            switch (mShadowType) {
-                case SHADOW_DYNAMIC:
-                    ShadowHelper.getInstance().setShadowFocusLevel(mShadowImpl, level);
-                    break;
-                case SHADOW_STATIC:
-                    StaticShadowHelper.getInstance().setShadowFocusLevel(mShadowImpl, level);
-                    break;
-            }
+            ShadowOverlayHelper.setShadowFocusLevel(mShadowImpl, mShadowType, level);
         }
     }
 
@@ -215,8 +257,12 @@
      * Set color (with alpha) of the overlay.
      */
     public void setOverlayColor(@ColorInt int overlayColor) {
-        if (mColorDimOverlay != null) {
-            mColorDimOverlay.setBackgroundColor(overlayColor);
+        if (mOverlayPaint != null) {
+            if (overlayColor != mOverlayColor) {
+                mOverlayColor = overlayColor;
+                mOverlayPaint.setColor(overlayColor);
+                invalidate();
+            }
         }
     }
 
@@ -227,15 +273,11 @@
         if (!mInitialized || mWrappedView != null) {
             throw new IllegalStateException();
         }
-        if (mColorDimOverlay != null) {
-            addView(view, indexOfChild(mColorDimOverlay));
-        } else {
-            addView(view);
+        addView(view);
+        if (mRoundedCorners && mShadowType == SHADOW_STATIC) {
+            RoundedRectHelper.getInstance().setClipToRoundedOutline(view, true);
         }
         mWrappedView = view;
-        if (mRoundedCorners) {
-            RoundedRectHelper.getInstance().setClipToRoundedOutline(mWrappedView, true);
-        }
     }
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayHelper.java
new file mode 100644
index 0000000..457e882
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayHelper.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.system.Settings;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.View;
+
+
+/**
+ * ShadowOverlayHelper is a helper class for shadow, overlay and rounded corner.
+ * Initialize it with all the options and it decides the best strategy.
+ * <li> For shadow:  it may use 9-patch with opticalBounds or Z-value based shadow for
+ *                   API >= 21.  When 9-patch is used, it requires a ShadowOverlayContainer
+ *                   created by ItemBridgeAdapter.Wrapper to include 9-patch views.
+ * <li> For overlay: it may use ShadowOverlayContainer which overrides draw() or it may
+ *                   use setForeground(new ColorDrawable()) for API>=23.  The foreground support
+ *                   might be disabled if rounded corner is applied due to performance reason.
+ * <li> For rounded-corner:  it uses a ViewOutlineProvider for API>=21.
+ * @hide
+ */
+public final class ShadowOverlayHelper {
+
+    /**
+     * No shadow.
+     */
+    public static final int SHADOW_NONE = 1;
+
+    /**
+     * Shadows are fixed.
+     */
+    public static final int SHADOW_STATIC = 2;
+
+    /**
+     * Shadows depend on the size, shape, and position of the view.
+     */
+    public static final int SHADOW_DYNAMIC = 3;
+
+    int mShadowType = SHADOW_NONE;
+    boolean mNeedsOverlay;
+    boolean mNeedsRoundedCorner;
+    boolean mNeedsShadow;
+    boolean mNeedsWrapper;
+    private ItemBridgeAdapter.Wrapper mCardWrapper;
+
+    float mUnfocusedZ;
+    float mFocusedZ;
+
+    /**
+     * Return true if the platform sdk supports shadow.
+     */
+    public static boolean supportsShadow() {
+        return StaticShadowHelper.getInstance().supportsShadow();
+    }
+
+    /**
+     * Returns true if the platform sdk supports dynamic shadows.
+     */
+    public static boolean supportsDynamicShadow() {
+        return ShadowHelper.getInstance().supportsDynamicShadow();
+    }
+
+    /**
+     * Returns true if the platform sdk supports rounded corner through outline.
+     */
+    public static boolean supportsRoundedCorner() {
+        return RoundedRectHelper.supportsRoundedCorner();
+    }
+
+    /**
+     * Returns true if view.setForeground() is supported.
+     */
+    public static boolean supportsForeground() {
+        return ForegroundHelper.supportsForeground();
+    }
+
+    /**
+     * Create ShadowHelper that includes all options.
+     *
+     * @param context               Context that required to query options
+     * @param needsOverlay          true if overlay (dim) is needed
+     * @param needsShadow           true if shadow is needed
+     * @param needsRoundedCorner    true if roundedCorner is needed.
+     * @param preferZOrder          true if prefer dynamic shadow otherwise static shadow is used.
+     */
+    public ShadowOverlayHelper(Context context,
+            boolean needsOverlay, boolean needsShadow, boolean needsRoundedCorner,
+            boolean preferZOrder) {
+        mNeedsOverlay = needsOverlay;
+        mNeedsRoundedCorner = needsRoundedCorner && supportsRoundedCorner();
+        mNeedsShadow = needsShadow && supportsShadow();
+
+        // Force to use wrapper to avoid rebuild rounded corner outline on animating foreground
+        // drawable.  See b/22724385
+        final boolean forceWrapperForOverlay = mNeedsRoundedCorner;
+
+        // figure out shadow type and if we need use wrapper:
+        if (mNeedsShadow) {
+            // if static shadow is prefered or dynamic shadow is not supported,
+            // use static shadow,  otherwise use dynamic shadow.
+            if (!preferZOrder || !supportsDynamicShadow()) {
+                mShadowType = SHADOW_STATIC;
+                // static shadow requires ShadowOverlayContainer to support crossfading
+                // of two shadow views.
+                mNeedsWrapper = true;
+            } else {
+                useDynamicShadow(context);
+                mNeedsWrapper = ((!supportsForeground() || forceWrapperForOverlay) && mNeedsOverlay);
+            }
+        } else {
+            mShadowType = SHADOW_NONE;
+            mNeedsWrapper = ((!supportsForeground() || forceWrapperForOverlay) && mNeedsOverlay);
+        }
+
+        if (mNeedsWrapper) {
+            mCardWrapper = new ItemBridgeAdapter.Wrapper() {
+                @Override
+                public View createWrapper(View root) {
+                    Context context = root.getContext();
+                    ShadowOverlayContainer wrapper = createShadowOverlayContainer(context);
+                    wrapper.setLayoutParams(
+                            new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+                    return wrapper;
+                }
+                @Override
+                public void wrap(View wrapper, View wrapped) {
+                    ((ShadowOverlayContainer) wrapper).wrap(wrapped);
+                }
+            };
+        }
+    }
+
+    /**
+     * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container
+     * before using shadow.  Depending on Shadow type, optical bounds might be applied.
+     */
+    public void prepareParentForShadow(ViewGroup parent) {
+        if (mShadowType == SHADOW_STATIC) {
+            StaticShadowHelper.getInstance().prepareParent(parent);
+        }
+    }
+
+    void useDynamicShadow(Context context) {
+        Resources res = context.getResources();
+        useDynamicShadow(res.getDimension(R.dimen.lb_material_shadow_normal_z),
+                res.getDimension(R.dimen.lb_material_shadow_focused_z));
+    }
+
+    void useDynamicShadow(float unfocusedZ, float focusedZ) {
+        mShadowType = SHADOW_DYNAMIC;
+        mUnfocusedZ = unfocusedZ;
+        mFocusedZ = focusedZ;
+    }
+
+    public int getShadowType() {
+        return mShadowType;
+    }
+
+    public boolean needsOverlay() {
+        return mNeedsOverlay;
+    }
+
+    public boolean needsRoundedCorner() {
+        return mNeedsRoundedCorner;
+    }
+
+    ShadowOverlayContainer createShadowOverlayContainer(Context context) {
+        return new ShadowOverlayContainer(context, mShadowType, mNeedsOverlay,
+                mNeedsRoundedCorner);
+    }
+
+    public ItemBridgeAdapter.Wrapper getWrapper() {
+        return mCardWrapper;
+    }
+
+    /**
+     * Set foreground color for view other than ShadowOverlayContainer.
+     */
+    public static void setForegroundColor(View view, int color) {
+        Drawable d = ForegroundHelper.getInstance().getForeground(view);
+        if (d instanceof ColorDrawable) {
+            ((ColorDrawable) d).setColor(color);
+        } else {
+            ForegroundHelper.getInstance().setForeground(view, new ColorDrawable(color));
+        }
+    }
+
+    /**
+     * Called on view is created.
+     * @param view
+     */
+    public void onViewCreated(View view) {
+        if (!mNeedsWrapper) {
+            if (!mNeedsShadow) {
+                if (mNeedsRoundedCorner) {
+                    RoundedRectHelper.getInstance().setClipToRoundedOutline(view, true);
+                }
+            } else {
+                if (mShadowType == SHADOW_DYNAMIC) {
+                    Object tag = ShadowHelper.getInstance().addDynamicShadow(
+                            view, mUnfocusedZ, mFocusedZ, mNeedsRoundedCorner);
+                    view.setTag(R.id.lb_shadow_impl, tag);
+                }
+            }
+        }
+    }
+
+    public static Object getNoneWrapperDyamicShadowImpl(View view) {
+        return view.getTag(R.id.lb_shadow_impl);
+    }
+
+    /**
+     * Set shadow focus level (0 to 1). 0 for unfocused, 1f for fully focused.
+     * This is for view other than ShadowOverlayContainer.
+     */
+    public static void setShadowFocusLevel(View view, float level) {
+        setShadowFocusLevel(getNoneWrapperDyamicShadowImpl(view), SHADOW_DYNAMIC, level);
+    }
+
+    static void setShadowFocusLevel(Object impl, int shadowType, float level) {
+        if (impl != null) {
+            if (level < 0f) {
+                level = 0f;
+            } else if (level > 1f) {
+                level = 1f;
+            }
+            switch (shadowType) {
+                case SHADOW_DYNAMIC:
+                    ShadowHelper.getInstance().setShadowFocusLevel(impl, level);
+                    break;
+                case SHADOW_STATIC:
+                    StaticShadowHelper.getInstance().setShadowFocusLevel(impl, level);
+                    break;
+            }
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
index 4d8411b..022ff1d 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
@@ -34,7 +34,7 @@
      */
     static interface ShadowHelperVersionImpl {
         public void prepareParent(ViewGroup parent);
-        public Object addStaticShadow(ViewGroup shadowContainer, boolean roundedCorners);
+        public Object addStaticShadow(ViewGroup shadowContainer);
         public void setShadowFocusLevel(Object impl, float level);
     }
 
@@ -48,7 +48,7 @@
         }
 
         @Override
-        public Object addStaticShadow(ViewGroup shadowContainer, boolean roundedCorners) {
+        public Object addStaticShadow(ViewGroup shadowContainer) {
             // do nothing
             return null;
         }
@@ -69,8 +69,7 @@
         }
 
         @Override
-        public Object addStaticShadow(ViewGroup shadowContainer, boolean roundedCorners) {
-            // Static shadows are always rounded
+        public Object addStaticShadow(ViewGroup shadowContainer) {
             return ShadowHelperJbmr2.addShadow(shadowContainer);
         }
 
@@ -105,8 +104,8 @@
         mImpl.prepareParent(parent);
     }
 
-    public Object addStaticShadow(ViewGroup shadowContainer, boolean roundedCorners) {
-        return mImpl.addStaticShadow(shadowContainer, roundedCorners);
+    public Object addStaticShadow(ViewGroup shadowContainer) {
+        return mImpl.addStaticShadow(shadowContainer);
     }
 
     public void setShadowFocusLevel(Object impl, float level) {
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 16b66cd..8f5df72 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
@@ -31,6 +31,13 @@
 
     class VerticalGridItemBridgeAdapter extends ItemBridgeAdapter {
         @Override
+        protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
+            if (mShadowOverlayHelper != null) {
+                mShadowOverlayHelper.onViewCreated(viewHolder.itemView);
+            }
+        }
+
+        @Override
         public void onBind(final ItemBridgeAdapter.ViewHolder itemViewHolder) {
             // Only when having an OnItemClickListner, we attach the OnClickListener.
             if (getOnItemViewClickedListener() != null) {
@@ -86,6 +93,7 @@
     private OnItemViewSelectedListener mOnItemViewSelectedListener;
     private OnItemViewClickedListener mOnItemViewClickedListener;
     private boolean mRoundedCornersEnabled = true;
+    private ShadowOverlayHelper mShadowOverlayHelper;
 
     /**
      * Constructs a VerticalGridPresenter with defaults.
@@ -170,7 +178,7 @@
      * Subclass may return false to disable.
      */
     public boolean isUsingDefaultShadow() {
-        return ShadowOverlayContainer.supportsShadow();
+        return ShadowOverlayHelper.supportsShadow();
     }
 
     /**
@@ -194,8 +202,7 @@
      * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false.
      */
     public boolean isUsingZOrder(Context context) {
-        return ShadowOverlayContainer.supportsDynamicShadow() &&
-                !Settings.getInstance(context).preferStaticShadows();
+        return !Settings.getInstance(context).preferStaticShadows();
     }
 
     final boolean needsDefaultShadow() {
@@ -238,21 +245,6 @@
         return new ViewHolder((VerticalGridView) root.findViewById(R.id.browse_grid));
     }
 
-    private ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() {
-        @Override
-        public View createWrapper(View root) {
-            ShadowOverlayContainer wrapper = new ShadowOverlayContainer(root.getContext());
-            wrapper.setLayoutParams(
-                    new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-            wrapper.initialize(needsDefaultShadow(), true, areChildRoundedCornersEnabled());
-            return wrapper;
-        }
-        @Override
-        public void wrap(View wrapper, View wrapped) {
-            ((ShadowOverlayContainer) wrapper).wrap(wrapped);
-        }
-    };
-
     /**
      * Called after a {@link VerticalGridPresenter.ViewHolder} is created.
      * Subclasses may override this method and start by calling
@@ -268,12 +260,15 @@
         vh.getGridView().setNumColumns(mNumColumns);
         vh.mInitialized = true;
 
-        vh.mItemBridgeAdapter.setWrapper(mWrapper);
-        if (needsDefaultShadow() || areChildRoundedCornersEnabled()) {
-            ShadowOverlayContainer.prepareParentForShadow(vh.getGridView());
-            ((ViewGroup) vh.view).setClipChildren(false);
+        Context context = vh.mGridView.getContext();
+        if (mShadowOverlayHelper == null) {
+            mShadowOverlayHelper = new ShadowOverlayHelper(context, mUseFocusDimmer,
+                    needsDefaultShadow(), areChildRoundedCornersEnabled(), isUsingZOrder(context));
         }
-        vh.getGridView().setFocusDrawingOrderEnabled(!isUsingZOrder(vh.getGridView().getContext()));
+        vh.mItemBridgeAdapter.setWrapper(mShadowOverlayHelper.getWrapper());
+        mShadowOverlayHelper.prepareParentForShadow(vh.mGridView);
+        vh.getGridView().setFocusDrawingOrderEnabled(mShadowOverlayHelper.getShadowType()
+                == ShadowOverlayHelper.SHADOW_STATIC);
         FocusHighlightHelper.setupBrowseItemFocusHighlight(vh.mItemBridgeAdapter,
                 mFocusZoomFactor, mUseFocusDimmer);
 
@@ -345,4 +340,18 @@
             }
         }
     }
+
+    /**
+     * Changes the visibility of views.  The entrance transition will be run against the views that
+     * change visibilities.  This method is called by the fragment, it should not be called
+     * directly by the application.
+     *
+     * @param holder         The ViewHolder for the vertical grid.
+     * @param afterEntrance  true if children of vertical grid participating in entrance transition
+     *                       should be set to visible, false otherwise.
+     */
+    public void setEntranceTransitionState(VerticalGridPresenter.ViewHolder holder,
+            boolean afterEntrance) {
+        holder.mGridView.setChildrenVisibility(afterEntrance? View.VISIBLE : View.INVISIBLE);
+    }
 }
diff --git a/v17/preference-leanback/res/color/lb_preference_item_primary_text_color.xml b/v17/preference-leanback/res/color/lb_preference_item_primary_text_color.xml
new file mode 100644
index 0000000..efdf1c0
--- /dev/null
+++ b/v17/preference-leanback/res/color/lb_preference_item_primary_text_color.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
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/lb_preference_item_primary_text_color_disabled" />
+    <item android:color="@color/lb_preference_item_primary_text_color_default"/>
+</selector>
diff --git a/v17/preference-leanback/res/color/lb_preference_item_secondary_text_color.xml b/v17/preference-leanback/res/color/lb_preference_item_secondary_text_color.xml
new file mode 100644
index 0000000..68bb81a
--- /dev/null
+++ b/v17/preference-leanback/res/color/lb_preference_item_secondary_text_color.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
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/lb_preference_item_secondary_text_color_disabled" />
+    <item android:color="@color/lb_preference_item_secondary_text_color_default"/>
+</selector>
diff --git a/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml b/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml
index 9fae0f8..2a4218b 100644
--- a/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml
+++ b/v17/preference-leanback/res/layout/leanback_list_preference_fragment.xml
@@ -35,11 +35,11 @@
             android:id="@+id/decor_title"
             android:layout_width="match_parent"
             android:layout_height="@dimen/lb_preference_decor_title_text_height"
+            android:layout_marginTop="@dimen/lb_preference_decor_title_margin_top"
+            android:layout_marginStart="@dimen/lb_preference_decor_title_margin_start"
+            android:layout_marginEnd="@dimen/lb_preference_decor_title_margin_end"
             android:fontFamily="sans-serif-condensed"
             android:gravity="center_vertical"
-            android:paddingTop="@dimen/lb_preference_decor_title_padding_top"
-            android:paddingStart="@dimen/lb_preference_decor_title_padding_start"
-            android:paddingEnd="@dimen/lb_preference_decor_title_padding_end"
             android:singleLine="true"
             android:textSize="@dimen/lb_preference_decor_title_text_size"
             android:textColor="?android:attr/textColorPrimary"
@@ -50,6 +50,7 @@
         android:id="@android:id/message"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:fontFamily="sans-serif-condensed"
         android:paddingTop="14dp"
         android:paddingBottom="14dp"
         android:paddingStart="24dp"
diff --git a/v17/preference-leanback/res/layout/leanback_list_preference_item_multi.xml b/v17/preference-leanback/res/layout/leanback_list_preference_item_multi.xml
index 3b1345c..728ecff 100644
--- a/v17/preference-leanback/res/layout/leanback_list_preference_item_multi.xml
+++ b/v17/preference-leanback/res/layout/leanback_list_preference_item_multi.xml
@@ -18,20 +18,35 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/container"
     android:layout_width="match_parent"
-    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:layout_height="wrap_content"
     android:background="?android:attr/selectableItemBackground"
     android:clickable="true"
     android:focusable="true"
     android:descendantFocusability="blocksDescendants"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:paddingStart="@dimen/lb_preference_item_padding_start"
+    android:paddingEnd="@dimen/lb_preference_item_padding_end" >
+
     <CheckBox
         android:id="@+id/button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-    <TextView
-        android:id="@android:id/title"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:gravity="center_vertical" />
+        android:layout_width="@dimen/lb_preference_item_icon_size"
+        android:layout_height="@dimen/lb_preference_item_icon_size"
+        android:layout_marginEnd="@dimen/lb_preference_item_icon_margin_end"
+        android:layout_gravity="center_vertical" />
+
+    <LinearLayout android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" />
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom"
+            android:fontFamily="sans-serif-condensed"
+            android:textColor="@color/lb_preference_item_primary_text_color"
+            android:textSize="@dimen/lb_preference_item_primary_text_size"/>
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" />
+    </LinearLayout>
+
 </LinearLayout>
diff --git a/v17/preference-leanback/res/layout/leanback_list_preference_item_single.xml b/v17/preference-leanback/res/layout/leanback_list_preference_item_single.xml
index eaf42a4..354ca41 100644
--- a/v17/preference-leanback/res/layout/leanback_list_preference_item_single.xml
+++ b/v17/preference-leanback/res/layout/leanback_list_preference_item_single.xml
@@ -18,20 +18,35 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/container"
     android:layout_width="match_parent"
-    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:layout_height="wrap_content"
     android:background="?android:attr/selectableItemBackground"
     android:clickable="true"
     android:focusable="true"
     android:descendantFocusability="blocksDescendants"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:paddingStart="@dimen/lb_preference_item_padding_start"
+    android:paddingEnd="@dimen/lb_preference_item_padding_end" >
+
     <RadioButton
         android:id="@+id/button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-    <TextView
-        android:id="@android:id/title"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:gravity="center_vertical" />
+        android:layout_width="@dimen/lb_preference_item_icon_size"
+        android:layout_height="@dimen/lb_preference_item_icon_size"
+        android:layout_marginEnd="@dimen/lb_preference_item_icon_margin_end"
+        android:layout_gravity="center_vertical" />
+
+    <LinearLayout android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" />
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom"
+            android:fontFamily="sans-serif-condensed"
+            android:textColor="@color/lb_preference_item_primary_text_color"
+            android:textSize="@dimen/lb_preference_item_primary_text_size"/>
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" />
+    </LinearLayout>
+
 </LinearLayout>
diff --git a/v17/preference-leanback/res/layout/leanback_preference.xml b/v17/preference-leanback/res/layout/leanback_preference.xml
new file mode 100644
index 0000000..85baff3
--- /dev/null
+++ b/v17/preference-leanback/res/layout/leanback_preference.xml
@@ -0,0 +1,74 @@
+<?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/container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:clickable="true"
+    android:focusable="true"
+    android:descendantFocusability="blocksDescendants"
+    android:orientation="horizontal"
+    android:paddingStart="@dimen/lb_preference_item_padding_start"
+    android:paddingEnd="@dimen/lb_preference_item_padding_end" >
+
+    <FrameLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical" >
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            />
+    </FrameLayout>
+
+    <LinearLayout android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" />
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom"
+            android:fontFamily="sans-serif-condensed"
+            android:textColor="@color/lb_preference_item_primary_text_color"
+            android:textSize="@dimen/lb_preference_item_primary_text_size"/>
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="sans-serif-condensed"
+            android:textColor="@color/lb_preference_item_secondary_text_color"
+            android:textSize="@dimen/lb_preference_item_secondary_text_size"
+            android:maxLines="4" />
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" />
+    </LinearLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/v17/preference-leanback/res/layout/leanback_preference_category.xml b/v17/preference-leanback/res/layout/leanback_preference_category.xml
new file mode 100644
index 0000000..9b978f3
--- /dev/null
+++ b/v17/preference-leanback/res/layout/leanback_preference_category.xml
@@ -0,0 +1,33 @@
+<?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="@dimen/lb_preference_category_height"
+    android:clipToPadding="false"
+    android:paddingStart="@dimen/lb_preference_item_padding_start"
+    android:paddingEnd="@dimen/lb_preference_item_padding_end">
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:fontFamily="sans-serif-condensed"
+        android:textColor="?android:attr/colorAccent"
+        android:textSize="@dimen/lb_preference_category_text_size"/>
+</FrameLayout>
diff --git a/v17/preference-leanback/res/layout/leanback_preference_fragment.xml b/v17/preference-leanback/res/layout/leanback_preference_fragment.xml
index d119c2d..199e0f7 100644
--- a/v17/preference-leanback/res/layout/leanback_preference_fragment.xml
+++ b/v17/preference-leanback/res/layout/leanback_preference_fragment.xml
@@ -20,6 +20,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/lb_preference_decor_list_background"
+    android:elevation="@dimen/lb_preference_decor_elevation"
     android:orientation="vertical"
     android:transitionGroup="false"
     >
@@ -29,17 +30,18 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="?attr/defaultBrandColor"
+        android:elevation="@dimen/lb_preference_decor_title_container_elevation"
         android:transitionGroup="false"
         >
         <TextView
             android:id="@+id/decor_title"
             android:layout_width="match_parent"
             android:layout_height="@dimen/lb_preference_decor_title_text_height"
+            android:layout_marginTop="@dimen/lb_preference_decor_title_margin_top"
+            android:layout_marginStart="@dimen/lb_preference_decor_title_margin_start"
+            android:layout_marginEnd="@dimen/lb_preference_decor_title_margin_end"
             android:fontFamily="sans-serif-condensed"
             android:gravity="center_vertical"
-            android:paddingTop="@dimen/lb_preference_decor_title_padding_top"
-            android:paddingStart="@dimen/lb_preference_decor_title_padding_start"
-            android:paddingEnd="@dimen/lb_preference_decor_title_padding_end"
             android:singleLine="true"
             android:textSize="@dimen/lb_preference_decor_title_text_size"
             android:textColor="?android:attr/textColorPrimary"
diff --git a/v17/preference-leanback/res/layout/leanback_preference_information.xml b/v17/preference-leanback/res/layout/leanback_preference_information.xml
new file mode 100644
index 0000000..18da8d9
--- /dev/null
+++ b/v17/preference-leanback/res/layout/leanback_preference_information.xml
@@ -0,0 +1,61 @@
+<?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/container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:clickable="false"
+    android:focusable="false"
+    android:descendantFocusability="blocksDescendants"
+    android:orientation="horizontal"
+    android:paddingStart="@dimen/lb_preference_item_padding_start"
+    android:paddingEnd="@dimen/lb_preference_item_padding_end" >
+
+    <LinearLayout android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" />
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom"
+            android:fontFamily="sans-serif-condensed"
+            android:textColor="@color/lb_preference_item_primary_text_color"
+            android:textSize="@dimen/lb_preference_item_primary_text_size"/>
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="sans-serif-condensed"
+            android:textColor="@color/lb_preference_item_secondary_text_color"
+            android:textSize="@dimen/lb_preference_item_secondary_text_size"
+            android:maxLines="4" />
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" />
+    </LinearLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/v17/preference-leanback/res/values/colors.xml b/v17/preference-leanback/res/values/colors.xml
index de6c888..30a373a 100644
--- a/v17/preference-leanback/res/values/colors.xml
+++ b/v17/preference-leanback/res/values/colors.xml
@@ -16,4 +16,11 @@
   -->
 <resources>
     <color name="lb_preference_decor_list_background">#263238</color>
+
+    <color name="lb_preference_item_primary_text_color_default">#EEEEEE</color>
+    <color name="lb_preference_item_primary_text_color_disabled">#4DEEEEEE</color>
+
+    <color name="lb_preference_item_secondary_text_color_default">#B3EEEEEE</color>
+    <color name="lb_preference_item_secondary_text_color_disabled">#4DEEEEEE</color>
+
 </resources>
diff --git a/v17/preference-leanback/res/values/dimens.xml b/v17/preference-leanback/res/values/dimens.xml
index 49763fe..f3d36af 100644
--- a/v17/preference-leanback/res/values/dimens.xml
+++ b/v17/preference-leanback/res/values/dimens.xml
@@ -15,11 +15,27 @@
   ~ limitations under the License
   -->
 <resources>
-  <dimen name="lb_preference_decor_title_text_height">64dp</dimen>
-  <dimen name="lb_preference_decor_title_padding_top">27dp</dimen>
-  <dimen name="lb_preference_decor_title_padding_start">24dp</dimen>
-  <dimen name="lb_preference_decor_title_padding_end">56dp</dimen>
-  <dimen name="lb_preference_decor_title_text_size">20sp</dimen>
+    <dimen name="lb_preference_decor_title_text_height">64dp</dimen>
+    <dimen name="lb_preference_decor_title_margin_top">27dp</dimen>
+    <dimen name="lb_preference_decor_title_margin_start">24dp</dimen>
+    <dimen name="lb_preference_decor_title_margin_end">56dp</dimen>
+    <dimen name="lb_preference_decor_title_text_size">20sp</dimen>
+    <dimen name="lb_preference_decor_title_container_elevation">2dp</dimen>
+    <dimen name="lb_preference_decor_elevation">6dp</dimen>
 
-  <dimen name="lb_settings_pane_width">360dp</dimen>
+    <dimen name="lb_preference_item_padding_start">24dp</dimen>
+    <dimen name="lb_preference_item_padding_end">56dp</dimen>
+    <dimen name="lb_preference_item_icon_size">32dp</dimen>
+    <dimen name="lb_preference_item_icon_margin_end">16dp</dimen>
+
+    <dimen name="lb_preference_item_primary_text_size">14sp</dimen>
+    <dimen name="lb_preference_item_primary_text_margin_bottom">2dp</dimen>
+    <dimen name="lb_preference_item_secondary_text_size">12sp</dimen>
+    <dimen name="lb_preference_item_text_space_top">14dp</dimen>
+    <dimen name="lb_preference_item_text_space_bottom">13dp</dimen>
+
+    <dimen name="lb_preference_category_text_size">12sp</dimen>
+    <dimen name="lb_preference_category_height">40dp</dimen>
+
+    <dimen name="lb_settings_pane_width">360dp</dimen>
 </resources>
diff --git a/v17/preference-leanback/res/values/styles.xml b/v17/preference-leanback/res/values/styles.xml
new file mode 100644
index 0000000..0b315a8
--- /dev/null
+++ b/v17/preference-leanback/res/values/styles.xml
@@ -0,0 +1,65 @@
+<?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="LeanbackPreference">
+        <item name="layout">@layout/leanback_preference</item>
+    </style>
+
+    <style name="LeanbackPreference.Information">
+        <item name="layout">@layout/leanback_preference_information</item>
+        <item name="enabled">false</item>
+        <item name="shouldDisableView">false</item>
+    </style>
+
+    <style name="LeanbackPreference.Category">
+        <item name="layout">@layout/leanback_preference_category</item>
+        <!-- The title should not dim if the category is disabled, instead only the preference children should dim. -->
+        <item name="shouldDisableView">false</item>
+        <item name="selectable">false</item>
+    </style>
+
+    <style name="LeanbackPreference.CheckBoxPreference">
+        <item name="widgetLayout">@layout/preference_widget_checkbox</item>
+    </style>
+
+    <style name="LeanbackPreference.SwitchPreferenceCompat">
+        <item name="widgetLayout">@layout/preference_widget_switch_compat</item>
+        <item name="switchTextOn">@string/v7_preference_on</item>
+        <item name="switchTextOff">@string/v7_preference_off</item>
+    </style>
+
+    <style name="LeanbackPreference.SwitchPreference">
+        <item name="widgetLayout">@layout/preference_widget_switch</item>
+        <item name="switchTextOn">@string/v7_preference_on</item>
+        <item name="switchTextOff">@string/v7_preference_off</item>
+    </style>
+
+    <style name="LeanbackPreference.PreferenceScreen">
+    </style>
+
+    <style name="LeanbackPreference.DialogPreference">
+        <item name="positiveButtonText">@android:string/ok</item>
+        <item name="negativeButtonText">@android:string/cancel</item>
+    </style>
+
+    <style name="LeanbackPreference.DialogPreference.EditTextPreference">
+        <item name="dialogLayout">@layout/preference_dialog_edittext</item>
+    </style>
+
+</resources>
diff --git a/v17/preference-leanback/res/values/themes.xml b/v17/preference-leanback/res/values/themes.xml
new file mode 100644
index 0000000..b3060d9
--- /dev/null
+++ b/v17/preference-leanback/res/values/themes.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="PreferenceThemeOverlay.v14.Leanback">
+        <item name="preferenceScreenStyle">@style/LeanbackPreference.PreferenceScreen</item>
+        <item name="preferenceCategoryStyle">@style/LeanbackPreference.Category</item>
+        <item name="preferenceStyle">@style/LeanbackPreference</item>
+        <item name="preferenceInformationStyle">@style/LeanbackPreference.Information</item>
+        <item name="checkBoxPreferenceStyle">@style/LeanbackPreference.CheckBoxPreference</item>
+        <item name="switchPreferenceCompatStyle">@style/LeanbackPreference.SwitchPreferenceCompat</item>
+        <item name="switchPreferenceStyle">@style/LeanbackPreference.SwitchPreference</item>
+        <item name="dialogPreferenceStyle">@style/LeanbackPreference.DialogPreference</item>
+        <item name="editTextPreferenceStyle">@style/LeanbackPreference.DialogPreference.EditTextPreference</item>
+    </style>
+</resources>
diff --git a/v4/api/current.txt b/v4/api/current.txt
index 27d5993..d57b279 100644
--- a/v4/api/current.txt
+++ b/v4/api/current.txt
@@ -1443,6 +1443,12 @@
 
 package android.support.v4.media.session {
 
+  public class MediaButtonReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaButtonReceiver();
+    method public static android.view.KeyEvent handleIntent(android.support.v4.media.session.MediaSessionCompat, android.content.Intent);
+    method public void onReceive(android.content.Context, android.content.Intent);
+  }
+
   public final class MediaControllerCompat {
     ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat);
     ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException;
@@ -1510,6 +1516,7 @@
   }
 
   public class MediaSessionCompat {
+    ctor public MediaSessionCompat(android.content.Context, java.lang.String);
     ctor public MediaSessionCompat(android.content.Context, java.lang.String, android.content.ComponentName, android.app.PendingIntent);
     method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
     method public android.support.v4.media.session.MediaControllerCompat getController();
@@ -2400,6 +2407,7 @@
     method public static float getZ(android.view.View);
     method public static boolean hasAccessibilityDelegate(android.view.View);
     method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasOnClickListeners(android.view.View);
     method public static boolean hasOverlappingRendering(android.view.View);
     method public static boolean hasTransientState(android.view.View);
     method public static boolean isAttachedToWindow(android.view.View);
@@ -3266,6 +3274,7 @@
     method public boolean isOverScrolled();
     method public void notifyHorizontalEdgeReached(int, int, int);
     method public void notifyVerticalEdgeReached(int, int, int);
+    method public boolean springBack(int, int, int, int, int, int);
     method public void startScroll(int, int, int, int);
     method public void startScroll(int, int, int, int, int);
   }
diff --git a/v4/api21/android/support/v4/media/MediaDescriptionCompatApi21.java b/v4/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
index 991515a..234a77a 100644
--- a/v4/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
+++ b/v4/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
@@ -21,7 +21,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 
-public class MediaDescriptionCompatApi21 {
+class MediaDescriptionCompatApi21 {
 
     public static String getMediaId(Object descriptionObj) {
         return ((MediaDescription) descriptionObj).getMediaId();
@@ -59,7 +59,7 @@
         return MediaDescription.CREATOR.createFromParcel(in);
     }
 
-    public static class Builder {
+    static class Builder {
         public static Object newInstance() {
             return new MediaDescription.Builder();
         }
diff --git a/v4/api21/android/support/v4/media/session/MediaSessionCompatApi21.java b/v4/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
index 8094391..b3e7fd1 100644
--- a/v4/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
+++ b/v4/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
@@ -132,7 +132,7 @@
         ((MediaSession) sessionObj).setExtras(extras);
     }
 
-    public static interface Callback {
+    static interface Callback {
         public void onCommand(String command, Bundle extras, ResultReceiver cb);
         public boolean onMediaButtonEvent(Intent mediaButtonIntent);
         public void onPlay();
diff --git a/v4/froyo/android/support/v4/media/session/MediaSessionCompatApi8.java b/v4/froyo/android/support/v4/media/session/MediaSessionCompatApi8.java
index f49eb2b..d03287fe 100644
--- a/v4/froyo/android/support/v4/media/session/MediaSessionCompatApi8.java
+++ b/v4/froyo/android/support/v4/media/session/MediaSessionCompatApi8.java
@@ -19,7 +19,7 @@
 import android.content.Context;
 import android.media.AudioManager;
 
-public class MediaSessionCompatApi8 {
+class MediaSessionCompatApi8 {
     public static void registerMediaButtonEventReceiver(Context context, ComponentName mbr) {
         AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         am.registerMediaButtonEventReceiver(mbr);
diff --git a/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java b/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java
index 429e864..61c9a03 100644
--- a/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java
+++ b/v4/gingerbread/android/support/v4/widget/ScrollerCompatGingerbread.java
@@ -87,4 +87,9 @@
     public static int getFinalY(Object scroller) {
         return ((OverScroller) scroller).getFinalY();
     }
+
+    public static boolean springBack(Object scroller, int startX, int startY, int minX, int maxX,
+            int minY, int maxY) {
+        return ((OverScroller) scroller).springBack(startX, startY, minX, maxX, minY, maxY);
+    }
 }
diff --git a/v4/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java b/v4/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java
new file mode 100644
index 0000000..780345c
--- /dev/null
+++ b/v4/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.view;
+
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+/**
+ * Helper for accessing newer features in View introduced in ICS Mr1.
+ */
+class ViewCompatICSMr1 {
+    public static boolean hasOnClickListeners(View v) {
+        return v.hasOnClickListeners();
+    }
+}
diff --git a/v4/ics/android/support/v4/media/session/MediaSessionCompatApi14.java b/v4/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
index de644e6..3378e1e 100644
--- a/v4/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
+++ b/v4/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
@@ -25,7 +25,7 @@
 import android.os.Bundle;
 import android.os.ResultReceiver;
 
-public class MediaSessionCompatApi14 {
+class MediaSessionCompatApi14 {
     /***** RemoteControlClient States, we only need none as the others were public *******/
     final static int RCC_PLAYSTATE_NONE = 0;
 
@@ -224,7 +224,7 @@
         }
     }
 
-    public static interface Callback {
+    static interface Callback {
         public void onCommand(String command, Bundle extras, ResultReceiver cb);
 
         public boolean onMediaButtonEvent(Intent mediaButtonIntent);
diff --git a/v4/java/android/support/v4/graphics/ColorUtils.java b/v4/java/android/support/v4/graphics/ColorUtils.java
index aac809b..4d9d9b2 100644
--- a/v4/java/android/support/v4/graphics/ColorUtils.java
+++ b/v4/java/android/support/v4/graphics/ColorUtils.java
@@ -17,6 +17,10 @@
 package android.support.v4.graphics;
 
 import android.graphics.Color;
+import android.support.annotation.ColorInt;
+import android.support.annotation.FloatRange;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
 
 /**
  * A set of color-related utility methods, building upon those available in {@code Color}.
@@ -24,14 +28,14 @@
 public class ColorUtils {
 
     private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10;
-    private static final int MIN_ALPHA_SEARCH_PRECISION = 10;
+    private static final int MIN_ALPHA_SEARCH_PRECISION = 1;
 
     private ColorUtils() {}
 
     /**
      * Composite two potentially translucent colors over each other and returns the result.
      */
-    public static int compositeColors(int foreground, int background) {
+    public static int compositeColors(@ColorInt int foreground, @ColorInt int background) {
         int bgAlpha = Color.alpha(background);
         int fgAlpha = Color.alpha(foreground);
         int a = compositeAlpha(fgAlpha, bgAlpha);
@@ -57,10 +61,13 @@
 
     /**
      * Returns the luminance of a color.
-     *
-     * Formula defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
+     * <p>
+     * Formula defined
+     * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef">here</a>.
+     * </p>
      */
-    public static double calculateLuminance(int color) {
+    @FloatRange(from = 0.0, to = 1.0)
+    public static double calculateLuminance(@ColorInt int color) {
         double red = Color.red(color) / 255d;
         red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4);
 
@@ -80,9 +87,10 @@
      * 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) {
+    public static double calculateContrast(@ColorInt int foreground, @ColorInt int background) {
         if (Color.alpha(background) != 255) {
-            throw new IllegalArgumentException("background can not be translucent");
+            throw new IllegalArgumentException("background can not be translucent: #"
+                    + Integer.toHexString(background));
         }
         if (Color.alpha(foreground) < 255) {
             // If the foreground is translucent, composite the foreground over the background
@@ -106,10 +114,11 @@
      * @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,
+    public static int calculateMinimumAlpha(@ColorInt int foreground, @ColorInt int background,
             float minContrastRatio) {
         if (Color.alpha(background) != 255) {
-            throw new IllegalArgumentException("background can not be translucent");
+            throw new IllegalArgumentException("background can not be translucent: #"
+                    + Integer.toHexString(background));
         }
 
         // First lets check that a fully opaque foreground has sufficient contrast
@@ -158,7 +167,9 @@
      * @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) {
+    public static void RGBToHSL(@IntRange(from = 0x0, to = 0xFF) int r,
+            @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
+            @NonNull float[] hsl) {
         final float rf = r / 255f;
         final float gf = g / 255f;
         final float bf = b / 255f;
@@ -206,7 +217,7 @@
      * @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) {
+    public static void colorToHSL(@ColorInt int color, @NonNull float[] hsl) {
         RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hsl);
     }
 
@@ -222,7 +233,8 @@
      * @param hsl 3 element array which holds the input HSL components.
      * @return the resulting RGB color
      */
-    public static int HSLToColor(float[] hsl) {
+    @ColorInt
+    public static int HSLToColor(@NonNull float[] hsl) {
         final float h = hsl[0];
         final float s = hsl[1];
         final float l = hsl[2];
@@ -279,7 +291,9 @@
     /**
      * Set the alpha component of {@code color} to be {@code alpha}.
      */
-    public static int setAlphaComponent(int color, int alpha) {
+    @ColorInt
+    public static int setAlphaComponent(@ColorInt int color,
+            @IntRange(from = 0x0, to = 0xFF) int alpha) {
         if (alpha < 0 || alpha > 255) {
             throw new IllegalArgumentException("alpha must be between 0 and 255.");
         }
diff --git a/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java b/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java
index 88b5f4a..64ae075 100644
--- a/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java
+++ b/v4/java/android/support/v4/graphics/drawable/DrawableCompat.java
@@ -123,7 +123,7 @@
         @Override
         public int getLayoutDirection(Drawable drawable) {
             final int dir = DrawableCompatJellybeanMr1.getLayoutDirection(drawable);
-            return dir < 0 ? dir : ViewCompat.LAYOUT_DIRECTION_LTR;
+            return dir >= 0 ? dir : ViewCompat.LAYOUT_DIRECTION_LTR;
         }
     }
 
diff --git a/v4/java/android/support/v4/media/session/MediaButtonReceiver.java b/v4/java/android/support/v4/media/session/MediaButtonReceiver.java
new file mode 100644
index 0000000..da2ce08
--- /dev/null
+++ b/v4/java/android/support/v4/media/session/MediaButtonReceiver.java
@@ -0,0 +1,129 @@
+/*
+ * 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.media.session;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.view.KeyEvent;
+
+import java.util.List;
+
+/**
+ * A media button receiver receives and helps translate hardware media playback buttons,
+ * such as those found on wired and wireless headsets, into the appropriate callbacks
+ * in your app.
+ * <p />
+ * You can add this MediaButtonReceiver to your app by adding it directly to your
+ * AndroidManifest.xml:
+ * <pre>
+ * &lt;receiver android:name="android.support.v4.media.session.MediaButtonReceiver" &gt;
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.intent.action.MEDIA_BUTTON" /&gt;
+ *   &lt;/intent-filter&gt;
+ * &lt;/service&gt;
+ * </pre>
+ * This class assumes you have a {@link Service} in your app that controls
+ * media playback via a {@link MediaSessionCompat}. That {@link Service} must
+ * include an intent filter that also handles {@link Intent#ACTION_MEDIA_BUTTON}:
+ * <pre>
+ * &lt;service android:name="com.example.android.MediaPlaybackService" &gt;
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.intent.action.MEDIA_BUTTON" /&gt;
+ *   &lt;/intent-filter&gt;
+ * &lt;/service&gt;
+ * </pre>
+ *
+ * All {@link Intent}s sent to this MediaButtonReceiver will then be forwarded
+ * to the {@link Service}. Events can then be handled in
+ * {@link Service#onStartCommand(Intent, int, int)} by calling
+ * {@link MediaButtonReceiver#handleIntent(MediaSessionCompat, Intent)}, passing in
+ * your current {@link MediaSessionCompat}:
+ * <pre>
+ * private MediaSessionCompat mMediaSessionCompat = ...;
+ *
+ * public int onStartCommand(Intent intent, int flags, int startId) {
+ *   MediaButtonReceiver.handleIntent(mMediaSessionCompat, intent);
+ *   return super.onStartCommand(intent, flags, startId);
+ * }
+ * </pre>
+ *
+ * This ensures that the correct callbacks to {@link MediaSessionCompat.Callback}
+ * will be triggered based on the incoming {@link KeyEvent}.
+ */
+public class MediaButtonReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        queryIntent.setPackage(context.getPackageName());
+        PackageManager pm = context.getPackageManager();
+        List<ResolveInfo> resolveInfos = pm.queryIntentServices(queryIntent, 0);
+        if (resolveInfos.size() != 1) {
+            throw new IllegalStateException("Expected 1 Service that handles " +
+                    Intent.ACTION_MEDIA_BUTTON + ", found " + resolveInfos.size());
+        }
+        ResolveInfo resolveInfo = resolveInfos.get(0);
+        ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName,
+                resolveInfo.serviceInfo.name);
+        intent.setComponent(componentName);
+        context.startService(intent);
+    }
+
+    /**
+     * Extracts any available {@link KeyEvent} from an {@link Intent#ACTION_MEDIA_BUTTON}
+     * intent, passing it onto the {@link MediaSessionCompat} using
+     * {@link MediaControllerCompat#dispatchMediaButtonEvent(KeyEvent)}, which in turn
+     * will trigger callbacks to the {@link MediaSessionCompat.Callback} registered via
+     * {@link MediaSessionCompat#setCallback(MediaSessionCompat.Callback)}.
+     * <p />
+     * The returned {@link KeyEvent} is non-null if any {@link KeyEvent} is found and can
+     * be used if any additional processing is needed beyond what is done in the
+     * {@link MediaSessionCompat.Callback}. An example of is to prevent redelivery of a
+     * {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} Intent in the case of the Service being
+     * restarted (which, by default, will redeliver the last received Intent).
+     * <pre>
+     * KeyEvent keyEvent = MediaButtonReceiver.handleIntent(mediaSession, intent);
+     * if (keyEvent != null && keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
+     *   Intent emptyIntent = new Intent(intent);
+     *   emptyIntent.setAction("");
+     *   startService(emptyIntent);
+     * }
+     * </pre>
+     * @param mediaSessionCompat A {@link MediaSessionCompat} that has a
+     * {@link MediaSessionCompat.Callback} set
+     * @param intent The intent to parse
+     * @return The extracted {@link KeyEvent} if found, or null.
+     */
+    public static KeyEvent handleIntent(MediaSessionCompat mediaSessionCompat, Intent intent) {
+        if (mediaSessionCompat == null || intent == null
+                || !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
+                || !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) {
+            return null;
+        }
+        KeyEvent ke = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+        MediaControllerCompat mediaController = mediaSessionCompat.getController();
+        mediaController.dispatchMediaButtonEvent(ke);
+        return ke;
+    }
+}
+
diff --git a/v4/java/android/support/v4/media/session/MediaSessionCompat.java b/v4/java/android/support/v4/media/session/MediaSessionCompat.java
index fa963df..6d3cb27 100644
--- a/v4/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/v4/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -19,9 +19,12 @@
 
 import android.app.Activity;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Bundle;
@@ -77,6 +80,8 @@
  * backwards compatible fashion.
  */
 public class MediaSessionCompat {
+    private static final String TAG = "MediaSessionCompat";
+
     private final MediaSessionImpl mImpl;
     private final MediaControllerCompat mController;
     private final ArrayList<OnActiveChangeListener>
@@ -102,12 +107,27 @@
     public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
 
     /**
+     * Creates a new session using a media button receiver from your manifest.
+     * Note that a media button receiver is required to support platform versions
+     * earlier than {@link android.os.Build.VERSION_CODES#LOLLIPOP}.
+     *
+     * @param context The context.
+     * @param tag A short name for debugging purposes.
+     */
+    public MediaSessionCompat(Context context, String tag) {
+        this(context, tag, null, null);
+    }
+
+    /**
      * Creates a new session.
      *
      * @param context The context.
      * @param tag A short name for debugging purposes.
      * @param mediaButtonEventReceiver The component name for your receiver.
-     *            This must be non-null to support platform versions earlier
+     *            If null, this will attempt to find an appropriate
+     *            {@link BroadcastReceiver} that handles
+     *            {@link Intent#ACTION_MEDIA_BUTTON} from your manifest.
+     *            A receiver is required to support platform versions earlier
      *            than {@link android.os.Build.VERSION_CODES#LOLLIPOP}.
      * @param mbrIntent The PendingIntent for your receiver component that
      *            handles media button events. This is optional and will be used
@@ -123,6 +143,24 @@
             throw new IllegalArgumentException("tag must not be null or empty");
         }
 
+        if (mediaButtonEventReceiver == null) {
+            Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+            queryIntent.setPackage(context.getPackageName());
+            PackageManager pm = context.getPackageManager();
+            List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(queryIntent, 0);
+            // If none are found, assume we are running on a newer platform version that does
+            // not require a media button receiver ComponentName. Later code will double check
+            // this assumption and throw an error if needed
+            if (resolveInfos.size() == 1) {
+                ResolveInfo resolveInfo = resolveInfos.get(0);
+                mediaButtonEventReceiver = new ComponentName(resolveInfo.activityInfo.packageName,
+                    resolveInfo.activityInfo.name);
+            } else if (resolveInfos.size() > 1) {
+                Log.w(TAG, "More than one BroadcastReceiver that handles " +
+                        Intent.ACTION_MEDIA_BUTTON + " was found, using null. Provide a " +
+                        "specific ComponentName to use as this session's media button receiver");
+            }
+        }
         if (mediaButtonEventReceiver != null && mbrIntent == null) {
             // construct a PendingIntent for the media button
             Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
diff --git a/v4/java/android/support/v4/view/ViewCompat.java b/v4/java/android/support/v4/view/ViewCompat.java
index d3dce89..4ae4910 100644
--- a/v4/java/android/support/v4/view/ViewCompat.java
+++ b/v4/java/android/support/v4/view/ViewCompat.java
@@ -384,6 +384,7 @@
         int combineMeasuredStates(int curState, int newState);
         public float getZ(View view);
         public boolean isAttachedToWindow(View view);
+        public boolean hasOnClickListeners(View view);
     }
 
     static class BaseViewCompatImpl implements ViewCompatImpl {
@@ -963,6 +964,11 @@
         public boolean isAttachedToWindow(View view) {
             return ViewCompatBase.isAttachedToWindow(view);
         }
+
+        @Override
+        public boolean hasOnClickListeners(View view) {
+            return false;
+        }
     }
 
     static class EclairMr1ViewCompatImpl extends BaseViewCompatImpl {
@@ -1222,7 +1228,14 @@
         }
     }
 
-    static class JBViewCompatImpl extends ICSViewCompatImpl {
+    static class ICSMr1ViewCompatImpl extends ICSViewCompatImpl {
+        @Override
+        public boolean hasOnClickListeners(View view) {
+            return ViewCompatICSMr1.hasOnClickListeners(view);
+        }
+    }
+
+    static class JBViewCompatImpl extends ICSMr1ViewCompatImpl {
         @Override
         public boolean hasTransientState(View view) {
             return ViewCompatJB.hasTransientState(view);
@@ -1540,6 +1553,8 @@
             IMPL = new JbMr1ViewCompatImpl();
         } else if (version >= 16) {
             IMPL = new JBViewCompatImpl();
+        } else if (version >= 15) {
+            IMPL = new ICSMr1ViewCompatImpl();
         } else if (version >= 14) {
             IMPL = new ICSViewCompatImpl();
         } else if (version >= 11) {
@@ -3085,4 +3100,13 @@
     public static boolean isAttachedToWindow(View view) {
         return IMPL.isAttachedToWindow(view);
     }
+
+    /**
+     * Returns whether the provided view has an attached {@link View.OnClickListener}.
+     *
+     * @return true if there is a listener, false if there is none.
+     */
+    public static boolean hasOnClickListeners(View view) {
+        return IMPL.hasOnClickListeners(view);
+    }
 }
diff --git a/v4/java/android/support/v4/widget/NestedScrollView.java b/v4/java/android/support/v4/widget/NestedScrollView.java
index 788761a..583c728 100644
--- a/v4/java/android/support/v4/widget/NestedScrollView.java
+++ b/v4/java/android/support/v4/widget/NestedScrollView.java
@@ -576,13 +576,6 @@
             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: {
                 /*
@@ -657,6 +650,9 @@
                 mIsBeingDragged = false;
                 mActivePointerId = INVALID_POINTER;
                 recycleVelocityTracker();
+                if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) {
+                    ViewCompat.postInvalidateOnAnimation(this);
+                }
                 stopNestedScroll();
                 break;
             case MotionEventCompat.ACTION_POINTER_UP:
@@ -795,6 +791,9 @@
 
                     if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                         flingWithNestedDispatch(-initialVelocity);
+                    } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
+                            getScrollRange())) {
+                        ViewCompat.postInvalidateOnAnimation(this);
                     }
 
                     mActivePointerId = INVALID_POINTER;
@@ -803,6 +802,10 @@
                 break;
             case MotionEvent.ACTION_CANCEL:
                 if (mIsBeingDragged && getChildCount() > 0) {
+                    if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
+                            getScrollRange())) {
+                        ViewCompat.postInvalidateOnAnimation(this);
+                    }
                     mActivePointerId = INVALID_POINTER;
                     endDrag();
                 }
@@ -942,6 +945,10 @@
             clampedY = true;
         }
 
+        if (clampedY) {
+            mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange());
+        }
+
         onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
 
         return clampedX || clampedY;
diff --git a/v4/java/android/support/v4/widget/ScrollerCompat.java b/v4/java/android/support/v4/widget/ScrollerCompat.java
index afbf897..f1b89da 100644
--- a/v4/java/android/support/v4/widget/ScrollerCompat.java
+++ b/v4/java/android/support/v4/widget/ScrollerCompat.java
@@ -54,6 +54,8 @@
         boolean isOverScrolled(Object scroller);
         int getFinalX(Object scroller);
         int getFinalY(Object scroller);
+        boolean springBack(Object scroller, int startX, int startY, int minX, int maxX,
+                int minY, int maxY);
     }
 
     static final int CHASE_FRAME_TIME = 16; // ms per target frame
@@ -145,6 +147,12 @@
         public int getFinalY(Object scroller) {
             return ((Scroller) scroller).getFinalY();
         }
+
+        @Override
+        public boolean springBack(Object scroller, int startX, int startY, int minX, int maxX,
+                int minY, int maxY) {
+            return false;
+        }
     }
 
     static class ScrollerCompatImplGingerbread implements ScrollerCompatImpl {
@@ -233,6 +241,13 @@
         public int getFinalY(Object scroller) {
             return ScrollerCompatGingerbread.getFinalY(scroller);
         }
+
+        @Override
+        public boolean springBack(Object scroller, int startX, int startY, int minX, int maxX,
+                int minY, int maxY) {
+            return ScrollerCompatGingerbread.springBack(scroller, startX, startY, minX, maxX,
+                    minY, maxY);
+        }
     }
 
     static class ScrollerCompatImplIcs extends ScrollerCompatImplGingerbread {
@@ -423,6 +438,22 @@
     }
 
     /**
+     * Call this when you want to 'spring back' into a valid coordinate range.
+     *
+     * @param startX Starting X coordinate
+     * @param startY Starting Y coordinate
+     * @param minX Minimum valid X value
+     * @param maxX Maximum valid X value
+     * @param minY Minimum valid Y value
+     * @param maxY Minimum valid Y value
+     * @return true if a springback was initiated, false if startX and startY were
+     *          already within the valid range.
+     */
+    public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
+        return mImpl.springBack(mScroller, startX, startY, minX, maxX, minY, maxY);
+    }
+
+    /**
      * Stops the animation. Aborting the animation causes the scroller to move to the final x and y
      * position.
      */
diff --git a/v4/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java b/v4/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
index 38b6778..be3555e 100644
--- a/v4/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
+++ b/v4/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
@@ -28,9 +28,7 @@
 import android.view.View;
 import android.view.ViewTreeObserver;
 
-class TransportMediatorJellybeanMR2
-        implements RemoteControlClient.OnGetPlaybackPositionListener,
-        RemoteControlClient.OnPlaybackPositionUpdateListener {
+class TransportMediatorJellybeanMR2 {
     final Context mContext;
     final AudioManager mAudioManager;
     final View mTargetView;
@@ -75,7 +73,20 @@
             mTransportCallback.handleAudioFocusChange(focusChange);
         }
     };
-
+    final RemoteControlClient.OnGetPlaybackPositionListener mGetPlaybackPositionListener
+            = new RemoteControlClient.OnGetPlaybackPositionListener() {
+                @Override
+                public long onGetPlaybackPosition() {
+                    return mTransportCallback.getPlaybackPosition();
+                }
+            };
+    final RemoteControlClient.OnPlaybackPositionUpdateListener mPlaybackPositionUpdateListener
+            = new RemoteControlClient.OnPlaybackPositionUpdateListener() {
+                public void onPlaybackPositionUpdate(long newPositionMs) {
+                    mTransportCallback.playbackPositionUpdate(newPositionMs);
+                }
+            };
+ 
     PendingIntent mPendingIntent;
     RemoteControlClient mRemoteControl;
     boolean mFocused;
@@ -112,8 +123,8 @@
         mPendingIntent = PendingIntent.getBroadcast(mContext, 0, mIntent,
                 PendingIntent.FLAG_CANCEL_CURRENT);
         mRemoteControl = new RemoteControlClient(mPendingIntent);
-        mRemoteControl.setOnGetPlaybackPositionListener(this);
-        mRemoteControl.setPlaybackPositionUpdateListener(this);
+        mRemoteControl.setOnGetPlaybackPositionListener(mGetPlaybackPositionListener);
+        mRemoteControl.setPlaybackPositionUpdateListener(mPlaybackPositionUpdateListener);
     }
 
     void gainFocus() {
@@ -145,16 +156,6 @@
         }
     }
 
-    @Override
-    public long onGetPlaybackPosition() {
-        return mTransportCallback.getPlaybackPosition();
-    }
-
-    @Override
-    public void onPlaybackPositionUpdate(long newPositionMs) {
-        mTransportCallback.playbackPositionUpdate(newPositionMs);
-    }
-
     public void refreshState(boolean playing, long position, int transportControls) {
         if (mRemoteControl != null) {
             mRemoteControl.setPlaybackState(playing ? RemoteControlClient.PLAYSTATE_PLAYING
diff --git a/v4/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java b/v4/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
index 1c5dca7..1f008f8 100644
--- a/v4/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
+++ b/v4/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
@@ -21,7 +21,7 @@
 import android.media.RemoteControlClient;
 import android.os.SystemClock;
 
-public class MediaSessionCompatApi18 {
+class MediaSessionCompatApi18 {
     /***** PlaybackState actions *****/
     private static final long ACTION_SEEK_TO = 1 << 8;
 
diff --git a/v4/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java b/v4/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
index 61039d3..f3c19e2 100644
--- a/v4/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
+++ b/v4/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
@@ -15,14 +15,13 @@
  */
 package android.support.v4.media.session;
 
-import android.graphics.Bitmap;
 import android.media.MediaMetadataEditor;
 import android.media.MediaMetadataRetriever;
 import android.media.Rating;
 import android.media.RemoteControlClient;
 import android.os.Bundle;
 
-public class MediaSessionCompatApi19 {
+class MediaSessionCompatApi19 {
     /***** PlaybackState actions *****/
     private static final long ACTION_SET_RATING = 1 << 7;
 
diff --git a/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java b/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java
index 855cdba..56cb6fb 100644
--- a/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java
+++ b/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java
@@ -17,9 +17,9 @@
 package android.support.v4.graphics;
 
 import android.graphics.Color;
-import android.support.v4.graphics.ColorUtils;
 import android.test.AndroidTestCase;
 
+import java.lang.Integer;
 import java.util.ArrayList;
 
 /**
@@ -31,21 +31,31 @@
     private static final float ALLOWED_OFFSET_HUE = 360 * 0.005f;
     private static final float ALLOWED_OFFSET_SATURATION = 0.005f;
     private static final float ALLOWED_OFFSET_LIGHTNESS = 0.005f;
+    private static final float ALLOWED_OFFSET_MIN_ALPHA = 0.01f;
 
     private static final int ALLOWED_OFFSET_RGB_COMPONENT = 2;
 
     private static final ArrayList<TestEntry> sEntryList = new ArrayList<>();
 
     static {
-        sEntryList.add(new TestEntry(Color.BLACK).setHsl(0f, 0f, 0f));
-        sEntryList.add(new TestEntry(Color.WHITE).setHsl(0f, 0f, 1f));
-        sEntryList.add(new TestEntry(Color.BLUE).setHsl(240f, 1f, 0.5f));
-        sEntryList.add(new TestEntry(Color.GREEN).setHsl(120f, 1f, 0.5f));
-        sEntryList.add(new TestEntry(Color.RED).setHsl(0f, 1f, 0.5f));
-        sEntryList.add(new TestEntry(Color.CYAN).setHsl(180f, 1f, 0.5f));
-        sEntryList.add(new TestEntry(0x2196F3).setHsl(207f, 0.9f, 0.54f));
-        sEntryList.add(new TestEntry(0xD1C4E9).setHsl(261f, 0.46f, 0.84f));
-        sEntryList.add(new TestEntry(0x311B92).setHsl(251.09f, 0.687f, 0.339f));
+        sEntryList.add(new TestEntry(Color.BLACK).setHsl(0f, 0f, 0f)
+                .setWhiteMinAlpha30(0.35f).setWhiteMinAlpha45(0.46f));
+        sEntryList.add(new TestEntry(Color.WHITE).setHsl(0f, 0f, 1f)
+                .setBlackMinAlpha30(0.42f).setBlackMinAlpha45(0.54f));
+        sEntryList.add(new TestEntry(Color.BLUE).setHsl(240f, 1f, 0.5f)
+                .setWhiteMinAlpha30(0.55f).setWhiteMinAlpha45(0.71f));
+        sEntryList.add(new TestEntry(Color.GREEN).setHsl(120f, 1f, 0.5f)
+                .setBlackMinAlpha30(0.43f).setBlackMinAlpha45(0.55f));
+        sEntryList.add(new TestEntry(Color.RED).setHsl(0f, 1f, 0.5f)
+                .setWhiteMinAlpha30(0.84f).setBlackMinAlpha30(0.55f).setBlackMinAlpha45(0.78f));
+        sEntryList.add(new TestEntry(Color.CYAN).setHsl(180f, 1f, 0.5f)
+                .setBlackMinAlpha30(0.43f).setBlackMinAlpha45(0.55f));
+        sEntryList.add(new TestEntry(0xFF2196F3).setHsl(207f, 0.9f, 0.54f)
+                .setBlackMinAlpha30(0.52f).setWhiteMinAlpha30(0.97f).setBlackMinAlpha45(0.7f));
+        sEntryList.add(new TestEntry(0xFFD1C4E9).setHsl(261f, 0.46f, 0.84f)
+                .setBlackMinAlpha30(0.45f).setBlackMinAlpha45(0.58f));
+        sEntryList.add(new TestEntry(0xFF311B92).setHsl(251.09f, 0.687f, 0.339f)
+                .setWhiteMinAlpha30(0.39f).setWhiteMinAlpha45(0.54f));
     }
 
     public void testToHSL() {
@@ -72,6 +82,28 @@
         }
     }
 
+    public void testMinAlphas() {
+        for (TestEntry entry : sEntryList) {
+            testMinAlpha("Black title", entry.rgb, entry.blackMinAlpha30,
+                    ColorUtils.calculateMinimumAlpha(Color.BLACK, entry.rgb, 3.0f));
+            testMinAlpha("Black body", entry.rgb, entry.blackMinAlpha45,
+                    ColorUtils.calculateMinimumAlpha(Color.BLACK, entry.rgb, 4.5f));
+            testMinAlpha("White title", entry.rgb, entry.whiteMinAlpha30,
+                    ColorUtils.calculateMinimumAlpha(Color.WHITE, entry.rgb, 3.0f));
+            testMinAlpha("White body", entry.rgb, entry.whiteMinAlpha45,
+                    ColorUtils.calculateMinimumAlpha(Color.WHITE, entry.rgb, 4.5f));
+        }
+    }
+
+    private static void testMinAlpha(String title, int color, float expected, int actual) {
+        final String message = title + " text within error for #" + Integer.toHexString(color);
+        if (expected < 0) {
+            assertEquals(message, actual, -1);
+        } else {
+            assertClose(message, expected, actual / 255f, ALLOWED_OFFSET_MIN_ALPHA);
+        }
+    }
+
     private static void assertClose(String message, float expected, float actual,
             float allowedOffset) {
         StringBuilder sb = new StringBuilder(message);
@@ -114,6 +146,10 @@
     private static class TestEntry {
         final int rgb;
         final float[] hsl = new float[3];
+        float blackMinAlpha45 = -1;
+        float blackMinAlpha30 = -1;
+        float whiteMinAlpha45 = -1;
+        float whiteMinAlpha30 = -1;
 
         TestEntry(int rgb) {
             this.rgb = rgb;
@@ -125,5 +161,25 @@
             hsl[2] = l;
             return this;
         }
+
+        TestEntry setBlackMinAlpha30(float minAlpha) {
+            blackMinAlpha30 = minAlpha;
+            return this;
+        }
+
+        TestEntry setBlackMinAlpha45(float minAlpha) {
+            blackMinAlpha45 = minAlpha;
+            return this;
+        }
+
+        TestEntry setWhiteMinAlpha30(float minAlpha) {
+            whiteMinAlpha30 = minAlpha;
+            return this;
+        }
+
+        TestEntry setWhiteMinAlpha45(float minAlpha) {
+            whiteMinAlpha45 = minAlpha;
+            return this;
+        }
     }
 }
\ No newline at end of file
diff --git a/v7/appcompat/api/current.txt b/v7/appcompat/api/current.txt
index 336e3a7..fcb8a82 100644
--- a/v7/appcompat/api/current.txt
+++ b/v7/appcompat/api/current.txt
@@ -368,6 +368,7 @@
     field public static int alertDialogCenterButtons;
     field public static int alertDialogStyle;
     field public static int alertDialogTheme;
+    field public static int allowStacking;
     field public static int arrowHeadLength;
     field public static int arrowShaftLength;
     field public static int autoCompleteTextViewStyle;
@@ -477,6 +478,7 @@
     field public static int searchHintIcon;
     field public static int searchIcon;
     field public static int searchViewStyle;
+    field public static int seekBarStyle;
     field public static int selectableItemBackground;
     field public static int selectableItemBackgroundBorderless;
     field public static int showAsAction;
@@ -540,6 +542,7 @@
     field public static int abc_action_bar_embed_tabs;
     field public static int abc_action_bar_embed_tabs_pre_jb;
     field public static int abc_action_bar_expanded_action_views_exclusive;
+    field public static int abc_allow_stacked_button_bar;
     field public static int abc_config_actionMenuItemAllCaps;
     field public static int abc_config_allowActionMenuItemTextWithIcon;
     field public static int abc_config_closeDialogWhenTouchOutside;
@@ -664,6 +667,8 @@
     field public static int abc_panel_menu_list_width;
     field public static int abc_search_view_preferred_width;
     field public static int abc_search_view_text_min_width;
+    field public static int abc_seekbar_track_background_height_material;
+    field public static int abc_seekbar_track_progress_height_material;
     field public static int abc_switch_padding;
     field public static int abc_text_size_body_1_material;
     field public static int abc_text_size_body_2_material;
@@ -748,6 +753,13 @@
     field public static int abc_menu_hardkey_panel_mtrl_mult;
     field public static int abc_popup_background_mtrl_mult;
     field public static int abc_ratingbar_full_material;
+    field public static int abc_scrubber_control_off_mtrl_alpha;
+    field public static int abc_scrubber_control_to_pressed_mtrl_000;
+    field public static int abc_scrubber_control_to_pressed_mtrl_005;
+    field public static int abc_scrubber_primary_mtrl_alpha;
+    field public static int abc_scrubber_track_mtrl_alpha;
+    field public static int abc_seekbar_thumb_material;
+    field public static int abc_seekbar_track_material;
     field public static int abc_spinner_mtrl_am_alpha;
     field public static int abc_spinner_textfield_background_material;
     field public static int abc_switch_thumb_material;
@@ -837,6 +849,7 @@
     field public static int showCustom;
     field public static int showHome;
     field public static int showTitle;
+    field public static int spacer;
     field public static int split_action_bar;
     field public static int src_atop;
     field public static int src_in;
@@ -877,6 +890,7 @@
     field public static int abc_action_mode_close_item_material;
     field public static int abc_activity_chooser_view;
     field public static int abc_activity_chooser_view_list_item;
+    field public static int abc_alert_dialog_button_bar_material;
     field public static int abc_alert_dialog_material;
     field public static int abc_dialog_title_material;
     field public static int abc_expanded_menu_layout;
@@ -916,6 +930,8 @@
     field public static int abc_action_mode_done;
     field public static int abc_activity_chooser_view_see_all;
     field public static int abc_activitychooserview_choose_application;
+    field public static int abc_capital_off;
+    field public static int abc_capital_on;
     field public static int abc_search_hint;
     field public static int abc_searchview_description_clear;
     field public static int abc_searchview_description_query;
@@ -1067,6 +1083,7 @@
     field public static int Base_Widget_AppCompat_RatingBar;
     field public static int Base_Widget_AppCompat_SearchView;
     field public static int Base_Widget_AppCompat_SearchView_ActionBar;
+    field public static int Base_Widget_AppCompat_SeekBar;
     field public static int Base_Widget_AppCompat_Spinner;
     field public static int Base_Widget_AppCompat_Spinner_Underlined;
     field public static int Base_Widget_AppCompat_TextView_SpinnerItem;
@@ -1225,6 +1242,7 @@
     field public static int Widget_AppCompat_RatingBar;
     field public static int Widget_AppCompat_SearchView;
     field public static int Widget_AppCompat_SearchView_ActionBar;
+    field public static int Widget_AppCompat_SeekBar;
     field public static int Widget_AppCompat_Spinner;
     field public static int Widget_AppCompat_Spinner_DropDown;
     field public static int Widget_AppCompat_Spinner_DropDown_ActionBar;
@@ -1289,6 +1307,7 @@
     field public static final int[] AppCompatTextView;
     field public static int AppCompatTextView_android_textAppearance;
     field public static int AppCompatTextView_textAllCaps;
+    field public static int ButtonBarLayout_allowStacking;
     field public static final int[] CompoundButton;
     field public static int CompoundButton_android_button;
     field public static int CompoundButton_buttonTint;
@@ -1394,6 +1413,10 @@
     field public static int SwitchCompat_thumbTextPadding;
     field public static int SwitchCompat_track;
     field public static final int[] TextAppearance;
+    field public static int TextAppearance_android_shadowColor;
+    field public static int TextAppearance_android_shadowDx;
+    field public static int TextAppearance_android_shadowDy;
+    field public static int TextAppearance_android_shadowRadius;
     field public static int TextAppearance_android_textColor;
     field public static int TextAppearance_android_textSize;
     field public static int TextAppearance_android_textStyle;
@@ -1483,6 +1506,7 @@
     field public static int Theme_radioButtonStyle;
     field public static int Theme_ratingBarStyle;
     field public static int Theme_searchViewStyle;
+    field public static int Theme_seekBarStyle;
     field public static int Theme_selectableItemBackground;
     field public static int Theme_selectableItemBackgroundBorderless;
     field public static int Theme_spinnerDropDownItemStyle;
@@ -1565,6 +1589,7 @@
     method public int getDirection();
     method public float getGapSize();
     method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
     method public float getProgress();
     method public boolean isSpinEnabled();
     method public void setAlpha(int);
@@ -1708,6 +1733,12 @@
     ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet, int);
   }
 
+  public class AppCompatSeekBar extends android.widget.SeekBar {
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet, int);
+  }
+
   public class AppCompatSpinner extends android.widget.Spinner {
     ctor public AppCompatSpinner(android.content.Context);
     ctor public AppCompatSpinner(android.content.Context, int);
@@ -1817,6 +1848,7 @@
     method public void setSoftInputMode(int);
     method public void setVerticalOffset(int);
     method public void setWidth(int);
+    method public void setWindowLayoutType(int);
     method public void show();
     field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
     field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
diff --git a/v7/appcompat/res-public/values/public_styles.xml b/v7/appcompat/res-public/values/public_styles.xml
index 4b42d0f..083fbc6 100644
--- a/v7/appcompat/res-public/values/public_styles.xml
+++ b/v7/appcompat/res-public/values/public_styles.xml
@@ -135,6 +135,7 @@
     <public type="style" name="Widget.AppCompat.RatingBar"/>
     <public type="style" name="Widget.AppCompat.SearchView"/>
     <public type="style" name="Widget.AppCompat.SearchView.ActionBar"/>
+    <public type="style" name="Widget.AppCompat.SeekBar"/>
     <public type="style" name="Widget.AppCompat.Spinner"/>
     <public type="style" name="Widget.AppCompat.Spinner.DropDown"/>
     <public type="style" name="Widget.AppCompat.Spinner.DropDown.ActionBar"/>
diff --git a/v7/appcompat/res/drawable-hdpi/abc_scrubber_control_off_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_scrubber_control_off_mtrl_alpha.png
new file mode 100644
index 0000000..4efe298
--- /dev/null
+++ b/v7/appcompat/res/drawable-hdpi/abc_scrubber_control_off_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_scrubber_control_to_pressed_mtrl_000.png b/v7/appcompat/res/drawable-hdpi/abc_scrubber_control_to_pressed_mtrl_000.png
new file mode 100644
index 0000000..543dec3
--- /dev/null
+++ b/v7/appcompat/res/drawable-hdpi/abc_scrubber_control_to_pressed_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_scrubber_control_to_pressed_mtrl_005.png b/v7/appcompat/res/drawable-hdpi/abc_scrubber_control_to_pressed_mtrl_005.png
new file mode 100644
index 0000000..9930b3a
--- /dev/null
+++ b/v7/appcompat/res/drawable-hdpi/abc_scrubber_control_to_pressed_mtrl_005.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_scrubber_primary_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_scrubber_primary_mtrl_alpha.9.png
new file mode 100644
index 0000000..4cfb1a7
--- /dev/null
+++ b/v7/appcompat/res/drawable-hdpi/abc_scrubber_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_scrubber_track_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_scrubber_track_mtrl_alpha.9.png
new file mode 100644
index 0000000..32ddf7a
--- /dev/null
+++ b/v7/appcompat/res/drawable-hdpi/abc_scrubber_track_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_scrubber_control_off_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_scrubber_control_off_mtrl_alpha.png
new file mode 100644
index 0000000..10df639
--- /dev/null
+++ b/v7/appcompat/res/drawable-mdpi/abc_scrubber_control_off_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_scrubber_control_to_pressed_mtrl_000.png b/v7/appcompat/res/drawable-mdpi/abc_scrubber_control_to_pressed_mtrl_000.png
new file mode 100644
index 0000000..f83b1ef
--- /dev/null
+++ b/v7/appcompat/res/drawable-mdpi/abc_scrubber_control_to_pressed_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_scrubber_control_to_pressed_mtrl_005.png b/v7/appcompat/res/drawable-mdpi/abc_scrubber_control_to_pressed_mtrl_005.png
new file mode 100644
index 0000000..e9efb20f
--- /dev/null
+++ b/v7/appcompat/res/drawable-mdpi/abc_scrubber_control_to_pressed_mtrl_005.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_scrubber_primary_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_scrubber_primary_mtrl_alpha.9.png
new file mode 100644
index 0000000..a4ab0a1
--- /dev/null
+++ b/v7/appcompat/res/drawable-mdpi/abc_scrubber_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_scrubber_track_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_scrubber_track_mtrl_alpha.9.png
new file mode 100644
index 0000000..db9e172
--- /dev/null
+++ b/v7/appcompat/res/drawable-mdpi/abc_scrubber_track_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_scrubber_control_off_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_scrubber_control_off_mtrl_alpha.png
new file mode 100644
index 0000000..138f643
--- /dev/null
+++ b/v7/appcompat/res/drawable-xhdpi/abc_scrubber_control_off_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_scrubber_control_to_pressed_mtrl_000.png b/v7/appcompat/res/drawable-xhdpi/abc_scrubber_control_to_pressed_mtrl_000.png
new file mode 100644
index 0000000..cd41d74
--- /dev/null
+++ b/v7/appcompat/res/drawable-xhdpi/abc_scrubber_control_to_pressed_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_scrubber_control_to_pressed_mtrl_005.png b/v7/appcompat/res/drawable-xhdpi/abc_scrubber_control_to_pressed_mtrl_005.png
new file mode 100644
index 0000000..8d67525
--- /dev/null
+++ b/v7/appcompat/res/drawable-xhdpi/abc_scrubber_control_to_pressed_mtrl_005.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_scrubber_primary_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_scrubber_primary_mtrl_alpha.9.png
new file mode 100644
index 0000000..2b4734d
--- /dev/null
+++ b/v7/appcompat/res/drawable-xhdpi/abc_scrubber_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_scrubber_track_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_scrubber_track_mtrl_alpha.9.png
new file mode 100644
index 0000000..805cb29
--- /dev/null
+++ b/v7/appcompat/res/drawable-xhdpi/abc_scrubber_track_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_control_off_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_control_off_mtrl_alpha.png
new file mode 100644
index 0000000..5268745
--- /dev/null
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_control_off_mtrl_alpha.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_control_to_pressed_mtrl_000.png b/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_control_to_pressed_mtrl_000.png
new file mode 100644
index 0000000..adffc14
--- /dev/null
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_control_to_pressed_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_control_to_pressed_mtrl_005.png b/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_control_to_pressed_mtrl_005.png
new file mode 100644
index 0000000..f3d16d5
--- /dev/null
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_control_to_pressed_mtrl_005.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_primary_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_primary_mtrl_alpha.9.png
new file mode 100644
index 0000000..6a82af5
--- /dev/null
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_track_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_track_mtrl_alpha.9.png
new file mode 100644
index 0000000..c3791fc
--- /dev/null
+++ b/v7/appcompat/res/drawable-xxhdpi/abc_scrubber_track_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_scrubber_control_to_pressed_mtrl_000.png b/v7/appcompat/res/drawable-xxxhdpi/abc_scrubber_control_to_pressed_mtrl_000.png
new file mode 100644
index 0000000..e5a43bb
--- /dev/null
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_scrubber_control_to_pressed_mtrl_000.png
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_scrubber_control_to_pressed_mtrl_005.png b/v7/appcompat/res/drawable-xxxhdpi/abc_scrubber_control_to_pressed_mtrl_005.png
new file mode 100644
index 0000000..eeb37c1
--- /dev/null
+++ b/v7/appcompat/res/drawable-xxxhdpi/abc_scrubber_control_to_pressed_mtrl_005.png
Binary files differ
diff --git a/v7/appcompat/res/drawable/abc_seekbar_thumb_material.xml b/v7/appcompat/res/drawable/abc_seekbar_thumb_material.xml
new file mode 100644
index 0000000..7fea83bc
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_seekbar_thumb_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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          android:constantSize="true">
+    <item android:state_enabled="false" android:state_pressed="true">
+        <bitmap android:src="@drawable/abc_scrubber_control_off_mtrl_alpha"
+                android:gravity="center"/>
+    </item>
+    <item android:state_enabled="false">
+        <bitmap android:src="@drawable/abc_scrubber_control_off_mtrl_alpha"
+                android:gravity="center"/>
+    </item>
+    <item android:state_pressed="true">
+        <bitmap android:src="@drawable/abc_scrubber_control_to_pressed_mtrl_005"
+                android:gravity="center"/>
+    </item>
+    <item>
+        <bitmap android:src="@drawable/abc_scrubber_control_to_pressed_mtrl_000"
+                android:gravity="center"/>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_seekbar_track_material.xml b/v7/appcompat/res/drawable/abc_seekbar_track_material.xml
new file mode 100644
index 0000000..e68ac03
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_seekbar_track_material.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/background"
+          android:drawable="@drawable/abc_scrubber_track_mtrl_alpha"/>
+    <item android:id="@android:id/secondaryProgress">
+        <scale android:scaleWidth="100%">
+            <selector>
+                <item android:state_enabled="false">
+                    <color android:color="@android:color/transparent"/>
+                </item>
+                <item android:drawable="@drawable/abc_scrubber_primary_mtrl_alpha"/>
+            </selector>
+        </scale>
+    </item>
+    <item android:id="@android:id/progress">
+        <scale android:scaleWidth="100%">
+            <selector>
+                <item android:state_enabled="false">
+                    <color android:color="@android:color/transparent"/>
+                </item>
+                <item android:drawable="@drawable/abc_scrubber_primary_mtrl_alpha"/>
+            </selector>
+        </scale>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml b/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml
new file mode 100644
index 0000000..4405707
--- /dev/null
+++ b/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+
+<android.support.v7.internal.widget.ButtonBarLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/buttonPanel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layoutDirection="locale"
+    android:orientation="horizontal"
+    android:paddingLeft="12dp"
+    android:paddingRight="12dp"
+    android:paddingTop="4dp"
+    android:paddingBottom="4dp"
+    android:gravity="bottom"
+    app:allowStacking="@bool/abc_allow_stacked_button_bar"
+    style="?attr/buttonBarStyle">
+
+    <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:id="@+id/spacer"
+        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" />
+
+</android.support.v7.internal.widget.ButtonBarLayout>
diff --git a/v7/appcompat/res/layout/abc_alert_dialog_material.xml b/v7/appcompat/res/layout/abc_alert_dialog_material.xml
index 9ba81fd..01ff885 100644
--- a/v7/appcompat/res/layout/abc_alert_dialog_material.xml
+++ b/v7/appcompat/res/layout/abc_alert_dialog_material.xml
@@ -108,41 +108,6 @@
                 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">
+    <include layout="@layout/abc_alert_dialog_button_bar_material" />
 
-        <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/values-az-rAZ/strings.xml b/v7/appcompat/res/values-az-rAZ/strings.xml
deleted file mode 100644
index a39f5f4..0000000
--- a/v7/appcompat/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="abc_action_mode_done" msgid="4076576682505996667">"Hazırdır"</string>
-    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Evə get"</string>
-    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Yuxarı get"</string>
-    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Daha çox seçim"</string>
-    <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"Dağıt"</string>
-    <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">"Axtarış"</string>
-    <string name="abc_search_hint" msgid="7723749260725869598">"Axtarış..."</string>
-    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Axtarış sorğusu"</string>
-    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Sorğunu təmizlə"</string>
-    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Sorğunu göndərin"</string>
-    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Səsli axtarış"</string>
-    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Tətbiq seçin"</string>
-    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Hamısına baxın"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for abc_shareactionprovider_share_with_application (7165123711973476752) -->
-    <skip />
-    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Bununla paylaşın"</string>
-    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
-</resources>
diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v7/appcompat/res/values-h320dp/bools.xml
similarity index 73%
copy from v17/leanback/res/layout/lb_card_color_overlay.xml
copy to v7/appcompat/res/values-h320dp/bools.xml
index 45a40e1..5576c18 100644
--- a/v17/leanback/res/layout/lb_card_color_overlay.xml
+++ b/v7/appcompat/res/values-h320dp/bools.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
@@ -15,7 +14,6 @@
      limitations under the License.
 -->
 
-<View
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent" />
+<resources>
+    <bool name="abc_allow_stacked_button_bar">true</bool>
+</resources>
diff --git a/v7/appcompat/res/values-kk-rKZ/strings.xml b/v7/appcompat/res/values-kk-rKZ/strings.xml
index 2ce080f..52b9af5 100644
--- a/v7/appcompat/res/values-kk-rKZ/strings.xml
+++ b/v7/appcompat/res/values-kk-rKZ/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="abc_action_mode_done" msgid="4076576682505996667">"Дайын"</string>
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Орындалды"</string>
     <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Негізгі бетте қозғалу"</string>
     <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Жоғары қозғалу"</string>
     <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Басқа опциялар"</string>
diff --git a/v7/appcompat/res/values-v21/styles_base.xml b/v7/appcompat/res/values-v21/styles_base.xml
index 47681f2..649f0ca 100644
--- a/v7/appcompat/res/values-v21/styles_base.xml
+++ b/v7/appcompat/res/values-v21/styles_base.xml
@@ -172,6 +172,8 @@
 
     <style name="Base.Widget.AppCompat.RatingBar" parent="android:Widget.Material.RatingBar" />
 
+    <style name="Base.Widget.AppCompat.SeekBar" parent="android:Widget.Material.SeekBar" />
+
     <style name="Base.Widget.AppCompat.Button" parent="android:Widget.Material.Button" />
 
     <style name="Base.Widget.AppCompat.Button.Small" parent="android:Widget.Material.Button.Small" />
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index 040bea1..c5c4244 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -371,6 +371,8 @@
         <attr name="radioButtonStyle" format="reference" />
         <!-- Default RatingBar style. -->
         <attr name="ratingBarStyle" format="reference" />
+        <!-- Default SeekBar style. -->
+        <attr name="seekBarStyle" format="reference" />
         <!-- Default Spinner style. -->
         <attr name="spinnerStyle" format="reference" />
         <!-- Default style for the Switch widget. -->
@@ -931,6 +933,10 @@
         <attr name="android:textStyle" />
         <attr name="android:typeface" />
         <attr name="textAllCaps" />
+        <attr name="android:shadowColor"/>
+        <attr name="android:shadowDy"/>
+        <attr name="android:shadowDx"/>
+        <attr name="android:shadowRadius"/>
     </declare-styleable>
 
     <!-- The set of attributes that describe a AlertDialog's theme. -->
@@ -943,4 +949,11 @@
         <attr name="listItemLayout" format="reference" />
     </declare-styleable>
 
+    <!-- @hide -->
+    <declare-styleable name="ButtonBarLayout">
+        <!-- Whether to automatically stack the buttons when there is not
+             enough space to lay them out side-by-side. -->
+        <attr name="allowStacking" format="boolean" />
+    </declare-styleable>
+
 </resources>
diff --git a/v7/appcompat/res/values/bools.xml b/v7/appcompat/res/values/bools.xml
index 79a5035..3508cf3 100644
--- a/v7/appcompat/res/values/bools.xml
+++ b/v7/appcompat/res/values/bools.xml
@@ -21,4 +21,8 @@
     <bool name="abc_action_bar_expanded_action_views_exclusive">true</bool>
 
     <bool name="abc_config_showMenuShortcutsWhenKeyboardPresent">false</bool>
+
+    <!-- Whether to allow vertically stacked button bars. This is disabled for
+         configurations with a small (e.g. less than 320dp) screen height. -->
+    <bool name="abc_allow_stacked_button_bar">false</bool>
 </resources>
diff --git a/v7/appcompat/res/values/colors_material.xml b/v7/appcompat/res/values/colors_material.xml
index cf30f0dd..70fd21d 100644
--- a/v7/appcompat/res/values/colors_material.xml
+++ b/v7/appcompat/res/values/colors_material.xml
@@ -30,8 +30,8 @@
     <color name="primary_dark_material_dark">@android:color/black</color>
     <color name="primary_dark_material_light">@color/material_grey_600</color>
 
-    <!-- 26% white (foreground) -->
-    <color name="ripple_material_dark">#42ffffff</color>
+    <!-- 20% white (foreground) -->
+    <color name="ripple_material_dark">#33ffffff</color>
     <!-- 12% black (foreground) -->
     <color name="ripple_material_light">#1f000000</color>
 
diff --git a/v7/appcompat/res/values/dimens_material.xml b/v7/appcompat/res/values/dimens_material.xml
index f67127c..357dc3e 100644
--- a/v7/appcompat/res/values/dimens_material.xml
+++ b/v7/appcompat/res/values/dimens_material.xml
@@ -68,4 +68,7 @@
     <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>
 
+    <dimen name="abc_seekbar_track_background_height_material">2dp</dimen>
+    <dimen name="abc_seekbar_track_progress_height_material">2dp</dimen>
+
 </resources>
diff --git a/v7/appcompat/res/values/strings.xml b/v7/appcompat/res/values/strings.xml
index a04b396..6e2a622 100644
--- a/v7/appcompat/res/values/strings.xml
+++ b/v7/appcompat/res/values/strings.xml
@@ -68,4 +68,9 @@
          for most appropriate textual indicator of "more than X".
          [CHAR LIMIT=4] -->
     <string name="status_bar_notification_info_overflow">999+</string>
+
+    <!-- Default text for a button that can be toggled on and off. -->
+    <string name="abc_capital_on">ON</string>
+    <!-- Default text for a button that can be toggled on and off. -->
+    <string name="abc_capital_off">OFF</string>
 </resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values/styles.xml b/v7/appcompat/res/values/styles.xml
index e10c01f..9453944 100644
--- a/v7/appcompat/res/values/styles.xml
+++ b/v7/appcompat/res/values/styles.xml
@@ -224,6 +224,8 @@
 
     <style name="AlertDialog.AppCompat.Light" parent="Base.AlertDialog.AppCompat.Light" />
 
+    <style name="Widget.AppCompat.SeekBar" parent="Base.Widget.AppCompat.SeekBar" />
+
     <!-- Toolbar -->
 
     <style name="Widget.AppCompat.Toolbar" parent="Base.Widget.AppCompat.Toolbar" />
diff --git a/v7/appcompat/res/values/styles_base.xml b/v7/appcompat/res/values/styles_base.xml
index d65b16f..0c3c056 100644
--- a/v7/appcompat/res/values/styles_base.xml
+++ b/v7/appcompat/res/values/styles_base.xml
@@ -386,6 +386,8 @@
         <item name="android:background">?attr/controlBackground</item>
         <item name="showText">false</item>
         <item name="switchPadding">@dimen/abc_switch_padding</item>
+        <item name="android:textOn">@string/abc_capital_on</item>
+        <item name="android:textOff">@string/abc_capital_off</item>
     </style>
 
     <style name="Base.TextAppearance.AppCompat.Widget.Switch" parent="TextAppearance.AppCompat.Button" />
@@ -395,6 +397,16 @@
         <item name="android:indeterminateDrawable">@drawable/abc_ratingbar_full_material</item>
     </style>
 
+    <style name="Base.Widget.AppCompat.SeekBar" parent="android:Widget">
+        <item name="android:indeterminateOnly">false</item>
+        <item name="android:progressDrawable">@drawable/abc_seekbar_track_material</item>
+        <item name="android:indeterminateDrawable">@drawable/abc_seekbar_track_material</item>
+        <item name="android:thumb">@drawable/abc_seekbar_thumb_material</item>
+        <item name="android:focusable">true</item>
+        <item name="android:paddingLeft">16dip</item>
+        <item name="android:paddingRight">16dip</item>
+    </style>
+
     <!-- Bordered ink button -->
     <style name="Base.Widget.AppCompat.Button" parent="android:Widget">
         <item name="android:background">@drawable/abc_btn_default_mtrl_shape</item>
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index d0dfca5..298196f 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -229,6 +229,7 @@
         <item name="switchStyle">@style/Widget.AppCompat.CompoundButton.Switch</item>
 
         <item name="ratingBarStyle">@style/Widget.AppCompat.RatingBar</item>
+        <item name="seekBarStyle">@style/Widget.AppCompat.SeekBar</item>
 
         <!-- Button styles -->
         <item name="buttonStyle">@style/Widget.AppCompat.Button</item>
@@ -379,6 +380,7 @@
         <item name="switchStyle">@style/Widget.AppCompat.CompoundButton.Switch</item>
 
         <item name="ratingBarStyle">@style/Widget.AppCompat.RatingBar</item>
+        <item name="seekBarStyle">@style/Widget.AppCompat.SeekBar</item>
 
         <!-- Button styles -->
         <item name="buttonStyle">@style/Widget.AppCompat.Button</item>
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
index 490abb4..31d1eb0 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
@@ -54,6 +54,8 @@
     boolean mIsFloating;
     // true if this activity has no title
     boolean mWindowNoTitle;
+    // true if the theme has been read
+    boolean mThemeRead;
 
     private CharSequence mTitle;
 
@@ -106,13 +108,11 @@
     @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_windowNoTitle, false)) {
             requestWindowFeature(Window.FEATURE_NO_TITLE);
         } else if (a.getBoolean(R.styleable.Theme_windowActionBar, false)) {
@@ -127,6 +127,7 @@
         }
         mIsFloating = a.getBoolean(R.styleable.Theme_android_windowIsFloating, false);
         a.recycle();
+        mThemeRead = true;
     }
 
     // Methods used to create and respond to options menu
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
index 1e138d0..21db106 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
@@ -277,6 +277,12 @@
 
     private void ensureSubDecor() {
         if (!mSubDecorInstalled) {
+            if (!mThemeRead) {
+                throw new IllegalStateException("AppCompat can not install it's decor before "
+                        + "reading the theme. This is usually caused by calling setContentView() "
+                        + "before super.onCreate().");
+            }
+
             final LayoutInflater inflater = LayoutInflater.from(mContext);
 
             if (!mWindowNoTitle) {
diff --git a/v7/appcompat/src/android/support/v7/graphics/drawable/DrawerArrowDrawable.java b/v7/appcompat/src/android/support/v7/graphics/drawable/DrawerArrowDrawable.java
index 82e8f18..2c658b1 100644
--- a/v7/appcompat/src/android/support/v7/graphics/drawable/DrawerArrowDrawable.java
+++ b/v7/appcompat/src/android/support/v7/graphics/drawable/DrawerArrowDrawable.java
@@ -447,6 +447,13 @@
     }
 
     /**
+     * Returns the paint instance used for all drawing.
+     */
+    public final Paint getPaint() {
+        return mPaint;
+    }
+
+    /**
      * Linear interpolate between a and b with parameter t.
      */
     private static float lerp(float a, float b, float t) {
diff --git a/v7/appcompat/src/android/support/v7/internal/app/AppCompatViewInflater.java b/v7/appcompat/src/android/support/v7/internal/app/AppCompatViewInflater.java
index 621fc51..7cf3e2d 100644
--- a/v7/appcompat/src/android/support/v7/internal/app/AppCompatViewInflater.java
+++ b/v7/appcompat/src/android/support/v7/internal/app/AppCompatViewInflater.java
@@ -17,9 +17,13 @@
 package android.support.v7.internal.app;
 
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.res.TypedArray;
+import android.os.Build;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.v4.util.ArrayMap;
+import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
 import android.support.v7.internal.view.ContextThemeWrapper;
 import android.support.v7.widget.AppCompatAutoCompleteTextView;
@@ -30,8 +34,8 @@
 import android.support.v7.widget.AppCompatMultiAutoCompleteTextView;
 import android.support.v7.widget.AppCompatRadioButton;
 import android.support.v7.widget.AppCompatRatingBar;
+import android.support.v7.widget.AppCompatSeekBar;
 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.util.Log;
@@ -39,7 +43,8 @@
 import android.view.View;
 
 import java.lang.reflect.Constructor;
-import java.util.HashMap;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.Map;
 
 /**
@@ -54,8 +59,9 @@
  */
 public class AppCompatViewInflater {
 
-    static final Class<?>[] sConstructorSignature = new Class[]{
+    private static final Class<?>[] sConstructorSignature = new Class[]{
             Context.class, AttributeSet.class};
+    private static final int[] sOnClickAttrs = new int[]{android.R.attr.onClick};
 
     private static final String LOG_TAG = "AppCompatViewInflater";
 
@@ -79,37 +85,57 @@
             context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
         }
 
+        View view = null;
+
         // We need to 'inject' our tint aware Views in place of the standard framework versions
         switch (name) {
             case "EditText":
-                return new AppCompatEditText(context, attrs);
+                view = new AppCompatEditText(context, attrs);
+                break;
             case "Spinner":
-                return new AppCompatSpinner(context, attrs);
+                view = new AppCompatSpinner(context, attrs);
+                break;
             case "CheckBox":
-                return new AppCompatCheckBox(context, attrs);
+                view = new AppCompatCheckBox(context, attrs);
+                break;
             case "RadioButton":
-                return new AppCompatRadioButton(context, attrs);
+                view = new AppCompatRadioButton(context, attrs);
+                break;
             case "CheckedTextView":
-                return new AppCompatCheckedTextView(context, attrs);
+                view = new AppCompatCheckedTextView(context, attrs);
+                break;
             case "AutoCompleteTextView":
-                return new AppCompatAutoCompleteTextView(context, attrs);
+                view = new AppCompatAutoCompleteTextView(context, attrs);
+                break;
             case "MultiAutoCompleteTextView":
-                return new AppCompatMultiAutoCompleteTextView(context, attrs);
+                view = new AppCompatMultiAutoCompleteTextView(context, attrs);
+                break;
             case "RatingBar":
-                return new AppCompatRatingBar(context, attrs);
+                view = new AppCompatRatingBar(context, attrs);
+                break;
             case "Button":
-                return new AppCompatButton(context, attrs);
+                view = new AppCompatButton(context, attrs);
+                break;
             case "TextView":
-                return new AppCompatTextView(context, attrs);
+                view = new AppCompatTextView(context, attrs);
+                break;
+            case "SeekBar":
+                view = new AppCompatSeekBar(context, attrs);
+                break;
         }
 
-        if (originalContext != context) {
+        if (view == null && originalContext != context) {
             // If the original context does not equal our themed context, then we need to manually
             // inflate it using the name so that android:theme takes effect.
-            return createViewFromTag(context, name, attrs);
+            view = createViewFromTag(context, name, attrs);
         }
 
-        return null;
+        if (view != null) {
+            // If we have created a view, check it's android:onClick
+            checkOnClickListener(view, attrs);
+        }
+
+        return view;
     }
 
     private View createViewFromTag(Context context, String name, AttributeSet attrs) {
@@ -138,6 +164,28 @@
         }
     }
 
+    /**
+     * android:onClick doesn't handle views with a ContextWrapper context. This method
+     * backports new framework functionality to traverse the Context wrappers to find a
+     * suitable target.
+     */
+    private void checkOnClickListener(View view, AttributeSet attrs) {
+        final Context context = view.getContext();
+
+        if (!ViewCompat.hasOnClickListeners(view) || !(context instanceof ContextWrapper)) {
+            // Skip our compat functionality if: the view doesn't have an onClickListener,
+            // or the Context isn't a ContextWrapper
+            return;
+        }
+
+        final TypedArray a = context.obtainStyledAttributes(attrs, sOnClickAttrs);
+        final String handlerName = a.getString(0);
+        if (handlerName != null) {
+            view.setOnClickListener(new DeclaredOnClickListener(view, handlerName));
+        }
+        a.recycle();
+    }
+
     private View createView(Context context, String name, String prefix)
             throws ClassNotFoundException, InflateException {
         Constructor<? extends View> constructor = sConstructorMap.get(name);
@@ -190,4 +238,70 @@
         }
         return context;
     }
+
+    /**
+     * An implementation of OnClickListener that attempts to lazily load a
+     * named click handling method from a parent or ancestor context.
+     */
+    private static class DeclaredOnClickListener implements View.OnClickListener {
+        private final View mHostView;
+        private final String mMethodName;
+
+        private Method mResolvedMethod;
+        private Context mResolvedContext;
+
+        public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) {
+            mHostView = hostView;
+            mMethodName = methodName;
+        }
+
+        @Override
+        public void onClick(@NonNull View v) {
+            if (mResolvedMethod == null) {
+                resolveMethod(mHostView.getContext(), mMethodName);
+            }
+
+            try {
+                mResolvedMethod.invoke(mResolvedContext, v);
+            } catch (IllegalAccessException e) {
+                throw new IllegalStateException(
+                        "Could not execute non-public method for android:onClick", e);
+            } catch (InvocationTargetException e) {
+                throw new IllegalStateException(
+                        "Could not execute method for android:onClick", e);
+            }
+        }
+
+        @NonNull
+        private void resolveMethod(@Nullable Context context, @NonNull String name) {
+            while (context != null) {
+                try {
+                    if (!context.isRestricted()) {
+                        final Method method = context.getClass().getMethod(mMethodName, View.class);
+                        if (method != null) {
+                            mResolvedMethod = method;
+                            mResolvedContext = context;
+                            return;
+                        }
+                    }
+                } catch (NoSuchMethodException e) {
+                    // Failed to find method, keep searching up the hierarchy.
+                }
+
+                if (context instanceof ContextWrapper) {
+                    context = ((ContextWrapper) context).getBaseContext();
+                } else {
+                    // Can't search up the hierarchy, null out and fail.
+                    context = null;
+                }
+            }
+
+            final int id = mHostView.getId();
+            final String idText = id == View.NO_ID ? "" : " with id '"
+                    + mHostView.getContext().getResources().getResourceEntryName(id) + "'";
+            throw new IllegalStateException("Could not find method " + mMethodName
+                    + "(View) in a parent or ancestor Context for android:onClick "
+                    + "attribute defined on view " + mHostView.getClass() + idText);
+        }
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/ButtonBarLayout.java b/v7/appcompat/src/android/support/v7/internal/widget/ButtonBarLayout.java
new file mode 100644
index 0000000..dd0044b
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ButtonBarLayout.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.internal.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.v7.appcompat.R;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * An extension of LinearLayout that automatically switches to vertical
+ * orientation when it can't fit its child views horizontally.
+ *
+ * @hide
+ */
+public class ButtonBarLayout extends LinearLayout {
+
+    /** Whether the current configuration allows stacking. */
+    private boolean mAllowStacking;
+    private int mLastWidthSize = -1;
+
+    public ButtonBarLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
+        mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, false);
+        ta.recycle();
+    }
+
+    public void setAllowStacking(boolean allowStacking) {
+        if (mAllowStacking != allowStacking) {
+            mAllowStacking = allowStacking;
+            if (!mAllowStacking && getOrientation() == LinearLayout.VERTICAL) {
+                setStacked(false);
+            }
+            requestLayout();
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+        if (mAllowStacking) {
+            if (widthSize > mLastWidthSize && isStacked()) {
+                // We're being measured wider this time, try un-stacking.
+                setStacked(false);
+            }
+            mLastWidthSize = widthSize;
+        }
+        boolean needsRemeasure = false;
+        // If we're not stacked, make sure the measure spec is AT_MOST rather
+        // than EXACTLY. This ensures that we'll still get TOO_SMALL so that we
+        // know to stack the buttons.
+        final int initialWidthMeasureSpec;
+        if (!isStacked() && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
+            initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
+            // We'll need to remeasure again to fill excess space.
+            needsRemeasure = true;
+        } else {
+            initialWidthMeasureSpec = widthMeasureSpec;
+        }
+        super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
+        if (mAllowStacking && !isStacked()) {
+            final int measuredWidth = getMeasuredWidthAndState();
+            final int measuredWidthState = measuredWidth & MEASURED_STATE_MASK;
+            if (measuredWidthState == MEASURED_STATE_TOO_SMALL) {
+                setStacked(true);
+                // Measure again in the new orientation.
+                needsRemeasure = true;
+            }
+        }
+        if (needsRemeasure) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    private void setStacked(boolean stacked) {
+        setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
+        setGravity(stacked ? Gravity.RIGHT : Gravity.BOTTOM);
+        final View spacer = findViewById(R.id.spacer);
+        if (spacer != null) {
+            spacer.setVisibility(stacked ? View.GONE : View.INVISIBLE);
+        }
+        // Reverse the child order. This is specific to the Material button
+        // bar's layout XML and will probably not generalize.
+        final int childCount = getChildCount();
+        for (int i = childCount - 2; i >= 0; i--) {
+            bringChildToFront(getChildAt(i));
+        }
+    }
+
+    private boolean isStacked() {
+        return getOrientation() == LinearLayout.VERTICAL;
+    }
+}
\ No newline at end of file
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 e2e6c4c..15e0e9b 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/ListViewCompat.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ListViewCompat.java
@@ -23,6 +23,7 @@
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v7.graphics.drawable.DrawableWrapper;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
@@ -50,6 +51,8 @@
     int mSelectionRightPadding = 0;
     int mSelectionBottomPadding = 0;
 
+    protected int mMotionPosition;
+
     private Field mIsChildViewEnabled;
 
     private GateKeeperDrawable mSelector;
@@ -107,6 +110,16 @@
         super.dispatchDraw(canvas);
     }
 
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mMotionPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
+                break;
+        }
+        return super.onTouchEvent(ev);
+    }
+
     protected void updateSelectorStateCompat() {
         Drawable selector = getSelector();
         if (selector != null && shouldShowSelectorCompat()) {
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 d926e17..ec85157 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
@@ -191,10 +191,18 @@
                     DrawableCompat.setTintMode(drawable, tintMode);
                 }
             } else if (resId == R.drawable.abc_cab_background_top_material) {
-                return new LayerDrawable(new Drawable[] {
+                return new LayerDrawable(new Drawable[]{
                         getDrawable(R.drawable.abc_cab_background_internal_bg),
                         getDrawable(R.drawable.abc_cab_background_top_mtrl_alpha)
                 });
+            } else if (resId == R.drawable.abc_seekbar_track_material) {
+                LayerDrawable ld = (LayerDrawable) drawable;
+                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
+                        getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
+                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
+                        getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
+                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
+                        getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
             } else {
                 final boolean usedColorFilter = tintDrawableUsingColorFilter(resId, drawable);
                 if (!usedColorFilter && failIfNotKnown) {
@@ -307,6 +315,8 @@
                 tint = getDefaultColorStateList(context);
             } else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) {
                 tint = createCheckableButtonColorStateList(context);
+            } else if (resId == R.drawable.abc_seekbar_thumb_material) {
+                tint = createSeekbarThumbColorStateList(context);
             }
 
             if (tint != null) {
@@ -541,6 +551,23 @@
         return new ColorStateList(states, colors);
     }
 
+    private ColorStateList createSeekbarThumbColorStateList(Context context) {
+        final int[][] states = new int[2][];
+        final int[] colors = new int[2];
+        int i = 0;
+
+        // Disabled state
+        states[i] = ThemeUtils.DISABLED_STATE_SET;
+        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlActivated);
+        i++;
+
+        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> {
 
         public ColorFilterLruCache(int maxSize) {
@@ -602,4 +629,8 @@
 
         return filter;
     }
+
+    private static void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) {
+        d.setColorFilter(getPorterDuffColorFilter(color, mode == null ? DEFAULT_MODE : mode));
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatProgressBarHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatProgressBarHelper.java
new file mode 100644
index 0000000..7fd8f05
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatProgressBarHelper.java
@@ -0,0 +1,151 @@
+/*
+ * 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.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Shader;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ClipDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+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.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.CompoundButton;
+import android.widget.ProgressBar;
+
+class AppCompatProgressBarHelper {
+
+    private static final int[] TINT_ATTRS = {
+            android.R.attr.indeterminateDrawable,
+            android.R.attr.progressDrawable
+    };
+
+    private final ProgressBar mView;
+    final TintManager mTintManager;
+
+    private Bitmap mSampleTile;
+
+    AppCompatProgressBarHelper(ProgressBar view, TintManager tintManager) {
+        mView = view;
+        mTintManager = tintManager;
+    }
+
+    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
+        TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
+                TINT_ATTRS, defStyleAttr, 0);
+
+        Drawable drawable = a.getDrawableIfKnown(0);
+        if (drawable != null) {
+            mView.setIndeterminateDrawable(tileifyIndeterminate(drawable));
+        }
+
+        drawable = a.getDrawableIfKnown(1);
+        if (drawable != null) {
+            mView.setProgressDrawable(tileify(drawable, false));
+        }
+
+        a.recycle();
+    }
+
+    /**
+     * Converts a drawable to a tiled version of itself. It will recursively
+     * traverse layer and state list drawables.
+     */
+    private Drawable tileify(Drawable drawable, boolean clip) {
+        if (drawable instanceof DrawableWrapper) {
+            Drawable inner = ((DrawableWrapper) drawable).getWrappedDrawable();
+            if (inner != null) {
+                inner = tileify(inner, clip);
+                ((DrawableWrapper) drawable).setWrappedDrawable(inner);
+            }
+        } else if (drawable instanceof LayerDrawable) {
+            LayerDrawable background = (LayerDrawable) drawable;
+            final int N = background.getNumberOfLayers();
+            Drawable[] outDrawables = new Drawable[N];
+
+            for (int i = 0; i < N; i++) {
+                int id = background.getId(i);
+                outDrawables[i] = tileify(background.getDrawable(i),
+                        (id == android.R.id.progress || id == android.R.id.secondaryProgress));
+            }
+            LayerDrawable newBg = new LayerDrawable(outDrawables);
+
+            for (int i = 0; i < N; i++) {
+                newBg.setId(i, background.getId(i));
+            }
+
+            return newBg;
+
+        } else if (drawable instanceof BitmapDrawable) {
+            final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap();
+            if (mSampleTile == null) {
+                mSampleTile = tileBitmap;
+            }
+
+            final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
+            final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
+                    Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
+            shapeDrawable.getPaint().setShader(bitmapShader);
+            return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
+                    ClipDrawable.HORIZONTAL) : shapeDrawable;
+        }
+
+        return drawable;
+    }
+
+    /**
+     * Convert a AnimationDrawable for use as a barberpole animation.
+     * Each frame of the animation is wrapped in a ClipDrawable and
+     * given a tiling BitmapShader.
+     */
+    private Drawable tileifyIndeterminate(Drawable drawable) {
+        if (drawable instanceof AnimationDrawable) {
+            AnimationDrawable background = (AnimationDrawable) drawable;
+            final int N = background.getNumberOfFrames();
+            AnimationDrawable newBg = new AnimationDrawable();
+            newBg.setOneShot(background.isOneShot());
+
+            for (int i = 0; i < N; i++) {
+                Drawable frame = tileify(background.getFrame(i), true);
+                frame.setLevel(10000);
+                newBg.addFrame(frame, background.getDuration(i));
+            }
+            newBg.setLevel(10000);
+            drawable = newBg;
+        }
+        return drawable;
+    }
+
+    private Shape getDrawableShape() {
+        final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
+        return new RoundRectShape(roundedCorners, null, null);
+    }
+
+    Bitmap getSampleTime() {
+        return mSampleTile;
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java b/v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java
index 51811a8..0bc14ab 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatRatingBar.java
@@ -18,23 +18,10 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Shader;
-import android.graphics.drawable.AnimationDrawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ClipDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-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;
 
 /**
@@ -45,12 +32,8 @@
  */
 public class AppCompatRatingBar extends RatingBar {
 
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.indeterminateDrawable,
-            android.R.attr.progressDrawable
-    };
-
-    private Bitmap mSampleTile;
+    private AppCompatProgressBarHelper mAppCompatProgressBarHelper;
+    private TintManager mTintManager;
 
     public AppCompatRatingBar(Context context) {
         this(context, null);
@@ -63,104 +46,19 @@
     public AppCompatRatingBar(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);
+        mTintManager = TintManager.get(context);
 
-            Drawable drawable = a.getDrawableIfKnown(0);
-            if (drawable != null) {
-                setIndeterminateDrawable(tileifyIndeterminate(drawable));
-            }
-
-            drawable = a.getDrawableIfKnown(1);
-            if (drawable != null) {
-                setProgressDrawable(tileify(drawable, false));
-            }
-
-            a.recycle();
-        }
-    }
-
-    /**
-     * Converts a drawable to a tiled version of itself. It will recursively
-     * traverse layer and state list drawables.
-     */
-    private Drawable tileify(Drawable drawable, boolean clip) {
-        if (drawable instanceof DrawableWrapper) {
-            Drawable inner = ((DrawableWrapper) drawable).getWrappedDrawable();
-            if (inner != null) {
-                inner = tileify(inner, clip);
-                ((DrawableWrapper) drawable).setWrappedDrawable(inner);
-            }
-        } else if (drawable instanceof LayerDrawable) {
-            LayerDrawable background = (LayerDrawable) drawable;
-            final int N = background.getNumberOfLayers();
-            Drawable[] outDrawables = new Drawable[N];
-
-            for (int i = 0; i < N; i++) {
-                int id = background.getId(i);
-                outDrawables[i] = tileify(background.getDrawable(i),
-                        (id == android.R.id.progress || id == android.R.id.secondaryProgress));
-            }
-            LayerDrawable newBg = new LayerDrawable(outDrawables);
-
-            for (int i = 0; i < N; i++) {
-                newBg.setId(i, background.getId(i));
-            }
-
-            return newBg;
-
-        } else if (drawable instanceof BitmapDrawable) {
-            final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap();
-            if (mSampleTile == null) {
-                mSampleTile = tileBitmap;
-            }
-
-            final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
-            final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
-                    Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
-            shapeDrawable.getPaint().setShader(bitmapShader);
-            return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
-                    ClipDrawable.HORIZONTAL) : shapeDrawable;
-        }
-
-        return drawable;
-    }
-
-    /**
-     * Convert a AnimationDrawable for use as a barberpole animation.
-     * Each frame of the animation is wrapped in a ClipDrawable and
-     * given a tiling BitmapShader.
-     */
-    private Drawable tileifyIndeterminate(Drawable drawable) {
-        if (drawable instanceof AnimationDrawable) {
-            AnimationDrawable background = (AnimationDrawable) drawable;
-            final int N = background.getNumberOfFrames();
-            AnimationDrawable newBg = new AnimationDrawable();
-            newBg.setOneShot(background.isOneShot());
-
-            for (int i = 0; i < N; i++) {
-                Drawable frame = tileify(background.getFrame(i), true);
-                frame.setLevel(10000);
-                newBg.addFrame(frame, background.getDuration(i));
-            }
-            newBg.setLevel(10000);
-            drawable = newBg;
-        }
-        return drawable;
-    }
-
-    private Shape getDrawableShape() {
-        final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
-        return new RoundRectShape(roundedCorners, null, null);
+        mAppCompatProgressBarHelper = new AppCompatProgressBarHelper(this, mTintManager);
+        mAppCompatProgressBarHelper.loadFromAttributes(attrs, defStyleAttr);
     }
 
     @Override
     protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        if (mSampleTile != null) {
-            final int width = mSampleTile.getWidth() * getNumStars();
+        Bitmap sampleTile = mAppCompatProgressBarHelper.getSampleTime();
+        if (sampleTile != null) {
+            final int width = sampleTile.getWidth() * getNumStars();
             setMeasuredDimension(ViewCompat.resolveSizeAndState(width, widthMeasureSpec, 0),
                     getMeasuredHeight());
         }
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
new file mode 100644
index 0000000..86ec881
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
@@ -0,0 +1,53 @@
+/*
+ * 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.v7.appcompat.R;
+import android.support.v7.internal.widget.TintManager;
+import android.util.AttributeSet;
+import android.widget.SeekBar;
+
+/**
+ * A {@link SeekBar} which supports compatible features on older version of the platform.
+ *
+ * <p>This will automatically be used when you use {@link SeekBar} in your layouts.
+ * You should only need to manually use this class when writing custom views.</p>
+ */
+public class AppCompatSeekBar extends SeekBar {
+
+    private AppCompatSeekBarHelper mAppCompatSeekBarHelper;
+    private TintManager mTintManager;
+
+    public AppCompatSeekBar(Context context) {
+        this(context, null);
+    }
+
+    public AppCompatSeekBar(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.seekBarStyle);
+    }
+
+    public AppCompatSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mTintManager = TintManager.get(context);
+
+        mAppCompatSeekBarHelper = new AppCompatSeekBarHelper(this, mTintManager);
+        mAppCompatSeekBarHelper.loadFromAttributes(attrs, defStyleAttr);
+    }
+
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java
new file mode 100644
index 0000000..3211942
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.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.v7.widget;
+
+import android.graphics.drawable.Drawable;
+import android.support.v7.internal.widget.TintManager;
+import android.support.v7.internal.widget.TintTypedArray;
+import android.util.AttributeSet;
+import android.widget.SeekBar;
+
+class AppCompatSeekBarHelper extends AppCompatProgressBarHelper {
+
+    private static final int[] TINT_ATTRS = {
+            android.R.attr.thumb
+    };
+
+    private final SeekBar mView;
+
+    AppCompatSeekBarHelper(SeekBar view, TintManager tintManager) {
+        super(view, tintManager);
+        mView = view;
+    }
+
+    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
+        super.loadFromAttributes(attrs, defStyleAttr);
+
+        TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
+                TINT_ATTRS, defStyleAttr, 0);
+        Drawable drawable = a.getDrawableIfKnown(0);
+        if (drawable != null) {
+            mView.setThumb(drawable);
+        }
+        a.recycle();
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java b/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
index 0d2bcb8..2b03774 100644
--- a/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
+++ b/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
@@ -26,6 +26,7 @@
 import android.os.SystemClock;
 import android.support.v4.text.TextUtilsCompat;
 import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewPropertyAnimatorCompat;
 import android.support.v4.widget.ListViewAutoScrollHelper;
 import android.support.v4.widget.PopupWindowCompat;
@@ -43,6 +44,7 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.view.WindowManager;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.LinearLayout;
@@ -93,6 +95,7 @@
     private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
     private int mDropDownHorizontalOffset;
     private int mDropDownVerticalOffset;
+    private int mDropDownWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
     private boolean mDropDownVerticalOffsetSet;
 
     private int mDropDownGravity = Gravity.NO_GRAVITY;
@@ -119,7 +122,7 @@
     private final ListSelectorHider mHideSelector = new ListSelectorHider();
     private Runnable mShowDropDownRunnable;
 
-    private Handler mHandler = new Handler();
+    private final Handler mHandler;
 
     private Rect mTempRect = new Rect();
 
@@ -226,6 +229,7 @@
      */
     public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         mContext = context;
+        mHandler = new Handler(context.getMainLooper());
 
         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ListPopupWindow,
                 defStyleAttr, defStyleRes);
@@ -531,6 +535,19 @@
     }
 
     /**
+     * Set the layout type for this popup window.
+     * <p>
+     * See {@link WindowManager.LayoutParams#type} for possible values.
+     *
+     * @param layoutType Layout type for this window.
+     *
+     * @see WindowManager.LayoutParams#type
+     */
+    public void setWindowLayoutType(int layoutType) {
+        mDropDownWindowLayoutType = layoutType;
+    }
+
+    /**
      * Sets a listener to receive events when a list item is clicked.
      *
      * @param clickListener Listener to register
@@ -583,12 +600,11 @@
     public void show() {
         int height = buildDropDown();
 
-        int widthSpec = 0;
-        int heightSpec = 0;
-
         boolean noInputMethod = isInputMethodNotNeeded();
+        PopupWindowCompat.setWindowLayoutType(mPopup, mDropDownWindowLayoutType);
 
         if (mPopup.isShowing()) {
+            final int widthSpec;
             if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
                 // The call to PopupWindow's update method below can accept -1 for any
                 // value you do not want to update.
@@ -599,19 +615,19 @@
                 widthSpec = mDropDownWidth;
             }
 
+            final int heightSpec;
             if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
                 // The call to PopupWindow's update method below can accept -1 for any
                 // value you do not want to update.
                 heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
                 if (noInputMethod) {
-                    mPopup.setWindowLayoutMode(
-                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
-                                    ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
+                    mPopup.setWidth(mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+                            ViewGroup.LayoutParams.MATCH_PARENT : 0);
+                    mPopup.setHeight(0);
                 } else {
-                    mPopup.setWindowLayoutMode(
-                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
-                                    ViewGroup.LayoutParams.MATCH_PARENT : 0,
-                            ViewGroup.LayoutParams.MATCH_PARENT);
+                    mPopup.setWidth(mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+                                    ViewGroup.LayoutParams.MATCH_PARENT : 0);
+                    mPopup.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
                 }
             } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
                 heightSpec = height;
@@ -622,29 +638,33 @@
             mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
 
             mPopup.update(getAnchorView(), mDropDownHorizontalOffset,
-                    mDropDownVerticalOffset, widthSpec, heightSpec);
+                            mDropDownVerticalOffset, (widthSpec < 0)? -1 : widthSpec,
+                            (heightSpec < 0)? -1 : heightSpec);
         } else {
+            final int widthSpec;
             if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
                 widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
             } else {
                 if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    mPopup.setWidth(getAnchorView().getWidth());
+                    widthSpec = getAnchorView().getWidth();
                 } else {
-                    mPopup.setWidth(mDropDownWidth);
+                    widthSpec = mDropDownWidth;
                 }
             }
 
+            final int heightSpec;
             if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
                 heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
             } else {
                 if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    mPopup.setHeight(height);
+                    heightSpec = height;
                 } else {
-                    mPopup.setHeight(mDropDownHeight);
+                    heightSpec = mDropDownHeight;
                 }
             }
 
-            mPopup.setWindowLayoutMode(widthSpec, heightSpec);
+            mPopup.setWidth(widthSpec);
+            mPopup.setHeight(heightSpec);
             setPopupClipToScreenEnabled(true);
 
             // use outside touchable to dismiss drop down when touching outside of it, so
@@ -1126,10 +1146,19 @@
                         break;
                 }
 
-                // measure the hint's height to find how much more vertical space
-                // we need to add to the drop down's height
-                int widthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.AT_MOST);
-                int heightSpec = MeasureSpec.UNSPECIFIED;
+                // Measure the hint's height to find how much more vertical
+                // space we need to add to the drop down's height.
+                final int widthSize;
+                final int widthMode;
+                if (mDropDownWidth >= 0) {
+                    widthMode = MeasureSpec.AT_MOST;
+                    widthSize = mDropDownWidth;
+                } else {
+                    widthMode = MeasureSpec.UNSPECIFIED;
+                    widthSize = 0;
+                }
+                final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
+                final int heightSpec = MeasureSpec.UNSPECIFIED;
                 hintView.measure(widthSpec, heightSpec);
 
                 hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
@@ -1629,6 +1658,11 @@
             // This will call through to updateSelectorState()
             drawableStateChanged();
 
+            final View motionView = getChildAt(mMotionPosition - getFirstVisiblePosition());
+            if (motionView != null) {
+                motionView.setPressed(false);
+            }
+
             if (mClickAnimation != null) {
                 mClickAnimation.cancel();
                 mClickAnimation = null;
@@ -1638,11 +1672,37 @@
         private void setPressedItem(View child, int position, float x, float y) {
             mDrawsInPressedState = true;
 
-            // Ordering is essential. First update the pressed state and layout
-            // the children. This will ensure the selector actually gets drawn.
-            setPressed(true);
+            // Ordering is essential. First, update the container's pressed state.
+            if (Build.VERSION.SDK_INT >= 21) {
+                drawableHotspotChanged(x, y);
+            }
+            if (!isPressed()) {
+                setPressed(true);
+            }
+
+            // Next, run layout to stabilize child positions.
             layoutChildren();
 
+            // Manage the pressed view based on motion position. This allows us to
+            // play nicely with actual touch and scroll events.
+            if (mMotionPosition != INVALID_POSITION) {
+                final View motionView = getChildAt(mMotionPosition - getFirstVisiblePosition());
+                if (motionView != null && motionView != child && motionView.isPressed()) {
+                    motionView.setPressed(false);
+                }
+            }
+            mMotionPosition = position;
+
+            // Offset for child coordinates.
+            final float childX = x - child.getLeft();
+            final float childY = y - child.getTop();
+            if (Build.VERSION.SDK_INT >= 21) {
+                child.drawableHotspotChanged(childX, childY);
+            }
+            if (!child.isPressed()) {
+                child.setPressed(true);
+            }
+
             // Ensure that keyboard focus starts from the last touched position.
             setSelection(position);
             positionSelectorLikeTouchCompat(position, child, x, y);
@@ -1697,7 +1757,6 @@
         public boolean hasFocus() {
             return mHijackFocus || super.hasFocus();
         }
-
     }
 
     private class PopupDataSetObserver extends DataSetObserver {
@@ -1723,8 +1782,9 @@
 
     private class ResizePopupRunnable implements Runnable {
         public void run() {
-            if (mDropDownList != null && mDropDownList.getCount() > mDropDownList.getChildCount() &&
-                    mDropDownList.getChildCount() <= mListItemExpandMaximum) {
+            if (mDropDownList != null && ViewCompat.isAttachedToWindow(mDropDownList)
+                    && mDropDownList.getCount() > mDropDownList.getChildCount()
+                    && mDropDownList.getChildCount() <= mListItemExpandMaximum) {
                 mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
                 show();
             }
diff --git a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
index 6aa55d7..19fe8ff 100644
--- a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
+++ b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
@@ -742,8 +742,9 @@
 
         if (newState != oldState) {
             playSoundEffect(SoundEffectConstants.CLICK);
-            setChecked(newState);
         }
+        // Always call setChecked so that the thumb is moved back to the correct edge
+        setChecked(newState);
         cancelSuperTouch(ev);
     }
 
diff --git a/v7/mediarouter/api/current.txt b/v7/mediarouter/api/current.txt
index 95fc9d2..7a94542 100644
--- a/v7/mediarouter/api/current.txt
+++ b/v7/mediarouter/api/current.txt
@@ -567,7 +567,7 @@
     method public java.util.List<android.support.v7.media.MediaRouter.RouteInfo> getRoutes();
   }
 
-  public static final class MediaRouter.RouteInfo {
+  public static class MediaRouter.RouteInfo {
     method public boolean canDisconnect();
     method public java.util.List<android.content.IntentFilter> getControlFilters();
     method public java.lang.String getDescription();
diff --git a/v7/mediarouter/build.gradle b/v7/mediarouter/build.gradle
index a304746..3461e7a 100644
--- a/v7/mediarouter/build.gradle
+++ b/v7/mediarouter/build.gradle
@@ -56,6 +56,11 @@
         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/v7/mediarouter/res/drawable-hdpi/ic_speaker_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_speaker_dark.png
new file mode 100755
index 0000000..0e1da44
--- /dev/null
+++ b/v7/mediarouter/res/drawable-hdpi/ic_speaker_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_speaker_group_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_speaker_group_dark.png
new file mode 100755
index 0000000..b90bb2f
--- /dev/null
+++ b/v7/mediarouter/res/drawable-hdpi/ic_speaker_group_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_speaker_group_light.png b/v7/mediarouter/res/drawable-hdpi/ic_speaker_group_light.png
new file mode 100755
index 0000000..afdb9c1
--- /dev/null
+++ b/v7/mediarouter/res/drawable-hdpi/ic_speaker_group_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_speaker_light.png b/v7/mediarouter/res/drawable-hdpi/ic_speaker_light.png
new file mode 100755
index 0000000..e2c88be
--- /dev/null
+++ b/v7/mediarouter/res/drawable-hdpi/ic_speaker_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_tv_dark.png b/v7/mediarouter/res/drawable-hdpi/ic_tv_dark.png
new file mode 100755
index 0000000..d1335f6
--- /dev/null
+++ b/v7/mediarouter/res/drawable-hdpi/ic_tv_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-hdpi/ic_tv_light.png b/v7/mediarouter/res/drawable-hdpi/ic_tv_light.png
new file mode 100755
index 0000000..7330f56
--- /dev/null
+++ b/v7/mediarouter/res/drawable-hdpi/ic_tv_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_speaker_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_speaker_dark.png
new file mode 100755
index 0000000..7cc9845
--- /dev/null
+++ b/v7/mediarouter/res/drawable-mdpi/ic_speaker_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_speaker_group_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_speaker_group_dark.png
new file mode 100755
index 0000000..4db7209
--- /dev/null
+++ b/v7/mediarouter/res/drawable-mdpi/ic_speaker_group_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_speaker_group_light.png b/v7/mediarouter/res/drawable-mdpi/ic_speaker_group_light.png
new file mode 100755
index 0000000..d26bb58
--- /dev/null
+++ b/v7/mediarouter/res/drawable-mdpi/ic_speaker_group_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_speaker_light.png b/v7/mediarouter/res/drawable-mdpi/ic_speaker_light.png
new file mode 100755
index 0000000..ae8d47f
--- /dev/null
+++ b/v7/mediarouter/res/drawable-mdpi/ic_speaker_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_tv_dark.png b/v7/mediarouter/res/drawable-mdpi/ic_tv_dark.png
new file mode 100755
index 0000000..82358a9
--- /dev/null
+++ b/v7/mediarouter/res/drawable-mdpi/ic_tv_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-mdpi/ic_tv_light.png b/v7/mediarouter/res/drawable-mdpi/ic_tv_light.png
new file mode 100755
index 0000000..ba3f3d5
--- /dev/null
+++ b/v7/mediarouter/res/drawable-mdpi/ic_tv_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_speaker_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_speaker_dark.png
new file mode 100755
index 0000000..8f8a552
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_speaker_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_speaker_group_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_speaker_group_dark.png
new file mode 100755
index 0000000..6227ca2
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_speaker_group_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_speaker_group_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_speaker_group_light.png
new file mode 100755
index 0000000..82599f5
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_speaker_group_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_speaker_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_speaker_light.png
new file mode 100755
index 0000000..74f9f6d
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_speaker_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_tv_dark.png b/v7/mediarouter/res/drawable-xhdpi/ic_tv_dark.png
new file mode 100755
index 0000000..cef8ac5
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_tv_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xhdpi/ic_tv_light.png b/v7/mediarouter/res/drawable-xhdpi/ic_tv_light.png
new file mode 100755
index 0000000..3131256
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xhdpi/ic_tv_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_dark.png
new file mode 100755
index 0000000..874c961
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_group_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_group_dark.png
new file mode 100755
index 0000000..6869bdc
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_group_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_group_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_group_light.png
new file mode 100755
index 0000000..35de6f4
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_group_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_light.png
new file mode 100755
index 0000000..65ee187
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_speaker_light.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_tv_dark.png b/v7/mediarouter/res/drawable-xxhdpi/ic_tv_dark.png
new file mode 100755
index 0000000..a6a4858
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_tv_dark.png
Binary files differ
diff --git a/v7/mediarouter/res/drawable-xxhdpi/ic_tv_light.png b/v7/mediarouter/res/drawable-xxhdpi/ic_tv_light.png
new file mode 100755
index 0000000..4ca6787
--- /dev/null
+++ b/v7/mediarouter/res/drawable-xxhdpi/ic_tv_light.png
Binary files differ
diff --git a/v7/mediarouter/res/layout-v17/mr_media_route_list_item.xml b/v7/mediarouter/res/layout-v17/mr_media_route_list_item.xml
index 1b798ee..e0bb840 100644
--- a/v7/mediarouter/res/layout-v17/mr_media_route_list_item.xml
+++ b/v7/mediarouter/res/layout-v17/mr_media_route_list_item.xml
@@ -16,32 +16,39 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="match_parent"
-              android:layout_height="?android:attr/listPreferredItemHeight"
+              android:layout_height="wrap_content"
+              android:orientation="horizontal"
               android:gravity="center_vertical">
 
+    <ImageView android:id="@+id/routeIcon"
+               android:layout_width="24dp"
+               android:layout_height="24dp"
+               android:layout_marginLeft="24dp"
+               android:layout_marginRight="24dp" />
+
     <LinearLayout android:layout_width="0dp"
-                  android:layout_height="match_parent"
+                  android:layout_height="wrap_content"
                   android:layout_weight="1"
+                  android:layout_marginRight="24dp"
                   android:orientation="vertical"
                   android:gravity="start|center_vertical"
-                  android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-                  android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
                   android:duplicateParentState="true">
 
         <TextView android:id="@android:id/text1"
                   android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
+                  android:layout_height="32dp"
                   android:singleLine="true"
+                  android:gravity="bottom"
                   android:ellipsize="marquee"
-                  android:textAppearance="?android:attr/textAppearanceMedium"
+                  android:textAppearance="?attr/mediaRouteTextStyle"
                   android:duplicateParentState="true" />
 
         <TextView android:id="@android:id/text2"
                   android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
+                  android:layout_height="24dp"
                   android:singleLine="true"
                   android:ellipsize="marquee"
-                  android:textAppearance="?android:attr/textAppearanceSmall"
+                  android:textAppearance="?attr/mediaRouteSecondaryTextStyle"
                   android:duplicateParentState="true" />
     </LinearLayout>
 
diff --git a/v7/mediarouter/res/layout/mr_media_route_chooser_dialog.xml b/v7/mediarouter/res/layout/mr_media_route_chooser_dialog.xml
index afdad71..6608f9a 100644
--- a/v7/mediarouter/res/layout/mr_media_route_chooser_dialog.xml
+++ b/v7/mediarouter/res/layout/mr_media_route_chooser_dialog.xml
@@ -17,25 +17,32 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="fill_parent"
               android:layout_height="wrap_content"
-              android:orientation="vertical">
+              android:orientation="vertical"
+              android:paddingTop="24dp"
+              android:paddingBottom="24dp">
+
     <ListView android:id="@+id/media_route_list"
               android:layout_width="fill_parent"
-              android:layout_height="wrap_content" />
+              android:layout_height="wrap_content"
+              android:divider="@android:color/transparent"
+              android:dividerHeight="@dimen/mr_list_item_margin"/>
 
     <LinearLayout android:id="@android:id/empty"
               android:layout_width="fill_parent"
-              android:layout_height="64dp"
-              android:orientation="horizontal"
+              android:layout_height="240dp"
+              android:orientation="vertical"
+              android:paddingTop="90dp"
               android:paddingLeft="16dp"
               android:paddingRight="16dp"
               android:visibility="gone">
-        <ProgressBar android:layout_width="wrap_content"
-                     android:layout_height="wrap_content"
-                     android:layout_gravity="center" />
         <TextView android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:layout_gravity="center"
-                  android:paddingLeft="16dp"
                   android:text="@string/mr_media_route_chooser_searching" />
+        <ProgressBar android:layout_width="150dp"
+                     android:layout_height="wrap_content"
+                     android:layout_gravity="center"
+                     android:indeterminate="true"
+                     style="?android:attr/progressBarStyleHorizontal" />
     </LinearLayout>
 </LinearLayout>
diff --git a/v7/mediarouter/res/values-az-rAZ/strings.xml b/v7/mediarouter/res/values-az-rAZ/strings.xml
deleted file mode 100644
index 2f3ff74..0000000
--- a/v7/mediarouter/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Cihazlar"</string>
-    <string name="mr_media_route_button_content_description" msgid="8327680881775995150">"İştirakçılar"</string>
-    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Cihaza qoş"</string>
-    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Cihazları axtarır..."</string>
-    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Bağlantını kəs"</string>
-    <string name="mr_media_route_controller_stop" msgid="5398645111664294430">"Yayımı dayandır"</string>
-    <string name="mr_media_route_controller_settings_description" msgid="379358765881274425">"Marşrut parametrləri"</string>
-    <string name="mr_media_route_controller_play" msgid="5214423499524760404">"Göstər"</string>
-    <string name="mr_media_route_controller_pause" msgid="8315773974194466049">"Fasilə ver"</string>
-</resources>
diff --git a/v17/leanback/res/layout/lb_card_color_overlay.xml b/v7/mediarouter/res/values-v17/dimens.xml
similarity index 73%
copy from v17/leanback/res/layout/lb_card_color_overlay.xml
copy to v7/mediarouter/res/values-v17/dimens.xml
index 45a40e1..41b5479 100644
--- a/v17/leanback/res/layout/lb_card_color_overlay.xml
+++ b/v7/mediarouter/res/values-v17/dimens.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
@@ -15,7 +14,6 @@
      limitations under the License.
 -->
 
-<View
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent" />
+<resources>
+    <dimen name="mr_list_item_margin">24dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/v7/mediarouter/res/values/attrs.xml b/v7/mediarouter/res/values/attrs.xml
index 9f5f8ac..36dd38f 100644
--- a/v7/mediarouter/res/values/attrs.xml
+++ b/v7/mediarouter/res/values/attrs.xml
@@ -34,4 +34,9 @@
     <attr name="mediaRoutePlayDrawable" format="reference" />
     <attr name="mediaRoutePauseDrawable" format="reference" />
     <attr name="mediaRouteCastDrawable" format="reference" />
-</resources>
\ No newline at end of file
+    <attr name="mediaRouteDefaultIconDrawable" format="reference" />
+    <attr name="mediaRouteSpeakerIconDrawable" format="reference" />
+    <attr name="mediaRouteSpeakerGroupIconDrawable" format="reference" />
+    <attr name="mediaRouteTextStyle" format="reference" />
+    <attr name="mediaRouteSecondaryTextStyle" format="reference" />
+</resources>
diff --git a/v7/mediarouter/res/values/dimens.xml b/v7/mediarouter/res/values/dimens.xml
index e687c82..b6c3058 100644
--- a/v7/mediarouter/res/values/dimens.xml
+++ b/v7/mediarouter/res/values/dimens.xml
@@ -16,4 +16,5 @@
 
 <resources>
     <dimen name="mr_media_route_controller_art_max_height">320dp</dimen>
+    <dimen name="mr_list_item_margin">0dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/v7/mediarouter/res/values/strings.xml b/v7/mediarouter/res/values/strings.xml
index a87ce4f..b621522 100644
--- a/v7/mediarouter/res/values/strings.xml
+++ b/v7/mediarouter/res/values/strings.xml
@@ -26,10 +26,10 @@
     <string name="mr_media_route_button_content_description">Cast</string>
 
     <!-- Title of the media route chooser dialog. [CHAR LIMIT=30] -->
-    <string name="mr_media_route_chooser_title">Connect to device</string>
+    <string name="mr_media_route_chooser_title">Cast to</string>
 
     <!-- Placeholder text to show when no devices have been found. [CHAR LIMIT=50] -->
-    <string name="mr_media_route_chooser_searching">Searching for devices\u2026</string>
+    <string name="mr_media_route_chooser_searching">Finding devices</string>
 
     <!-- Button to disconnect from a media route.  [CHAR LIMIT=30] -->
     <string name="mr_media_route_controller_disconnect">Disconnect</string>
diff --git a/v7/mediarouter/res/values/styles.xml b/v7/mediarouter/res/values/styles.xml
index 9be8545..1023f80 100644
--- a/v7/mediarouter/res/values/styles.xml
+++ b/v7/mediarouter/res/values/styles.xml
@@ -17,21 +17,41 @@
 <resources>
     <style name="Widget.MediaRouter.MediaRouteButton"
             parent="Widget.AppCompat.ActionButton">
-        <item name="android:minWidth">56dp</item>
-        <item name="android:minHeight">48dp</item>
-        <item name="android:padding">0dp</item>
-        <item name="android:focusable">true</item>
         <item name="android:contentDescription">@string/mr_media_route_button_content_description</item>
         <item name="externalRouteEnabledDrawable">@drawable/mr_ic_media_route_mono_dark</item>
     </style>
 
     <style name="Widget.MediaRouter.Light.MediaRouteButton"
             parent="Widget.AppCompat.Light.ActionButton">
-        <item name="android:minWidth">56dp</item>
-        <item name="android:minHeight">48dp</item>
-        <item name="android:padding">0dp</item>
-        <item name="android:focusable">true</item>
         <item name="android:contentDescription">@string/mr_media_route_button_content_description</item>
         <item name="externalRouteEnabledDrawable">@drawable/mr_ic_media_route_mono_light</item>
     </style>
-</resources>
\ No newline at end of file
+
+    <style name="Widget.MediaRouter.MediaRouteText" parent="">
+        <item name="android:fontFamily">sans-serif</item>
+    </style>
+
+    <style name="Widget.MediaRouter.MediaRouteText.Primary">
+        <item name="android:textSize">18sp</item>
+    </style>
+
+    <style name="Widget.MediaRouter.MediaRouteText.Secondary">
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="Widget.MediaRouter.MediaRouteText.Primary.Dark">
+        <item name="android:textColor">#FFFFFFFF</item>
+    </style>
+
+    <style name="Widget.MediaRouter.MediaRouteText.Primary.Light">
+        <item name="android:textColor">#DE000000</item>
+    </style>
+
+    <style name="Widget.MediaRouter.MediaRouteText.Secondary.Dark">
+        <item name="android:textColor">#8AFFFFFF</item>
+    </style>
+
+    <style name="Widget.MediaRouter.MediaRouteText.Secondary.Light">
+        <item name="android:textColor">#8A000000</item>
+    </style>
+</resources>
diff --git a/v7/mediarouter/res/values/themes.xml b/v7/mediarouter/res/values/themes.xml
index 85d0a8b..70a58ad 100644
--- a/v7/mediarouter/res/values/themes.xml
+++ b/v7/mediarouter/res/values/themes.xml
@@ -17,6 +17,7 @@
 <resources>
 
     <style name="Theme.MediaRouter" parent="">
+        <item name="android:windowNoTitle">false</item>
         <item name="mediaRouteButtonStyle">@style/Widget.MediaRouter.MediaRouteButton</item>
 
         <item name="mediaRouteOffDrawable">@drawable/ic_media_route_off_mono_dark</item>
@@ -26,9 +27,15 @@
         <item name="mediaRoutePlayDrawable">@drawable/mr_ic_play_dark</item>
         <item name="mediaRoutePauseDrawable">@drawable/mr_ic_pause_dark</item>
         <item name="mediaRouteCastDrawable">@drawable/mr_ic_cast_dark</item>
+        <item name="mediaRouteDefaultIconDrawable">@drawable/ic_tv_dark</item>
+        <item name="mediaRouteSpeakerIconDrawable">@drawable/ic_speaker_dark</item>
+        <item name="mediaRouteSpeakerGroupIconDrawable">@drawable/ic_speaker_group_dark</item>
+        <item name="mediaRouteTextStyle">@style/Widget.MediaRouter.MediaRouteText.Primary.Dark</item>
+        <item name="mediaRouteSecondaryTextStyle">@style/Widget.MediaRouter.MediaRouteText.Secondary.Dark</item>
     </style>
 
     <style name="Theme.MediaRouter.Light" parent="">
+        <item name="android:windowNoTitle">false</item>
         <item name="mediaRouteButtonStyle">@style/Widget.MediaRouter.Light.MediaRouteButton</item>
 
         <item name="mediaRouteOffDrawable">@drawable/ic_cast_off_light</item>
@@ -38,6 +45,11 @@
         <item name="mediaRoutePlayDrawable">@drawable/mr_ic_play_light</item>
         <item name="mediaRoutePauseDrawable">@drawable/mr_ic_pause_light</item>
         <item name="mediaRouteCastDrawable">@drawable/mr_ic_cast_light</item>
+        <item name="mediaRouteDefaultIconDrawable">@drawable/ic_tv_light</item>
+        <item name="mediaRouteSpeakerIconDrawable">@drawable/ic_speaker_light</item>
+        <item name="mediaRouteSpeakerGroupIconDrawable">@drawable/ic_speaker_group_light</item>
+        <item name="mediaRouteTextStyle">@style/Widget.MediaRouter.MediaRouteText.Primary.Light</item>
+        <item name="mediaRouteSecondaryTextStyle">@style/Widget.MediaRouter.MediaRouteText.Secondary.Light</item>
     </style>
 
 </resources>
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
index 379ba80..9fdd56c 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
@@ -431,40 +431,40 @@
         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 
-        final int minWidth = Math.max(mMinWidth,
-                mRemoteIndicator != null ? mRemoteIndicator.getIntrinsicWidth() : 0);
-        final int minHeight = Math.max(mMinHeight,
-                mRemoteIndicator != null ? mRemoteIndicator.getIntrinsicHeight() : 0);
+        final int width = Math.max(mMinWidth, mRemoteIndicator != null ?
+                mRemoteIndicator.getIntrinsicWidth() + getPaddingLeft() + getPaddingRight() : 0);
+        final int height = Math.max(mMinHeight, mRemoteIndicator != null ?
+                mRemoteIndicator.getIntrinsicHeight() + getPaddingTop() + getPaddingBottom() : 0);
 
-        int width;
+        int measuredWidth;
         switch (widthMode) {
             case MeasureSpec.EXACTLY:
-                width = widthSize;
+                measuredWidth = widthSize;
                 break;
             case MeasureSpec.AT_MOST:
-                width = Math.min(widthSize, minWidth + getPaddingLeft() + getPaddingRight());
+                measuredWidth = Math.min(widthSize, width);
                 break;
             default:
             case MeasureSpec.UNSPECIFIED:
-                width = minWidth + getPaddingLeft() + getPaddingRight();
+                measuredWidth = width;
                 break;
         }
 
-        int height;
+        int measuredHeight;
         switch (heightMode) {
             case MeasureSpec.EXACTLY:
-                height = heightSize;
+                measuredHeight = heightSize;
                 break;
             case MeasureSpec.AT_MOST:
-                height = Math.min(heightSize, minHeight + getPaddingTop() + getPaddingBottom());
+                measuredHeight = Math.min(heightSize, height);
                 break;
             default:
             case MeasureSpec.UNSPECIFIED:
-                height = minHeight + getPaddingTop() + getPaddingBottom();
+                measuredHeight = height;
                 break;
         }
 
-        setMeasuredDimension(width, height);
+        setMeasuredDimension(measuredWidth, measuredHeight);
     }
 
     @Override
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
index 779ae8b..d01f140 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
@@ -18,24 +18,31 @@
 
 import android.app.Dialog;
 import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.preference.PreferenceManager;
 import android.support.annotation.NonNull;
-import android.support.v7.media.MediaRouter;
+import android.support.v7.media.MediaControlIntent;
 import android.support.v7.media.MediaRouteSelector;
+import android.support.v7.media.MediaRouter;
 import android.support.v7.mediarouter.R;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.Window;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
+import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.TextView;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -138,16 +145,9 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
-
         setContentView(R.layout.mr_media_route_chooser_dialog);
         setTitle(R.string.mr_media_route_chooser_title);
 
-        // Must be called after setContentView.
-        getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
-                MediaRouterThemeHelper.getThemeResource(
-                        getContext(), R.attr.mediaRouteOffDrawable));
-
         mRoutes = new ArrayList<MediaRouter.RouteInfo>();
         mAdapter = new RouteAdapter(getContext(), mRoutes);
         mListView = (ListView)findViewById(R.id.media_route_list);
@@ -181,6 +181,7 @@
             mRoutes.clear();
             mRoutes.addAll(mRouter.getRoutes());
             onFilterRoutes(mRoutes);
+            RouteComparator.loadRouteUsageScores(getContext(), mRoutes);
             Collections.sort(mRoutes, RouteComparator.sInstance);
             mAdapter.notifyDataSetChanged();
         }
@@ -189,10 +190,21 @@
     private final class RouteAdapter extends ArrayAdapter<MediaRouter.RouteInfo>
             implements ListView.OnItemClickListener {
         private final LayoutInflater mInflater;
+        private final Drawable mDefaultIcon;
+        private final Drawable mSpeakerIcon;
+        private final Drawable mSpeakerGroupIcon;
 
         public RouteAdapter(Context context, List<MediaRouter.RouteInfo> routes) {
             super(context, 0, routes);
             mInflater = LayoutInflater.from(context);
+            int[] attrs = new int[] {
+                    R.attr.mediaRouteDefaultIconDrawable,
+                    R.attr.mediaRouteSpeakerIconDrawable,
+                    R.attr.mediaRouteSpeakerGroupIconDrawable };
+            TypedArray styledAttributes = getContext().obtainStyledAttributes(attrs);
+            mDefaultIcon = styledAttributes.getDrawable(0);
+            mSpeakerIcon = styledAttributes.getDrawable(1);
+            mSpeakerGroupIcon = styledAttributes.getDrawable(2);
         }
 
         @Override
@@ -211,9 +223,10 @@
             if (view == null) {
                 view = mInflater.inflate(R.layout.mr_media_route_list_item, parent, false);
             }
+
             MediaRouter.RouteInfo route = getItem(position);
-            TextView text1 = (TextView)view.findViewById(android.R.id.text1);
-            TextView text2 = (TextView)view.findViewById(android.R.id.text2);
+            TextView text1 = (TextView) view.findViewById(android.R.id.text1);
+            TextView text2 = (TextView) view.findViewById(android.R.id.text2);
             text1.setText(route.getName());
             String description = route.getDescription();
             if (TextUtils.isEmpty(description)) {
@@ -224,6 +237,11 @@
                 text2.setText(description);
             }
             view.setEnabled(route.isEnabled());
+
+            ImageView iconView = (ImageView) view.findViewById(R.id.routeIcon);
+            if (iconView != null) {
+                iconView.setImageDrawable(getIconDrawable(route));
+            }
             return view;
         }
 
@@ -232,9 +250,22 @@
             MediaRouter.RouteInfo route = getItem(position);
             if (route.isEnabled()) {
                 route.select();
+                RouteComparator.storeRouteUsageScores(getContext(), route.getId());
                 dismiss();
             }
         }
+
+        private Drawable getIconDrawable(MediaRouter.RouteInfo route) {
+            if (route instanceof MediaRouter.RouteGroup) {
+                // Only speakers can be grouped for now.
+                return mSpeakerGroupIcon;
+            }
+            if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
+                    && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
+                return mSpeakerIcon;
+            }
+            return mDefaultIcon;
+        }
     }
 
     private final class MediaRouterCallback extends MediaRouter.Callback {
@@ -260,11 +291,96 @@
     }
 
     private static final class RouteComparator implements Comparator<MediaRouter.RouteInfo> {
+        private static final String PREF_ROUTE_IDS =
+                "android.support.v7.app.MediaRouteChooserDialog_route_ids";
+        private static final String PREF_USAGE_SCORE_PREFIX =
+                "android.support.v7.app.MediaRouteChooserDialog_route_usage_score_";
+        // Routes with the usage score less than MIN_USAGE_SCORE are decayed.
+        private static final float MIN_USAGE_SCORE = 0.1f;
+        private static final float USAGE_SCORE_DECAY_FACTOR = 0.95f;
+
+        // Should match to SystemMediaRouteProvider.PACKAGE_NAME.
+        static final String SYSTEM_MEDIA_ROUTE_PROVIDER_PACKAGE_NAME = "android";
+
         public static final RouteComparator sInstance = new RouteComparator();
+        public static final HashMap<String, Float> sRouteUsageScoreMap = new HashMap();
 
         @Override
         public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) {
+            if (isSystemLiveAudioOnlyRoute(lhs)) {
+                if (!isSystemLiveAudioOnlyRoute(rhs)) {
+                    return 1;
+                }
+            } else if (isSystemLiveAudioOnlyRoute(rhs)) {
+                return -1;
+            }
+            Float lhsUsageScore = sRouteUsageScoreMap.get(lhs.getId());
+            if (lhsUsageScore == null) {
+                lhsUsageScore = 0f;
+            }
+            Float rhsUsageScore = sRouteUsageScoreMap.get(rhs.getId());
+            if (rhsUsageScore == null) {
+                rhsUsageScore = 0f;
+            }
+            if (!lhsUsageScore.equals(rhsUsageScore)) {
+                return lhsUsageScore > rhsUsageScore ? -1 : 1;
+            }
             return lhs.getName().compareTo(rhs.getName());
         }
+
+        private boolean isSystemLiveAudioOnlyRoute(MediaRouter.RouteInfo route) {
+            return isSystemMediaRouteProvider(route)
+                    && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
+                    && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
+        }
+
+        private boolean isSystemMediaRouteProvider(MediaRouter.RouteInfo route) {
+            return TextUtils.equals(route.getProviderInstance().getMetadata().getPackageName(),
+                    SYSTEM_MEDIA_ROUTE_PROVIDER_PACKAGE_NAME);
+        }
+
+        private static void loadRouteUsageScores(
+                Context context, List<MediaRouter.RouteInfo> routes) {
+            sRouteUsageScoreMap.clear();
+            SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+            for (MediaRouter.RouteInfo route : routes) {
+                sRouteUsageScoreMap.put(route.getId(),
+                        preferences.getFloat(PREF_USAGE_SCORE_PREFIX + route.getId(), 0f));
+            }
+        }
+
+        private static void storeRouteUsageScores(Context context, String selectedRouteId) {
+            SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+            SharedPreferences.Editor prefEditor = preferences.edit();
+            List<String> routeIds = new ArrayList<String>(
+                    Arrays.asList(preferences.getString(PREF_ROUTE_IDS, "").split(",")));
+            if (!routeIds.contains(selectedRouteId)) {
+                routeIds.add(selectedRouteId);
+            }
+            StringBuilder routeIdsBuilder = new StringBuilder();
+            for (String routeId : routeIds) {
+                // The new route usage score is calculated as follows:
+                // 1) usageScore * USAGE_SCORE_DECAY_FACTOR + 1, if the route is selected,
+                // 2) 0, if usageScore * USAGE_SCORE_DECAY_FACTOR < MIN_USAGE_SCORE, or
+                // 3) usageScore * USAGE_SCORE_DECAY_FACTOR, otherwise,
+                String routeUsageScoreKey = PREF_USAGE_SCORE_PREFIX + routeId;
+                float newUsageScore = preferences.getFloat(routeUsageScoreKey, 0f)
+                        * USAGE_SCORE_DECAY_FACTOR;
+                if (selectedRouteId.equals(routeId)) {
+                    newUsageScore += 1f;
+                }
+                if (newUsageScore < MIN_USAGE_SCORE) {
+                    prefEditor.remove(routeId);
+                } else {
+                    prefEditor.putFloat(routeUsageScoreKey, newUsageScore);
+                    if (routeIdsBuilder.length() > 0) {
+                        routeIdsBuilder.append(',');
+                    }
+                    routeIdsBuilder.append(routeId);
+                }
+            }
+            prefEditor.putString(PREF_ROUTE_IDS, routeIdsBuilder.toString());
+            prefEditor.commit();
+        }
     }
 }
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouteDescriptor.java b/v7/mediarouter/src/android/support/v7/media/MediaRouteDescriptor.java
index d83ad2f..f3909cc 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouteDescriptor.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouteDescriptor.java
@@ -37,6 +37,7 @@
  */
 public final class MediaRouteDescriptor {
     private static final String KEY_ID = "id";
+    private static final String KEY_CHILD_IDS = "child_ids";
     private static final String KEY_NAME = "name";
     private static final String KEY_DESCRIPTION = "status";
     private static final String KEY_ENABLED = "enabled";
@@ -73,6 +74,19 @@
     }
 
     /**
+     * Gets the child ids of the route.
+     * <p>
+     * A route descriptor that has one or more child route ids represents a
+     * route group. A child route may belong to another group.
+     * </p>
+     * @hide
+     * STOPSHIP: Unhide or remove.
+     */
+    public List<String> getChildIds() {
+        return mBundle.getStringArrayList(KEY_CHILD_IDS);
+    }
+
+    /**
      * Gets the user-visible name of the route.
      * <p>
      * The route name identifies the destination represented by the route.
@@ -109,10 +123,17 @@
     }
 
     /**
-     * Gets whether the route can be disconnected without stopping playback. To
-     * specify that the route should disconnect without stopping use
+     * Gets whether the route can be disconnected without stopping playback.
+     * <p>
+     * The route can normally be disconnected without stopping playback when
+     * the destination device on the route is connected to two or more source
+     * devices. The route provider should update the route immediately when the
+     * number of connected devices changes.
+     * </p><p>
+     * To specify that the route should disconnect without stopping use
      * {@link MediaRouter#unselect(int)} with
      * {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}.
+     * </p>
      */
     public boolean canDisconnectAndKeepPlaying() {
         return mBundle.getBoolean(KEY_CAN_DISCONNECT, false);
@@ -216,6 +237,7 @@
         StringBuilder result = new StringBuilder();
         result.append("MediaRouteDescriptor{ ");
         result.append("id=").append(getId());
+        result.append(", childIds=").append(getChildIds());
         result.append(", name=").append(getName());
         result.append(", description=").append(getDescription());
         result.append(", isEnabled=").append(isEnabled());
@@ -257,6 +279,7 @@
      */
     public static final class Builder {
         private final Bundle mBundle;
+        private ArrayList<String> mChildIds;
         private ArrayList<IntentFilter> mControlFilters;
 
         /**
@@ -302,6 +325,51 @@
         }
 
         /**
+         * Adds a child id of the route.
+         * <p>
+         * A route descriptor that has one or more child route ids represents a
+         * route group. A child route may belong to another group.
+         * </p>
+         * @hide
+         * STOPSHIP: Unhide or remove.
+         */
+        public Builder addChildId(String childId) {
+            if (TextUtils.isEmpty(childId)) {
+                throw new IllegalArgumentException("childId must not be empty");
+            }
+
+            if (mChildIds == null) {
+                mChildIds = new ArrayList<>();
+            }
+            if (!mChildIds.contains(childId)) {
+                mChildIds.add(childId);
+            }
+            return this;
+        }
+
+        /**
+         * Adds a list of child ids of the route.
+         * <p>
+         * A route descriptor that has one or more child route ids represents a
+         * route group. A child route may belong to another group.
+         * </p>
+         * @hide
+         * STOPSHIP: Unhide or remove.
+         */
+        public Builder addChildIds(Collection<String> childIds) {
+            if (childIds == null) {
+                throw new IllegalArgumentException("childIds must not be null");
+            }
+
+            if (!childIds.isEmpty()) {
+                for (String childId : childIds) {
+                    addChildId(childId);
+                }
+            }
+            return this;
+        }
+
+        /**
          * Sets the user-visible name of the route.
          * <p>
          * The route name identifies the destination represented by the route.
@@ -461,6 +529,9 @@
             if (mControlFilters != null) {
                 mBundle.putParcelableArrayList(KEY_CONTROL_FILTERS, mControlFilters);
             }
+            if (mChildIds != null) {
+                mBundle.putStringArrayList(KEY_CHILD_IDS, mChildIds);
+            }
             return new MediaRouteDescriptor(mBundle, mControlFilters);
         }
     }
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
index d37ed95..ef97421 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
@@ -17,7 +17,6 @@
 package android.support.v7.media;
 
 import android.app.ActivityManager;
-import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -37,6 +36,7 @@
 import android.support.v4.hardware.display.DisplayManagerCompat;
 import android.support.v4.media.VolumeProviderCompat;
 import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.util.Pair;
 import android.support.v7.media.MediaRouteProvider.ProviderMetadata;
 import android.util.Log;
 import android.view.Display;
@@ -762,7 +762,7 @@
      * route and the manner in which it is used and controlled.
      * </p>
      */
-    public static final class RouteInfo {
+    public static class RouteInfo {
         private final ProviderInfo mProvider;
         private final String mDescriptorId;
         private final String mUniqueId;
@@ -771,7 +771,7 @@
         private boolean mEnabled;
         private boolean mConnecting;
         private boolean mCanDisconnect;
-        private final ArrayList<IntentFilter> mControlFilters = new ArrayList<IntentFilter>();
+        private final ArrayList<IntentFilter> mControlFilters = new ArrayList<>();
         private int mPlaybackType;
         private int mPlaybackStream;
         private int mVolumeHandling;
@@ -781,7 +781,7 @@
         private int mPresentationDisplayId = -1;
         private Bundle mExtras;
         private IntentSender mSettingsIntent;
-        private MediaRouteDescriptor mDescriptor;
+        MediaRouteDescriptor mDescriptor;
 
         /** @hide */
         @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE})
@@ -1268,69 +1268,75 @@
                     + " }";
         }
 
-        int updateDescriptor(MediaRouteDescriptor descriptor) {
+        int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) {
             int changes = 0;
             if (mDescriptor != descriptor) {
-                mDescriptor = descriptor;
-                if (descriptor != null) {
-                    if (!equal(mName, descriptor.getName())) {
-                        mName = descriptor.getName();
-                        changes |= CHANGE_GENERAL;
-                    }
-                    if (!equal(mDescription, descriptor.getDescription())) {
-                        mDescription = descriptor.getDescription();
-                        changes |= CHANGE_GENERAL;
-                    }
-                    if (mEnabled != descriptor.isEnabled()) {
-                        mEnabled = descriptor.isEnabled();
-                        changes |= CHANGE_GENERAL;
-                    }
-                    if (mConnecting != descriptor.isConnecting()) {
-                        mConnecting = descriptor.isConnecting();
-                        changes |= CHANGE_GENERAL;
-                    }
-                    if (!mControlFilters.equals(descriptor.getControlFilters())) {
-                        mControlFilters.clear();
-                        mControlFilters.addAll(descriptor.getControlFilters());
-                        changes |= CHANGE_GENERAL;
-                    }
-                    if (mPlaybackType != descriptor.getPlaybackType()) {
-                        mPlaybackType = descriptor.getPlaybackType();
-                        changes |= CHANGE_GENERAL;
-                    }
-                    if (mPlaybackStream != descriptor.getPlaybackStream()) {
-                        mPlaybackStream = descriptor.getPlaybackStream();
-                        changes |= CHANGE_GENERAL;
-                    }
-                    if (mVolumeHandling != descriptor.getVolumeHandling()) {
-                        mVolumeHandling = descriptor.getVolumeHandling();
-                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
-                    }
-                    if (mVolume != descriptor.getVolume()) {
-                        mVolume = descriptor.getVolume();
-                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
-                    }
-                    if (mVolumeMax != descriptor.getVolumeMax()) {
-                        mVolumeMax = descriptor.getVolumeMax();
-                        changes |= CHANGE_GENERAL | CHANGE_VOLUME;
-                    }
-                    if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) {
-                        mPresentationDisplayId = descriptor.getPresentationDisplayId();
-                        mPresentationDisplay = null;
-                        changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
-                    }
-                    if (!equal(mExtras, descriptor.getExtras())) {
-                        mExtras = descriptor.getExtras();
-                        changes |= CHANGE_GENERAL;
-                    }
-                    if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) {
-                        mSettingsIntent = descriptor.getSettingsActivity();
-                        changes |= CHANGE_GENERAL;
-                    }
-                    if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) {
-                        mCanDisconnect = descriptor.canDisconnectAndKeepPlaying();
-                        changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
-                    }
+                changes = updateDescriptor(descriptor);
+            }
+            return changes;
+        }
+
+        int updateDescriptor(MediaRouteDescriptor descriptor) {
+            int changes = 0;
+            mDescriptor = descriptor;
+            if (descriptor != null) {
+                if (!equal(mName, descriptor.getName())) {
+                    mName = descriptor.getName();
+                    changes |= CHANGE_GENERAL;
+                }
+                if (!equal(mDescription, descriptor.getDescription())) {
+                    mDescription = descriptor.getDescription();
+                    changes |= CHANGE_GENERAL;
+                }
+                if (mEnabled != descriptor.isEnabled()) {
+                    mEnabled = descriptor.isEnabled();
+                    changes |= CHANGE_GENERAL;
+                }
+                if (mConnecting != descriptor.isConnecting()) {
+                    mConnecting = descriptor.isConnecting();
+                    changes |= CHANGE_GENERAL;
+                }
+                if (!mControlFilters.equals(descriptor.getControlFilters())) {
+                    mControlFilters.clear();
+                    mControlFilters.addAll(descriptor.getControlFilters());
+                    changes |= CHANGE_GENERAL;
+                }
+                if (mPlaybackType != descriptor.getPlaybackType()) {
+                    mPlaybackType = descriptor.getPlaybackType();
+                    changes |= CHANGE_GENERAL;
+                }
+                if (mPlaybackStream != descriptor.getPlaybackStream()) {
+                    mPlaybackStream = descriptor.getPlaybackStream();
+                    changes |= CHANGE_GENERAL;
+                }
+                if (mVolumeHandling != descriptor.getVolumeHandling()) {
+                    mVolumeHandling = descriptor.getVolumeHandling();
+                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
+                }
+                if (mVolume != descriptor.getVolume()) {
+                    mVolume = descriptor.getVolume();
+                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
+                }
+                if (mVolumeMax != descriptor.getVolumeMax()) {
+                    mVolumeMax = descriptor.getVolumeMax();
+                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
+                }
+                if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) {
+                    mPresentationDisplayId = descriptor.getPresentationDisplayId();
+                    mPresentationDisplay = null;
+                    changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
+                }
+                if (!equal(mExtras, descriptor.getExtras())) {
+                    mExtras = descriptor.getExtras();
+                    changes |= CHANGE_GENERAL;
+                }
+                if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) {
+                    mSettingsIntent = descriptor.getSettingsActivity();
+                    changes |= CHANGE_GENERAL;
+                }
+                if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) {
+                    mCanDisconnect = descriptor.canDisconnectAndKeepPlaying();
+                    changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
                 }
             }
             return changes;
@@ -1340,12 +1346,82 @@
             return mDescriptorId;
         }
 
-        MediaRouteProvider getProviderInstance() {
+        /** @hide */
+        public MediaRouteProvider getProviderInstance() {
             return mProvider.getProviderInstance();
         }
     }
 
     /**
+     * Information about a route that consists of multiple other routes in a group.
+     * @hide
+     * STOPSHIP: Unhide or remove.
+     */
+    public static class RouteGroup extends RouteInfo {
+        private List<RouteInfo> mRoutes = new ArrayList<>();
+
+        RouteGroup(ProviderInfo provider, String descriptorId, String uniqueId) {
+            super(provider, descriptorId, uniqueId);
+        }
+
+        /**
+         * @return The number of routes in this group
+         */
+        public int getRouteCount() {
+            return mRoutes.size();
+        }
+
+        /**
+         * Returns the route in this group at the specified index
+         *
+         * @param index Index to fetch
+         * @return The route at index
+         */
+        public RouteInfo getRouteAt(int index) {
+            return mRoutes.get(index);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(super.toString());
+            sb.append('[');
+            final int count = mRoutes.size();
+            for (int i = 0; i < count; i++) {
+                if (i > 0) sb.append(", ");
+                sb.append(mRoutes.get(i));
+            }
+            sb.append(']');
+            return sb.toString();
+        }
+
+        @Override
+        int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) {
+            boolean changed = false;
+            if (mDescriptor != descriptor) {
+                mDescriptor = descriptor;
+                if (descriptor != null) {
+                    List<String> childIds = descriptor.getChildIds();
+                    List<RouteInfo> routes = new ArrayList<>();
+                    changed = childIds.size() != mRoutes.size();
+                    for (String childId : childIds) {
+                        RouteInfo child = sGlobal.getRoute(childId);
+                        if (child != null) {
+                            routes.add(child);
+                            if (!changed && !mRoutes.contains(child)) {
+                                changed = true;
+                            }
+                        }
+                    }
+                    if (changed) {
+                        mRoutes = routes;
+                    }
+                }
+            }
+            return (changed ? CHANGE_GENERAL : 0) | super.updateDescriptor(descriptor);
+        }
+    }
+
+    /**
      * Provides information about a media route provider.
      * <p>
      * This object may be used to determine which media route provider has
@@ -1354,7 +1430,7 @@
      */
     public static final class ProviderInfo {
         private final MediaRouteProvider mProviderInstance;
-        private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+        private final List<RouteInfo> mRoutes = new ArrayList<>();
 
         private final ProviderMetadata mMetadata;
         private MediaRouteProviderDescriptor mDescriptor;
@@ -1720,6 +1796,15 @@
             }
         }
 
+        public RouteInfo getRoute(String uniqueId) {
+            for (RouteInfo info : mRoutes) {
+                if (info.mUniqueId.equals(uniqueId)) {
+                    return info;
+                }
+            }
+            return null;
+        }
+
         public List<RouteInfo> getRoutes() {
             return mRoutes;
         }
@@ -1930,6 +2015,11 @@
                         final List<MediaRouteDescriptor> routeDescriptors =
                                 providerDescriptor.getRoutes();
                         final int routeCount = routeDescriptors.size();
+                        // Updating route group's contents requires all member routes' information.
+                        // Add the groups to the lists and update them later.
+                        List<Pair<RouteInfo, MediaRouteDescriptor>> addedGroups = new ArrayList<>();
+                        List<Pair<RouteInfo, MediaRouteDescriptor>> updatedGroups =
+                                new ArrayList<>();
                         for (int i = 0; i < routeCount; i++) {
                             final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i);
                             final String id = routeDescriptor.getId();
@@ -1937,16 +2027,23 @@
                             if (sourceIndex < 0) {
                                 // 1. Add the route to the list.
                                 String uniqueId = assignRouteUniqueId(provider, id);
-                                RouteInfo route = new RouteInfo(provider, id, uniqueId);
+                                boolean isGroup = routeDescriptor.getChildIds() != null;
+                                RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) :
+                                        new RouteInfo(provider, id, uniqueId);
                                 provider.mRoutes.add(targetIndex++, route);
                                 mRoutes.add(route);
                                 // 2. Create the route's contents.
-                                route.updateDescriptor(routeDescriptor);
-                                // 3. Notify clients about addition.
-                                if (DEBUG) {
-                                    Log.d(TAG, "Route added: " + route);
+                                if (isGroup) {
+                                    addedGroups.add(new Pair(route, routeDescriptor));
+                                } else {
+                                    route.maybeUpdateDescriptor(routeDescriptor);
+                                    // 3. Notify clients about addition.
+                                    if (DEBUG) {
+                                        Log.d(TAG, "Route added: " + route);
+                                    }
+                                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
                                 }
-                                mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
+
                             } else if (sourceIndex < targetIndex) {
                                 Log.w(TAG, "Ignoring route descriptor with duplicate id: "
                                         + routeDescriptor);
@@ -1956,34 +2053,33 @@
                                 Collections.swap(provider.mRoutes,
                                         sourceIndex, targetIndex++);
                                 // 2. Update the route's contents.
-                                int changes = route.updateDescriptor(routeDescriptor);
-                                // 3. Notify clients about changes.
-                                if (changes != 0) {
-                                    if ((changes & RouteInfo.CHANGE_GENERAL) != 0) {
-                                        if (DEBUG) {
-                                            Log.d(TAG, "Route changed: " + route);
+                                if (route instanceof RouteGroup) {
+                                    updatedGroups.add(new Pair(route, routeDescriptor));
+                                } else {
+                                    // 3. Notify clients about changes.
+                                    if (updateRouteDescriptorAndNotify(route, routeDescriptor)
+                                            != 0) {
+                                        if (route == mSelectedRoute) {
+                                            selectedRouteDescriptorChanged = true;
                                         }
-                                        mCallbackHandler.post(
-                                                CallbackHandler.MSG_ROUTE_CHANGED, route);
                                     }
-                                    if ((changes & RouteInfo.CHANGE_VOLUME) != 0) {
-                                        if (DEBUG) {
-                                            Log.d(TAG, "Route volume changed: " + route);
-                                        }
-                                        mCallbackHandler.post(
-                                                CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route);
-                                    }
-                                    if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) {
-                                        if (DEBUG) {
-                                            Log.d(TAG, "Route presentation display changed: "
-                                                    + route);
-                                        }
-                                        mCallbackHandler.post(CallbackHandler.
-                                                MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route);
-                                    }
-                                    if (route == mSelectedRoute) {
-                                        selectedRouteDescriptorChanged = true;
-                                    }
+                                }
+                            }
+                        }
+                        // Update the new and/or existing groups.
+                        for (Pair<RouteInfo, MediaRouteDescriptor> pair : addedGroups) {
+                            RouteInfo route = pair.first;
+                            route.maybeUpdateDescriptor(pair.second);
+                            if (DEBUG) {
+                                Log.d(TAG, "Route added: " + route);
+                            }
+                            mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
+                        }
+                        for (Pair<RouteInfo, MediaRouteDescriptor> pair : updatedGroups) {
+                            RouteInfo route = pair.first;
+                            if (updateRouteDescriptorAndNotify(route, pair.second) != 0) {
+                                if (route == mSelectedRoute) {
+                                    selectedRouteDescriptorChanged = true;
                                 }
                             }
                         }
@@ -1996,7 +2092,7 @@
                 for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
                     // 1. Delete the route's contents.
                     RouteInfo route = provider.mRoutes.get(i);
-                    route.updateDescriptor(null);
+                    route.maybeUpdateDescriptor(null);
                     // 2. Remove the route from the list.
                     mRoutes.remove(route);
                 }
@@ -2025,6 +2121,36 @@
             }
         }
 
+        private int updateRouteDescriptorAndNotify(RouteInfo route,
+                MediaRouteDescriptor routeDescriptor) {
+            int changes = route.maybeUpdateDescriptor(routeDescriptor);
+            if (changes != 0) {
+                if ((changes & RouteInfo.CHANGE_GENERAL) != 0) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Route changed: " + route);
+                    }
+                    mCallbackHandler.post(
+                            CallbackHandler.MSG_ROUTE_CHANGED, route);
+                }
+                if ((changes & RouteInfo.CHANGE_VOLUME) != 0) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Route volume changed: " + route);
+                    }
+                    mCallbackHandler.post(
+                            CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route);
+                }
+                if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Route presentation display changed: "
+                                + route);
+                    }
+                    mCallbackHandler.post(CallbackHandler.
+                            MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route);
+                }
+            }
+            return changes;
+        }
+
         private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) {
             // Although route descriptor ids are unique within a provider, it's
             // possible for there to be two providers with the same package name.
diff --git a/v7/palette/api/current.txt b/v7/palette/api/current.txt
index 1b6c745..fac6a55 100644
--- a/v7/palette/api/current.txt
+++ b/v7/palette/api/current.txt
@@ -27,10 +27,12 @@
     ctor public Palette.Builder(java.util.List<android.support.v7.graphics.Palette.Swatch>);
     method public android.support.v7.graphics.Palette.Builder addFilter(android.support.v7.graphics.Palette.Filter);
     method public android.support.v7.graphics.Palette.Builder clearFilters();
+    method public android.support.v7.graphics.Palette.Builder clearRegion();
     method public android.support.v7.graphics.Palette generate();
     method public android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generate(android.support.v7.graphics.Palette.PaletteAsyncListener);
     method public android.support.v7.graphics.Palette.Builder maximumColorCount(int);
     method public android.support.v7.graphics.Palette.Builder resizeBitmapSize(int);
+    method public android.support.v7.graphics.Palette.Builder setRegion(int, int, int, int);
   }
 
   public static abstract interface Palette.Filter {
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
index 1ffb7a1..a476559 100644
--- a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
+++ b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.os.AsyncTask;
 import android.support.annotation.ColorInt;
 import android.support.annotation.Nullable;
@@ -441,11 +442,12 @@
      * Builder class for generating {@link Palette} instances.
      */
     public static final class Builder {
-        private List<Swatch> mSwatches;
-        private Bitmap mBitmap;
+        private final List<Swatch> mSwatches;
+        private final Bitmap mBitmap;
         private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS;
         private int mResizeMaxDimension = DEFAULT_RESIZE_BITMAP_MAX_DIMENSION;
         private final List<Filter> mFilters = new ArrayList<>();
+        private Rect mRegion;
 
         private Generator mGenerator;
 
@@ -453,11 +455,12 @@
          * Construct a new {@link Builder} using a source {@link Bitmap}
          */
         public Builder(Bitmap bitmap) {
-            this();
             if (bitmap == null || bitmap.isRecycled()) {
                 throw new IllegalArgumentException("Bitmap is not valid");
             }
+            mFilters.add(DEFAULT_FILTER);
             mBitmap = bitmap;
+            mSwatches = null;
         }
 
         /**
@@ -465,15 +468,12 @@
          * Typically only used for testing.
          */
         public Builder(List<Swatch> swatches) {
-            this();
             if (swatches == null || swatches.isEmpty()) {
                 throw new IllegalArgumentException("List of Swatches is not valid");
             }
-            mSwatches = swatches;
-        }
-
-        private Builder() {
             mFilters.add(DEFAULT_FILTER);
+            mSwatches = swatches;
+            mBitmap = null;
         }
 
         /**
@@ -536,6 +536,37 @@
         }
 
         /**
+         * Set a region of the bitmap to be used exclusively when calculating the palette.
+         * <p>This only works when the original input is a {@link Bitmap}.</p>
+         *
+         * @param left The left side of the rectangle used for the region.
+         * @param top The top of the rectangle used for the region.
+         * @param right The right side of the rectangle used for the region.
+         * @param bottom The bottom of the rectangle used for the region.
+         */
+        public Builder setRegion(int left, int top, int right, int bottom) {
+            if (mBitmap != null) {
+                if (mRegion == null) mRegion = new Rect();
+                // Set the Rect to be initially the whole Bitmap
+                mRegion.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+                // Now just get the intersection with the region
+                if (!mRegion.intersect(left, top, right, bottom)) {
+                    throw new IllegalArgumentException("The given region must intersect with "
+                            + "the Bitmap's dimensions.");
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Clear any previously region set via {@link #setRegion(int, int, int, int)}.
+         */
+        public Builder clearRegion() {
+            mRegion = null;
+            return this;
+        }
+
+        /**
          * Generate and return the {@link Palette} synchronously.
          */
         public Palette generate() {
@@ -554,24 +585,32 @@
                 }
 
                 // First we'll scale down the bitmap so it's largest dimension is as specified
-                final Bitmap scaledBitmap = scaleBitmapDown(mBitmap, mResizeMaxDimension);
+                final Bitmap bitmap = scaleBitmapDown(mBitmap, mResizeMaxDimension);
 
                 if (logger != null) {
                     logger.addSplit("Processed Bitmap");
                 }
 
-                // Now generate a quantizer from the Bitmap
-                final int width = scaledBitmap.getWidth();
-                final int height = scaledBitmap.getHeight();
-                final int[] pixels = new int[width * height];
-                scaledBitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+                final Rect region = mRegion;
+                if (bitmap != mBitmap && region != null) {
+                    // If we have a scaled bitmap and a selected region, we need to scale down the
+                    // region to match the new scale
+                    final float scale = bitmap.getWidth() / (float) mBitmap.getWidth();
+                    region.left = (int) Math.floor(region.left * scale);
+                    region.top = (int) Math.floor(region.top * scale);
+                    region.right = (int) Math.ceil(region.right * scale);
+                    region.bottom = (int) Math.ceil(region.bottom * scale);
+                }
 
-                final ColorCutQuantizer quantizer = new ColorCutQuantizer(pixels, mMaxColors,
+                // Now generate a quantizer from the Bitmap
+                final ColorCutQuantizer quantizer = new ColorCutQuantizer(
+                        getPixelsFromBitmap(),
+                        mMaxColors,
                         mFilters.isEmpty() ? null : mFilters.toArray(new Filter[mFilters.size()]));
 
                 // If created a new bitmap, recycle it
-                if (scaledBitmap != mBitmap) {
-                    scaledBitmap.recycle();
+                if (bitmap != mBitmap) {
+                    bitmap.recycle();
                 }
                 swatches = quantizer.getQuantizedColors();
 
@@ -629,6 +668,35 @@
                         }
                     }, mBitmap);
         }
+
+        private int[] getPixelsFromBitmap() {
+            final Bitmap bitmap = mBitmap;
+            final int bitmapWidth = bitmap.getWidth();
+            final int bitmapHeight = bitmap.getHeight();
+            final int[] pixels = new int[bitmapWidth * bitmapHeight];
+
+            if (mRegion == null) {
+                // If we don't have a region, return all of the pixels
+                bitmap.getPixels(pixels, 0, bitmapWidth, 0, 0, bitmapWidth, bitmapHeight);
+                return pixels;
+            } else {
+                // If we do have a region, lets create a subset array containing only the region's
+                // pixels
+                final int regionWidth = mRegion.width();
+                final int regionHeight = mRegion.height();
+                // First read the pixels within the region
+                bitmap.getPixels(pixels, 0, bitmapWidth, mRegion.left, mRegion.top,
+                        regionWidth, regionHeight);
+                // pixels now contains all of the pixels, but not packed together. We need to
+                // iterate through each row and copy them into a new smaller array
+                final int[] subsetPixels = new int[regionWidth * mRegion.height()];
+                for (int row = mRegion.top; row < mRegion.bottom; row++) {
+                    System.arraycopy(pixels, (row * bitmapWidth) + mRegion.left,
+                            subsetPixels, row * regionWidth, regionWidth);
+                }
+                return subsetPixels;
+            }
+        }
     }
 
     static abstract class Generator {
diff --git a/v7/preference/res/layout/preference_category.xml b/v7/preference/res/layout/preference_category.xml
index 0b58063..258f7da 100644
--- a/v7/preference/res/layout/preference_category.xml
+++ b/v7/preference/res/layout/preference_category.xml
@@ -20,6 +20,6 @@
     android:layout_height="wrap_content" >
     <TextView
      style="?android:attr/listSeparatorTextViewStyle"
-     android:id="@+android:id/title"
+     android:id="@android:id/title"
     />
 </FrameLayout>
diff --git a/v7/preference/res/layout/preference_information.xml b/v7/preference/res/layout/preference_information.xml
index e3be3820..5815c46 100644
--- a/v7/preference/res/layout/preference_information.xml
+++ b/v7/preference/res/layout/preference_information.xml
@@ -33,14 +33,14 @@
         android:layout_marginBottom="6sp"
         android:layout_weight="1">
 
-        <TextView android:id="@+android:id/title"
+        <TextView android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:singleLine="true"
             android:textAppearance="?android:attr/textAppearanceLarge"
             android:textColor="?android:attr/textColorSecondary" />
 
-        <TextView android:id="@+android:id/summary"
+        <TextView android:id="@android:id/summary"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_below="@android:id/title"
@@ -52,7 +52,7 @@
     </RelativeLayout>
 
     <!-- Preference should place its actual preference widget here. -->
-    <LinearLayout android:id="@+android:id/widget_frame"
+    <LinearLayout android:id="@android:id/widget_frame"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:gravity="center_vertical"