Merge "MediaRouter: Clean up resources"
diff --git a/api/current.txt b/api/current.txt
index 2f64e50..7dc5ab7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -160,6 +160,7 @@
     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_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
+    field public static final java.lang.String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
     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";
@@ -197,6 +198,7 @@
     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);
+    method public android.support.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
     method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(int);
     method public android.support.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[], android.app.PendingIntent);
     method public android.support.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
@@ -307,6 +309,24 @@
     method public boolean onDependentViewChanged(android.support.design.widget.CoordinatorLayout, android.view.View, android.view.View);
   }
 
+  public class BottomNavigationView extends android.widget.FrameLayout {
+    ctor public BottomNavigationView(android.content.Context);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet);
+    ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet, int);
+    method public android.content.res.ColorStateList getItemIconTintList();
+    method public android.content.res.ColorStateList getItemTextColor();
+    method public android.view.Menu getMenu();
+    method public void inflateMenu(int);
+    method public void setItemBackgroundResource(int);
+    method public void setItemIconTintList(android.content.res.ColorStateList);
+    method public void setItemTextColor(android.content.res.ColorStateList);
+    method public void setOnNavigationItemSelectedListener(android.support.design.widget.BottomNavigationView.OnNavigationItemSelectedListener);
+  }
+
+  public static abstract interface BottomNavigationView.OnNavigationItemSelectedListener {
+    method public abstract boolean onNavigationItemSelected(android.view.MenuItem);
+  }
+
   public class BottomSheetBehavior extends android.support.design.widget.CoordinatorLayout.Behavior {
     ctor public BottomSheetBehavior();
     ctor public BottomSheetBehavior(android.content.Context, android.util.AttributeSet);
@@ -722,11 +742,14 @@
     method public android.widget.EditText getEditText();
     method public java.lang.CharSequence getError();
     method public java.lang.CharSequence getHint();
+    method public java.lang.CharSequence getPasswordVisibilityToggleContentDescription();
+    method public android.graphics.drawable.Drawable getPasswordVisibilityToggleDrawable();
     method public android.graphics.Typeface getTypeface();
     method public boolean isCounterEnabled();
     method public boolean isErrorEnabled();
     method public boolean isHintAnimationEnabled();
     method public boolean isHintEnabled();
+    method public boolean isPasswordVisibilityToggleEnabled();
     method public android.os.Parcelable onSaveInstanceState();
     method public void setCounterEnabled(boolean);
     method public void setCounterMaxLength(int);
@@ -736,6 +759,13 @@
     method public void setHintAnimationEnabled(boolean);
     method public void setHintEnabled(boolean);
     method public void setHintTextAppearance(int);
+    method public void setPasswordVisibilityToggleContentDescription(int);
+    method public void setPasswordVisibilityToggleContentDescription(java.lang.CharSequence);
+    method public void setPasswordVisibilityToggleDrawable(int);
+    method public void setPasswordVisibilityToggleDrawable(android.graphics.drawable.Drawable);
+    method public void setPasswordVisibilityToggleEnabled(boolean);
+    method public void setPasswordVisibilityToggleTintList(android.content.res.ColorStateList);
+    method public void setPasswordVisibilityToggleTintMode(android.graphics.PorterDuff.Mode);
     method public void setTypeface(android.graphics.Typeface);
   }
 
@@ -2074,15 +2104,21 @@
     ctor public AbstractMediaItemPresenter(int);
     method protected android.support.v17.leanback.widget.RowPresenter.ViewHolder createRowViewHolder(android.view.ViewGroup);
     method public android.support.v17.leanback.widget.Presenter getActionPresenter();
+    method protected int getMediaPlayState(java.lang.Object);
     method public int getThemeId();
     method public boolean hasMediaRowSeparator();
     method protected abstract void onBindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder, java.lang.Object);
+    method public void onBindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
     method protected void onBindRowActions(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
     method protected void onUnbindMediaDetails(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
+    method public void onUnbindMediaPlayState(android.support.v17.leanback.widget.AbstractMediaItemPresenter.ViewHolder);
     method public void setActionPresenter(android.support.v17.leanback.widget.Presenter);
     method public void setBackgroundColor(int);
     method public void setHasMediaRowSeparator(boolean);
     method public void setThemeId(int);
+    field public static final int PLAY_STATE_INITIAL = 0; // 0x0
+    field public static final int PLAY_STATE_PAUSED = 1; // 0x1
+    field public static final int PLAY_STATE_PLAYING = 2; // 0x2
   }
 
   public static class AbstractMediaItemPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
@@ -2092,12 +2128,17 @@
     method public android.widget.TextView getMediaItemDurationView();
     method public android.widget.TextView getMediaItemNameView();
     method public android.widget.TextView getMediaItemNumberView();
+    method public android.widget.ViewFlipper getMediaItemNumberViewFlipper();
+    method public android.view.View getMediaItemPausedView();
+    method public android.view.View getMediaItemPlayingView();
     method public android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getMediaItemRowActions();
     method public android.view.View getMediaItemRowSeparator();
     method public android.view.View getSelectorView();
     method public void notifyActionChanged(android.support.v17.leanback.widget.MultiActionsProvider.MultiAction);
     method public void notifyDetailsChanged();
+    method public void notifyPlayStateChanged();
     method public void onBindRowActions();
+    method public void setSelectedMediaItemNumberView(int);
   }
 
   public abstract class AbstractMediaListHeaderPresenter extends android.support.v17.leanback.widget.RowPresenter {
@@ -2839,6 +2880,10 @@
     method public android.support.v17.leanback.widget.HorizontalGridView getGridView();
   }
 
+  public class MediaNowPlayingView extends android.widget.LinearLayout {
+    ctor public MediaNowPlayingView(android.content.Context, android.util.AttributeSet);
+  }
+
   public abstract interface MultiActionsProvider {
     method public abstract android.support.v17.leanback.widget.MultiActionsProvider.MultiAction[] getActions();
   }
@@ -4166,12 +4211,14 @@
     method public android.support.v4.app.NotificationCompat.Action.Builder extend(android.support.v4.app.NotificationCompat.Action.Builder);
     method public java.lang.CharSequence getCancelLabel();
     method public java.lang.CharSequence getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
     method public boolean getHintLaunchesActivity();
     method public java.lang.CharSequence getInProgressLabel();
     method public boolean isAvailableOffline();
     method public android.support.v4.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
     method public android.support.v4.app.NotificationCompat.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
     method public android.support.v4.app.NotificationCompat.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+    method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
     method public android.support.v4.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
     method public android.support.v4.app.NotificationCompat.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
   }
@@ -7315,6 +7362,7 @@
   }
 
   public final class TextViewCompat {
+    method public static android.graphics.drawable.Drawable[] getCompoundDrawablesRelative(android.widget.TextView);
     method public static int getMaxLines(android.widget.TextView);
     method public static int getMinLines(android.widget.TextView);
     method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
diff --git a/compat/api20/android/support/v4/app/NotificationCompatApi20.java b/compat/api20/android/support/v4/app/NotificationCompatApi20.java
index 036fb24..88ea703 100644
--- a/compat/api20/android/support/v4/app/NotificationCompatApi20.java
+++ b/compat/api20/android/support/v4/app/NotificationCompatApi20.java
@@ -126,7 +126,7 @@
         }
         actionExtras.putBoolean(NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES,
                 action.getAllowGeneratedReplies());
-        actionBuilder.addExtras(action.getExtras());
+        actionBuilder.addExtras(actionExtras);
         b.addAction(actionBuilder.build());
     }
 
diff --git a/compat/api24/android/support/v4/app/NotificationCompatApi24.java b/compat/api24/android/support/v4/app/NotificationCompatApi24.java
index e533b21..a631f23 100644
--- a/compat/api24/android/support/v4/app/NotificationCompatApi24.java
+++ b/compat/api24/android/support/v4/app/NotificationCompatApi24.java
@@ -119,9 +119,15 @@
                     actionBuilder.addRemoteInput(remoteInput);
                 }
             }
+            Bundle actionExtras;
             if (action.getExtras() != null) {
-                actionBuilder.addExtras(action.getExtras());
+                actionExtras = new Bundle(action.getExtras());
+            } else {
+                actionExtras = new Bundle();
             }
+            actionExtras.putBoolean(NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES,
+                    action.getAllowGeneratedReplies());
+            actionBuilder.addExtras(actionExtras);
             actionBuilder.setAllowGeneratedReplies(action.getAllowGeneratedReplies());
             b.addAction(actionBuilder.build());
         }
diff --git a/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java b/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
index eeadfab..9143214 100644
--- a/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
@@ -29,13 +29,23 @@
  */
 class SearchViewCompatHoneycomb {
 
+    public static void checkIfLegalArg(View searchView) {
+        if (searchView == null) {
+            throw new IllegalArgumentException("searchView must be non-null");
+        }
+        if (!(searchView instanceof SearchView)) {
+            throw new IllegalArgumentException("searchView must be an instance of" +
+                    "android.widget.SearchView");
+        }
+    }
+
     interface OnQueryTextListenerCompatBridge {
-        public boolean onQueryTextSubmit(String query);
-        public boolean onQueryTextChange(String newText);
+        boolean onQueryTextSubmit(String query);
+        boolean onQueryTextChange(String newText);
     }
 
     interface OnCloseListenerCompatBridge {
-        public boolean onClose();
+        boolean onClose();
     }
 
     public static View newSearchView(Context context) {
@@ -63,7 +73,7 @@
         };
     }
 
-    public static void setOnQueryTextListener(Object searchView, Object listener) {
+    public static void setOnQueryTextListener(View searchView, Object listener) {
         ((SearchView) searchView).setOnQueryTextListener((OnQueryTextListener) listener);
     }
 
@@ -76,7 +86,7 @@
         };
     }
 
-    public static void setOnCloseListener(Object searchView, Object listener) {
+    public static void setOnCloseListener(View searchView, Object listener) {
         ((SearchView) searchView).setOnCloseListener((OnCloseListener) listener);
     }
 
diff --git a/compat/java/android/support/v4/app/NotificationCompat.java b/compat/java/android/support/v4/app/NotificationCompat.java
index 0c515a1..c5a6628 100644
--- a/compat/java/android/support/v4/app/NotificationCompat.java
+++ b/compat/java/android/support/v4/app/NotificationCompat.java
@@ -2541,6 +2541,7 @@
             // Flags bitwise-ored to mFlags
             private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
             private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
+            private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
 
             // Default value for flags integer
             private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
@@ -2727,6 +2728,29 @@
             public boolean getHintLaunchesActivity() {
                 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
             }
+
+            /**
+             * Set a hint that this Action should be displayed inline.
+             *
+             * @param hintDisplayInline {@code true} if action should be displayed inline, false
+             *        otherwise
+             * @return this object for method chaining
+             */
+            public WearableExtender setHintDisplayActionInline(
+                    boolean hintDisplayInline) {
+                setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
+                return this;
+            }
+
+            /**
+             * Get a hint that this Action should be displayed inline.
+             *
+             * @return {@code true} if the Action should be displayed inline, {@code false}
+             *         otherwise. The default value is {@code false} if this was never set.
+             */
+            public boolean getHintDisplayActionInline() {
+                return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
+            }
         }
 
         /** @hide */
diff --git a/compat/java/android/support/v4/content/ContextCompat.java b/compat/java/android/support/v4/content/ContextCompat.java
index 3c6100e..58e2057 100644
--- a/compat/java/android/support/v4/content/ContextCompat.java
+++ b/compat/java/android/support/v4/content/ContextCompat.java
@@ -25,6 +25,9 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Process;
+import android.support.annotation.ColorInt;
+import android.support.annotation.ColorRes;
+import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.v4.os.BuildCompat;
 import android.support.v4.os.EnvironmentCompat;
@@ -328,7 +331,7 @@
      *           The value 0 is an invalid identifier.
      * @return Drawable An object that can be used to draw this resource.
      */
-    public static final Drawable getDrawable(Context context, int id) {
+    public static final Drawable getDrawable(Context context, @DrawableRes int id) {
         final int version = Build.VERSION.SDK_INT;
         if (version >= 21) {
             return ContextCompatApi21.getDrawable(context, id);
@@ -365,7 +368,7 @@
      * @throws android.content.res.Resources.NotFoundException if the given ID
      *         does not exist.
      */
-    public static final ColorStateList getColorStateList(Context context, int id) {
+    public static final ColorStateList getColorStateList(Context context, @ColorRes int id) {
         final int version = Build.VERSION.SDK_INT;
         if (version >= 23) {
             return ContextCompatApi23.getColorStateList(context, id);
@@ -387,7 +390,8 @@
      * @throws android.content.res.Resources.NotFoundException if the given ID
      *         does not exist.
      */
-    public static final int getColor(Context context, int id) {
+    @ColorInt
+    public static final int getColor(Context context, @ColorRes int id) {
         final int version = Build.VERSION.SDK_INT;
         if (version >= 23) {
             return ContextCompatApi23.getColor(context, id);
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
index 2232430..53f57fc 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
@@ -424,7 +424,7 @@
      * @return An instance.
      */
     public static AccessibilityWindowInfoCompat obtain(AccessibilityWindowInfoCompat info) {
-        return wrapNonNullInstance(IMPL.obtain(info.mInfo));
+        return info == null ? null : wrapNonNullInstance(IMPL.obtain(info.mInfo));
     }
 
     /**
diff --git a/compat/java/android/support/v4/widget/SearchViewCompat.java b/compat/java/android/support/v4/widget/SearchViewCompat.java
index 297ee84..48fbc5b 100644
--- a/compat/java/android/support/v4/widget/SearchViewCompat.java
+++ b/compat/java/android/support/v4/widget/SearchViewCompat.java
@@ -35,9 +35,9 @@
         void setImeOptions(View searchView, int imeOptions);
         void setInputType(View searchView, int inputType);
         Object newOnQueryTextListener(OnQueryTextListenerCompat listener);
-        void setOnQueryTextListener(Object searchView, Object listener);
+        void setOnQueryTextListener(View searchView, Object listener);
         Object newOnCloseListener(OnCloseListenerCompat listener);
-        void setOnCloseListener(Object searchView, Object listener);
+        void setOnCloseListener(View searchView, Object listener);
         CharSequence getQuery(View searchView);
         void setQuery(View searchView, CharSequence query, boolean submit);
         void setQueryHint(View searchView, CharSequence hint);
@@ -75,7 +75,7 @@
         }
 
         @Override
-        public void setOnQueryTextListener(Object searchView, Object listener) {
+        public void setOnQueryTextListener(View searchView, Object listener) {
         }
 
         @Override
@@ -84,7 +84,7 @@
         }
 
         @Override
-        public void setOnCloseListener(Object searchView, Object listener) {
+        public void setOnCloseListener(View searchView, Object listener) {
         }
 
         @Override
@@ -141,6 +141,7 @@
 
         @Override
         public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
+            checkIfLegalArg(searchView);
             SearchViewCompatHoneycomb.setSearchableInfo(searchView, searchableComponent);
         }
 
@@ -160,7 +161,8 @@
         }
 
         @Override
-        public void setOnQueryTextListener(Object searchView, Object listener) {
+        public void setOnQueryTextListener(View searchView, Object listener) {
+            checkIfLegalArg(searchView);
             SearchViewCompatHoneycomb.setOnQueryTextListener(searchView, listener);
         }
 
@@ -176,59 +178,74 @@
         }
 
         @Override
-        public void setOnCloseListener(Object searchView, Object listener) {
+        public void setOnCloseListener(View searchView, Object listener) {
+            checkIfLegalArg(searchView);
             SearchViewCompatHoneycomb.setOnCloseListener(searchView, listener);
         }
 
         @Override
         public CharSequence getQuery(View searchView) {
+            checkIfLegalArg(searchView);
             return SearchViewCompatHoneycomb.getQuery(searchView);
         }
 
         @Override
         public void setQuery(View searchView, CharSequence query, boolean submit) {
+            checkIfLegalArg(searchView);
             SearchViewCompatHoneycomb.setQuery(searchView, query, submit);
         }
 
         @Override
         public void setQueryHint(View searchView, CharSequence hint) {
+            checkIfLegalArg(searchView);
             SearchViewCompatHoneycomb.setQueryHint(searchView, hint);
         }
 
         @Override
         public void setIconified(View searchView, boolean iconify) {
+            checkIfLegalArg(searchView);
             SearchViewCompatHoneycomb.setIconified(searchView, iconify);
         }
 
         @Override
         public boolean isIconified(View searchView) {
+            checkIfLegalArg(searchView);
             return SearchViewCompatHoneycomb.isIconified(searchView);
         }
 
         @Override
         public void setSubmitButtonEnabled(View searchView, boolean enabled) {
+            checkIfLegalArg(searchView);
             SearchViewCompatHoneycomb.setSubmitButtonEnabled(searchView, enabled);
         }
 
         @Override
         public boolean isSubmitButtonEnabled(View searchView) {
+            checkIfLegalArg(searchView);
             return SearchViewCompatHoneycomb.isSubmitButtonEnabled(searchView);
         }
 
         @Override
         public void setQueryRefinementEnabled(View searchView, boolean enable) {
+            checkIfLegalArg(searchView);
             SearchViewCompatHoneycomb.setQueryRefinementEnabled(searchView, enable);
         }
 
         @Override
         public boolean isQueryRefinementEnabled(View searchView) {
+            checkIfLegalArg(searchView);
             return SearchViewCompatHoneycomb.isQueryRefinementEnabled(searchView);
         }
 
         @Override
         public void setMaxWidth(View searchView, int maxpixels) {
+            checkIfLegalArg(searchView);
             SearchViewCompatHoneycomb.setMaxWidth(searchView, maxpixels);
         }
+
+        protected void checkIfLegalArg(View searchView) {
+            SearchViewCompatHoneycomb.checkIfLegalArg(searchView);
+        }
     }
 
     static class SearchViewCompatIcsImpl extends SearchViewCompatHoneycombImpl {
@@ -240,11 +257,13 @@
 
         @Override
         public void setImeOptions(View searchView, int imeOptions) {
+            checkIfLegalArg(searchView);
             SearchViewCompatIcs.setImeOptions(searchView, imeOptions);
         }
 
         @Override
         public void setInputType(View searchView, int inputType) {
+            checkIfLegalArg(searchView);
             SearchViewCompatIcs.setInputType(searchView, inputType);
         }
     }
diff --git a/compat/java/android/support/v4/widget/TextViewCompat.java b/compat/java/android/support/v4/widget/TextViewCompat.java
index 3239587..d61ddc2 100644
--- a/compat/java/android/support/v4/widget/TextViewCompat.java
+++ b/compat/java/android/support/v4/widget/TextViewCompat.java
@@ -46,6 +46,7 @@
         int getMaxLines(TextView textView);
         int getMinLines(TextView textView);
         void setTextAppearance(@NonNull TextView textView, @StyleRes int resId);
+        Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView);
     }
 
     static class BaseTextViewCompatImpl implements TextViewCompatImpl {
@@ -84,6 +85,11 @@
         public void setTextAppearance(TextView textView, @StyleRes int resId) {
             TextViewCompatGingerbread.setTextAppearance(textView, resId);
         }
+
+        @Override
+        public Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
+            return TextViewCompatGingerbread.getCompoundDrawablesRelative(textView);
+        }
     }
 
     static class JbTextViewCompatImpl extends BaseTextViewCompatImpl {
@@ -121,6 +127,11 @@
             TextViewCompatJbMr1.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
                     start, top, end, bottom);
         }
+
+        @Override
+        public Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
+            return TextViewCompatJbMr1.getCompoundDrawablesRelative(textView);
+        }
     }
 
     static class JbMr2TextViewCompatImpl extends JbMr1TextViewCompatImpl {
@@ -268,4 +279,11 @@
     public static void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
         IMPL.setTextAppearance(textView, resId);
     }
+
+    /**
+     * Returns drawables for the start, top, end, and bottom borders from the given text view.
+     */
+    public static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
+        return textView.getCompoundDrawables();
+    }
 }
diff --git a/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java b/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
index 65056f5..faa0939 100644
--- a/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
+++ b/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.widget;
 
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
 import android.util.Log;
 import android.widget.TextView;
 
@@ -95,4 +97,8 @@
     static void setTextAppearance(TextView textView, int resId) {
         textView.setTextAppearance(textView.getContext(), resId);
     }
+
+    static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
+        return textView.getCompoundDrawables();
+    }
 }
diff --git a/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java b/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
index 7c073c6..fc088ed 100644
--- a/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
@@ -46,4 +46,8 @@
                 bottom);
     }
 
+    static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
+        return textView.getCompoundDrawablesRelative();
+    }
+
 }
diff --git a/customtabs/src/android/support/customtabs/CustomTabsIntent.java b/customtabs/src/android/support/customtabs/CustomTabsIntent.java
index cd26670..d2aabbe 100644
--- a/customtabs/src/android/support/customtabs/CustomTabsIntent.java
+++ b/customtabs/src/android/support/customtabs/CustomTabsIntent.java
@@ -207,6 +207,11 @@
     public static final String EXTRA_REMOTEVIEWS_CLICKED_ID =
             "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
 
+    /**
+     * Extra that specifies whether Instant Apps is enabled.
+     */
+    public static final String EXTRA_ENABLE_INSTANT_APPS =
+            "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
 
     /**
      * Key that specifies the unique ID for an action button. To make a button to show on the
@@ -257,6 +262,7 @@
         private ArrayList<Bundle> mMenuItems = null;
         private Bundle mStartAnimationBundle = null;
         private ArrayList<Bundle> mActionButtons = null;
+        private boolean mInstantAppsEnabled = true;
 
         /**
          * Creates a {@link CustomTabsIntent.Builder} object associated with no
@@ -452,6 +458,16 @@
         }
 
         /**
+         * Sets whether Instant Apps is enabled for this Custom Tab.
+
+         * @param enabled Whether Instant Apps should be enabled.
+         */
+        public Builder setInstantAppsEnabled(boolean enabled) {
+            mInstantAppsEnabled = enabled;
+            return this;
+        }
+
+        /**
          * Sets the start animations.
          *
          * @param context Application context.
@@ -491,6 +507,7 @@
             if (mActionButtons != null) {
                 mIntent.putParcelableArrayListExtra(EXTRA_TOOLBAR_ITEMS, mActionButtons);
             }
+            mIntent.putExtra(EXTRA_ENABLE_INSTANT_APPS, mInstantAppsEnabled);
             return new CustomTabsIntent(mIntent, mStartAnimationBundle);
         }
     }
diff --git a/design/res-public/values/public_styles.xml b/design/res-public/values/public_styles.xml
index a7c0af6..0dcde45 100644
--- a/design/res-public/values/public_styles.xml
+++ b/design/res-public/values/public_styles.xml
@@ -24,6 +24,7 @@
     <public type="style" name="TextAppearance.Design.Snackbar.Message"/>
     <public type="style" name="TextAppearance.Design.Tab"/>
     <public type="style" name="Theme.Design"/>
+    <public type="style" name="Theme.Design.BottomNavigationView"/>
     <public type="style" name="Theme.Design.BottomSheetDialog"/>
     <public type="style" name="Theme.Design.Light"/>
     <public type="style" name="Theme.Design.Light.BottomSheetDialog"/>
diff --git a/design/res/color-v23/design_tint_password_toggle.xml b/design/res/color-v23/design_tint_password_toggle.xml
new file mode 100644
index 0000000..3a09d32
--- /dev/null
+++ b/design/res/color-v23/design_tint_password_toggle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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_checked="true" android:color="?android:attr/colorForeground" android:alpha="0.54"/>
+    <item android:color="?android:attr/colorForeground" android:alpha="0.38"/>
+</selector>
\ No newline at end of file
diff --git a/design/res/color/design_tint_password_toggle.xml b/design/res/color/design_tint_password_toggle.xml
new file mode 100644
index 0000000..15016e4
--- /dev/null
+++ b/design/res/color/design_tint_password_toggle.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:state_checked="true" android:color="?android:attr/colorForeground" app:alpha="0.54"/>
+    <item android:color="?android:attr/colorForeground" app:alpha="0.38"/>
+</selector>
\ No newline at end of file
diff --git a/design/res/drawable-v21/design_bottom_navigation_item_background.xml b/design/res/drawable-v21/design_bottom_navigation_item_background.xml
new file mode 100644
index 0000000..f30f08b
--- /dev/null
+++ b/design/res/drawable-v21/design_bottom_navigation_item_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?attr/colorPrimary" />
\ No newline at end of file
diff --git a/design/res/drawable/design_bottom_navigation_item_background.xml b/design/res/drawable/design_bottom_navigation_item_background.xml
new file mode 100644
index 0000000..7674f42
--- /dev/null
+++ b/design/res/drawable/design_bottom_navigation_item_background.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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_pressed="true">
+        <shape android:shape="rectangle">
+            <solid android:color="#ff0000"/>
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="#ffffff"/>
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/design/res/drawable/design_ic_visibility.xml b/design/res/drawable/design_ic_visibility.xml
new file mode 100644
index 0000000..91d46ff
--- /dev/null
+++ b/design/res/drawable/design_ic_visibility.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/black"
+        android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
\ No newline at end of file
diff --git a/design/res/drawable/navigation_empty_icon.xml b/design/res/drawable/navigation_empty_icon.xml
new file mode 100644
index 0000000..6cd938d
--- /dev/null
+++ b/design/res/drawable/navigation_empty_icon.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <size
+        android:width="@dimen/design_navigation_icon_size"
+        android:height="@dimen/design_navigation_icon_size" />
+</shape>
diff --git a/design/res/layout/design_bottom_navigation_item.xml b/design/res/layout/design_bottom_navigation_item.xml
new file mode 100644
index 0000000..7f93eba
--- /dev/null
+++ b/design/res/layout/design_bottom_navigation_item.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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">
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:duplicateParentState="true" />
+    <TextView
+        android:id="@+id/label"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/design_bottom_navigation_text_size"
+        android:gravity="center"
+        android:duplicateParentState="true" />
+</merge>
\ No newline at end of file
diff --git a/design/res/layout/design_text_input_password_icon.xml b/design/res/layout/design_text_input_password_icon.xml
new file mode 100644
index 0000000..0dbb9fa
--- /dev/null
+++ b/design/res/layout/design_text_input_password_icon.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<android.support.design.widget.CheckableImageButton
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/text_input_password_toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:layout_gravity="center_vertical|end|right"/>
\ No newline at end of file
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index fc233da..f132d7a 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -187,25 +187,50 @@
 
     <declare-styleable name="TextInputLayout">
         <attr name="hintTextAppearance" format="reference"/>
-        <!-- The hint to display in the floating label -->
+        <!-- The hint to display in the floating label. -->
         <attr name="android:hint"/>
-        <!-- Whether the layout's floating label functionality is enabled -->
+        <!-- Whether the layout's floating label functionality is enabled. -->
         <attr name="hintEnabled" format="boolean"/>
-        <!-- Whether the layout is laid out as if an error will be displayed -->
+        <!-- Whether the layout is laid out as if an error will be displayed. -->
         <attr name="errorEnabled" format="boolean"/>
-        <!-- TextAppearance of any error message displayed -->
+        <!-- TextAppearance of any error message displayed. -->
         <attr name="errorTextAppearance" format="reference"/>
-        <!-- Whether the layout is laid out as if the character counter will be displayed -->
+        <!-- Whether the layout is laid out as if the character counter will be displayed. -->
         <attr name="counterEnabled" format="boolean"/>
-        <!-- The max length to display in the character counter -->
+        <!-- The max length to display in the character counter. -->
         <attr name="counterMaxLength" format="integer" />
-        <!-- TextAppearance of the character counter -->
+        <!-- TextAppearance of the character counter. -->
         <attr name="counterTextAppearance" format="reference"/>
-        <!-- TextAppearance of the character counter when the text is longer than the max -->
+        <!-- TextAppearance of the character counter when the text is longer than the max. -->
         <attr name="counterOverflowTextAppearance" format="reference"/>
         <attr name="android:textColorHint"/>
         <!-- Whether to animate hint state changes. -->
         <attr name="hintAnimationEnabled" format="boolean"/>
+        <!-- Whether the view will display a toggle when the EditText has a password. -->
+        <attr name="passwordToggleEnabled" format="boolean"/>
+        <!-- Drawable to use as the password input visibility toggle icon. -->
+        <attr name="passwordToggleDrawable" format="reference"/>
+        <!-- Text to set as the content description for the password input visibility toggle. -->
+        <attr name="passwordToggleContentDescription" format="string"/>
+        <!-- Icon to use for the password input visibility toggle -->
+        <attr name="passwordToggleTint" format="color"/>
+        <!-- Blending mode used to apply the background tint. -->
+        <attr name="passwordToggleTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="SnackbarLayout">
@@ -397,5 +422,12 @@
         <attr name="textColorError" format="color" />
     </declare-styleable>
 
-</resources>
+    <declare-styleable name="BottomNavigationView">
+        <!-- The menu resource to inflate and populate items from. -->
+        <attr name="menu"/>
+        <attr name="itemIconTint"/>
+        <attr name="itemTextColor"/>
+        <attr name="itemBackground"/>
+    </declare-styleable>
 
+</resources>
diff --git a/design/res/values/dimens.xml b/design/res/values/dimens.xml
index 604ac52..6439b57 100644
--- a/design/res/values/dimens.xml
+++ b/design/res/values/dimens.xml
@@ -58,4 +58,14 @@
     <dimen name="design_bottom_sheet_modal_elevation">16dp</dimen>
     <dimen name="design_bottom_sheet_modal_peek_height">256dp</dimen>
 
+    <dimen name="design_bottom_navigation_height">56dp</dimen>
+    <dimen name="design_bottom_navigation_text_size">12sp</dimen>
+    <dimen name="design_bottom_navigation_active_text_size">14sp</dimen>
+    <dimen name="design_bottom_navigation_top_padding">8dp</dimen>
+    <dimen name="design_bottom_navigation_active_top_padding">6dp</dimen>
+    <dimen name="design_bottom_navigation_horizontal_padding">12dp</dimen>
+    <dimen name="design_bottom_navigation_bottom_padding">8dp</dimen>
+    <dimen name="design_bottom_navigation_item_max_width">96dp</dimen>
+    <dimen name="design_bottom_navigation_active_item_max_width">168dp</dimen>
+
 </resources>
diff --git a/design/res/values/styles.xml b/design/res/values/styles.xml
index b25646c..c5c6051 100644
--- a/design/res/values/styles.xml
+++ b/design/res/values/styles.xml
@@ -42,6 +42,10 @@
         <item name="tabMode">fixed</item>
     </style>
 
+    <style name="Widget.Design.BottomNavigationView" parent="">
+        <item name="itemBackground">?attr/selectableItemBackgroundBorderless</item>
+    </style>
+
     <style name="Base.Widget.Design.TabLayout" parent="android:Widget">
         <item name="tabMaxWidth">@dimen/design_tab_max_width</item>
         <item name="tabIndicatorColor">?attr/colorAccent</item>
@@ -64,6 +68,8 @@
         <item name="errorTextAppearance">@style/TextAppearance.Design.Error</item>
         <item name="counterTextAppearance">@style/TextAppearance.Design.Counter</item>
         <item name="counterOverflowTextAppearance">@style/TextAppearance.Design.Counter.Overflow</item>
+        <item name="passwordToggleDrawable">@drawable/design_ic_visibility</item>
+        <item name="passwordToggleTint">@color/design_tint_password_toggle</item>
     </style>
 
     <style name="TextAppearance.Design.Hint" parent="TextAppearance.AppCompat.Caption">
diff --git a/design/src/android/support/design/internal/BottomNavigationItemView.java b/design/src/android/support/design/internal/BottomNavigationItemView.java
new file mode 100644
index 0000000..0d7df1a
--- /dev/null
+++ b/design/src/android/support/design/internal/BottomNavigationItemView.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.design.R;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v4.view.animation.LinearOutSlowInInterpolator;
+import android.support.v7.view.menu.MenuItemImpl;
+import android.support.v7.view.menu.MenuView;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * @hide
+ */
+public class BottomNavigationItemView extends ForegroundLinearLayout implements MenuView.ItemView {
+    public static final int INVALID_ITEM_POSTION = -1;
+
+    private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
+    private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;
+
+    private final int mHorizontalPadding;
+    private final int mBottomPadding;
+    private final int mTopPadding;
+    private final int mActiveTopPadding;
+    private final float mInactiveLabelSize;
+    private final float mActiveLabelSize;
+
+    private ImageView mIcon;
+    private TextView mLabel;
+    private int mItemPosition = INVALID_ITEM_POSTION;
+
+    private MenuItemImpl mItemData;
+
+    private ColorStateList mIconTint;
+    private ColorStateList mTextColor;
+
+    public BottomNavigationItemView(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public BottomNavigationItemView(@NonNull Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public BottomNavigationItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mHorizontalPadding = getResources().getDimensionPixelSize(
+                R.dimen.design_bottom_navigation_horizontal_padding);
+        mBottomPadding = getResources().getDimensionPixelSize(
+                R.dimen.design_bottom_navigation_bottom_padding);
+        mTopPadding = getResources().getDimensionPixelSize(
+                R.dimen.design_bottom_navigation_top_padding);
+        mActiveTopPadding = getResources().getDimensionPixelSize(
+                R.dimen.design_bottom_navigation_active_top_padding);
+        mInactiveLabelSize =
+                getResources().getDimension(R.dimen.design_bottom_navigation_text_size);
+        mActiveLabelSize =
+                getResources().getDimension(R.dimen.design_bottom_navigation_active_text_size);
+
+        setOrientation(VERTICAL);
+        setGravity(Gravity.CENTER);
+        LayoutInflater.from(context).inflate(R.layout.design_bottom_navigation_item, this, true);
+        setBackgroundResource(R.drawable.design_bottom_navigation_item_background);
+        mIcon = (ImageView) findViewById(R.id.icon);
+        mLabel = (TextView) findViewById(R.id.label);
+    }
+
+    @Override
+    public void initialize(MenuItemImpl itemData, int menuType) {
+        mItemData = itemData;
+        setCheckable(itemData.isCheckable());
+        setChecked(itemData.isChecked());
+        setEnabled(itemData.isEnabled());
+        setIcon(itemData.getIcon());
+        setTitle(itemData.getTitle());
+    }
+
+    public void setItemPosition(int position) {
+        mItemPosition = position;
+    }
+
+    public int getItemPosition() {
+        return mItemPosition;
+    }
+
+    @Override
+    public MenuItemImpl getItemData() {
+        return null;
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mLabel.setText(title);
+    }
+
+    @Override
+    public void setCheckable(boolean checkable) {
+        refreshDrawableState();
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        mItemData.setChecked(checked);
+        if (checked) {
+            mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mActiveLabelSize);
+            setPadding(mHorizontalPadding, mActiveTopPadding, mHorizontalPadding, mBottomPadding);
+        } else {
+            mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mInactiveLabelSize);
+            setPadding(mHorizontalPadding, mTopPadding, mHorizontalPadding, mBottomPadding);
+        }
+        refreshDrawableState();
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        mLabel.setEnabled(enabled);
+        mIcon.setEnabled(enabled);
+    }
+
+    @Override
+    public int[] onCreateDrawableState(final int extraSpace) {
+        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+        if (mItemData != null && mItemData.isCheckable() && mItemData.isChecked()) {
+            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
+        }
+        return drawableState;
+    }
+
+    @Override
+    public void setShortcut(boolean showShortcut, char shortcutKey) {
+    }
+
+    @Override
+    public void setIcon(Drawable icon) {
+        if (icon != null) {
+            Drawable.ConstantState state = icon.getConstantState();
+            icon = DrawableCompat.wrap(state == null ? icon : state.newDrawable()).mutate();
+            DrawableCompat.setTintList(icon, mIconTint);
+        }
+        mIcon.setImageDrawable(icon);
+    }
+
+    @Override
+    public boolean prefersCondensedTitle() {
+        return false;
+    }
+
+    @Override
+    public boolean showsIcon() {
+        return true;
+    }
+
+    public void setIconTintList(ColorStateList tint) {
+        mIconTint = tint;
+        if (mItemData != null) {
+            // Update the icon so that the tint takes effect
+            setIcon(mItemData.getIcon());
+        }
+        mLabel.setTextColor(mIconTint);
+    }
+
+    public void setTextColor(ColorStateList color) {
+        mTextColor = color;
+        mLabel.setTextColor(color);
+    }
+
+    public void setItemBackground(int background) {
+        Drawable backgroundDrawable = background == 0
+                ? null : ContextCompat.getDrawable(getContext(), background);
+        setBackgroundDrawable(backgroundDrawable);
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    public Animator getAnimator(boolean active) {
+        final float currentTextSize = mLabel.getTextSize();
+        final int currentTopPadding = getPaddingTop();
+
+        final float finalTextSize = active ? mActiveLabelSize : mInactiveLabelSize;
+        final int finalTopPadding = active ? mActiveTopPadding : mTopPadding;
+
+        if (currentTextSize == finalTextSize && currentTopPadding == finalTopPadding) {
+            return null;
+        }
+
+        // Grow or shrink the text of the tab.
+        ValueAnimator textAnimator = ValueAnimator.ofFloat(currentTextSize, finalTextSize);
+        textAnimator.setDuration(ACTIVE_ANIMATION_DURATION_MS);
+        textAnimator.setInterpolator(new LinearOutSlowInInterpolator());
+        textAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                float animatedValue = (float) valueAnimator.getAnimatedValue();
+                mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, animatedValue);
+            }
+        });
+
+        // Reduce or increase the padding top of the tab.
+        ValueAnimator paddingTopAnimator = ValueAnimator.ofInt(currentTopPadding, finalTopPadding);
+        paddingTopAnimator.setDuration(ACTIVE_ANIMATION_DURATION_MS);
+        paddingTopAnimator.setInterpolator(new LinearOutSlowInInterpolator());
+        paddingTopAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                int animatedValue = (int) valueAnimator.getAnimatedValue();
+                setPadding(mHorizontalPadding, animatedValue,
+                        mHorizontalPadding, mBottomPadding);
+            }
+        });
+
+        AnimatorSet set = new AnimatorSet();
+        set.playTogether(textAnimator, paddingTopAnimator);
+        return set;
+    }
+}
diff --git a/design/src/android/support/design/internal/BottomNavigationMenuView.java b/design/src/android/support/design/internal/BottomNavigationMenuView.java
new file mode 100644
index 0000000..a4df28f
--- /dev/null
+++ b/design/src/android/support/design/internal/BottomNavigationMenuView.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.animation.AnimatorSet;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.support.design.R;
+import android.support.v4.util.Pools;
+import android.support.v7.view.menu.MenuBuilder;
+import android.support.v7.view.menu.MenuItemImpl;
+import android.support.v7.view.menu.MenuView;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * @hide
+ */
+public class BottomNavigationMenuView extends LinearLayout implements MenuView {
+    private final int mInactiveItemMaxWidth;
+    private final int mActiveItemMaxWidth;
+    private final OnClickListener mOnClickListener;
+    private static final Pools.Pool<BottomNavigationItemView> sItemPool =
+            new Pools.SynchronizedPool<>(5);
+
+    private BottomNavigationItemView[] mButtons;
+    private int mActiveButton = 0;
+    private ColorStateList mItemIconTint;
+    private ColorStateList mItemTextColor;
+    private int mItemBackgroundRes;
+
+    private BottomNavigationPresenter mPresenter;
+    private MenuBuilder mMenu;
+
+    public BottomNavigationMenuView(Context context) {
+        this(context, null);
+    }
+
+    public BottomNavigationMenuView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public BottomNavigationMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        setGravity(Gravity.CENTER);
+        setOrientation(HORIZONTAL);
+
+        mInactiveItemMaxWidth = getResources().getDimensionPixelSize(
+                R.dimen.design_bottom_navigation_item_max_width);
+        mActiveItemMaxWidth = getResources()
+                .getDimensionPixelSize(R.dimen.design_bottom_navigation_active_item_max_width);
+
+        mOnClickListener = new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                final int itemPosition = ((BottomNavigationItemView) v).getItemPosition();
+                activateNewButton(itemPosition);
+            }
+        };
+    }
+
+    @Override
+    public void initialize(MenuBuilder menu) {
+        mMenu = menu;
+        if (mMenu == null) return;
+        if (mMenu.size() > mActiveButton) {
+            mMenu.getItem(mActiveButton).setChecked(true);
+        }
+    }
+
+    @Override
+    public int getWindowAnimations() {
+        return 0;
+    }
+
+    public void setIconTintList(ColorStateList color) {
+        mItemIconTint = color;
+        if (mButtons == null) return;
+        for (BottomNavigationItemView item : mButtons) {
+            item.setIconTintList(color);
+        }
+    }
+
+    @Nullable
+    public ColorStateList getIconTintList() {
+        return mItemIconTint;
+    }
+
+    public void setItemTextColor(ColorStateList color) {
+        mItemTextColor = color;
+        if (mButtons == null) return;
+        for (BottomNavigationItemView item : mButtons) {
+            item.setTextColor(color);
+        }
+    }
+
+    public ColorStateList getItemTextColor() {
+        return mItemTextColor;
+    }
+
+    public void setItemBackgroundRes(int background) {
+        mItemBackgroundRes = background;
+        if (mButtons == null) return;
+        for (BottomNavigationItemView item : mButtons) {
+            item.setItemBackground(background);
+        }
+    }
+
+    public int getItemBackgroundRes() {
+        return mItemBackgroundRes;
+    }
+
+    public void setPresenter(BottomNavigationPresenter presenter) {
+        mPresenter = presenter;
+    }
+
+    public void buildMenuView() {
+        if (mButtons != null) {
+            for (BottomNavigationItemView item : mButtons) {
+                sItemPool.release(item);
+            }
+        }
+        removeAllViews();
+        mButtons = new BottomNavigationItemView[mMenu.size()];
+        for (int i = 0; i < mMenu.size(); i++) {
+            mPresenter.setUpdateSuspended(true);
+            mMenu.getItem(i).setCheckable(true);
+            mPresenter.setUpdateSuspended(false);
+            BottomNavigationItemView child = getNewItem();
+            mButtons[i] = child;
+            child.setIconTintList(mItemIconTint);
+            child.setTextColor(mItemTextColor);
+            child.setItemBackground(mItemBackgroundRes);
+            child.initialize((MenuItemImpl) mMenu.getItem(i), 0);
+            child.setItemPosition(i);
+            child.setOnClickListener(mOnClickListener);
+            addView(child);
+        }
+    }
+
+    public void updateMenuView() {
+        final int menuSize = mMenu.size();
+        if (menuSize != mButtons.length) {
+            // The size has changed. Rebuild menu view from scratch.
+            buildMenuView();
+            return;
+        }
+        for (int i = 0; i < menuSize; i++) {
+            mPresenter.setUpdateSuspended(true);
+            mButtons[i].initialize((MenuItemImpl) mMenu.getItem(i), 0);
+            mPresenter.setUpdateSuspended(false);
+        }
+    }
+
+    private void activateNewButton(int newButton) {
+        if (mActiveButton == newButton) return;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            AnimatorSet animatorSet = new AnimatorSet();
+            animatorSet.playTogether(
+                    mButtons[mActiveButton].getAnimator(false),
+                    mButtons[newButton].getAnimator(true));
+            animatorSet.start();
+        }
+        mPresenter.setUpdateSuspended(true);
+        mButtons[mActiveButton].setChecked(false);
+        mButtons[newButton].setChecked(true);
+        mPresenter.setUpdateSuspended(false);
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+            // Manually force UI update since we cannot use animations.
+            mPresenter.updateMenuView(true);
+        }
+        mActiveButton = newButton;
+    }
+
+    public void updateOnSizeChange(int width) {
+        if (getChildCount() == 0) return;
+        int available = width / getChildCount();
+        int itemWidth = Math.min(available, mActiveItemMaxWidth);
+
+        for (int i = 0; i < mButtons.length; i++) {
+            ViewGroup.LayoutParams params = mButtons[i].getLayoutParams();
+            if (params.width == itemWidth) {
+                continue;
+            }
+            params.width = itemWidth;
+            params.height = ViewGroup.LayoutParams.MATCH_PARENT;
+            mButtons[i].setLayoutParams(params);
+        }
+    }
+
+    private BottomNavigationItemView getNewItem() {
+        BottomNavigationItemView item = sItemPool.acquire();
+        if (item == null) {
+            item = new BottomNavigationItemView(getContext());
+        }
+        return item;
+    }
+}
diff --git a/design/src/android/support/design/internal/BottomNavigationPresenter.java b/design/src/android/support/design/internal/BottomNavigationPresenter.java
new file mode 100644
index 0000000..8dc0549
--- /dev/null
+++ b/design/src/android/support/design/internal/BottomNavigationPresenter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.internal;
+
+import android.content.Context;
+import android.os.Parcelable;
+import android.support.v7.view.menu.MenuBuilder;
+import android.support.v7.view.menu.MenuItemImpl;
+import android.support.v7.view.menu.MenuPresenter;
+import android.support.v7.view.menu.MenuView;
+import android.support.v7.view.menu.SubMenuBuilder;
+import android.view.ViewGroup;
+
+/**
+ * @hide
+ */
+public class BottomNavigationPresenter implements MenuPresenter {
+    private MenuBuilder mMenu;
+    private BottomNavigationMenuView mMenuView;
+    private boolean mUpdateSuspended = false;
+
+    public void setBottomNavigationMenuView(BottomNavigationMenuView menuView) {
+        mMenuView = menuView;
+    }
+
+    @Override
+    public void initForMenu(Context context, MenuBuilder menu) {
+        mMenuView.initialize(mMenu);
+        mMenu = menu;
+    }
+
+    @Override
+    public MenuView getMenuView(ViewGroup root) {
+        return mMenuView;
+    }
+
+    @Override
+    public void updateMenuView(boolean cleared) {
+        if (mUpdateSuspended) return;
+        if (cleared) {
+            mMenuView.buildMenuView();
+        } else {
+            mMenuView.updateMenuView();
+        }
+    }
+
+    @Override
+    public void setCallback(Callback cb) {}
+
+    @Override
+    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+        return false;
+    }
+
+    @Override
+    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {}
+
+    @Override
+    public boolean flagActionItems() {
+        return false;
+    }
+
+    @Override
+    public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
+        return false;
+    }
+
+    @Override
+    public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
+        return false;
+    }
+
+    @Override
+    public int getId() {
+        return -1;
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        return null;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {}
+
+    public void setUpdateSuspended(boolean updateSuspended) {
+        mUpdateSuspended = updateSuspended;
+    }
+}
diff --git a/design/src/android/support/design/internal/NavigationMenuItemView.java b/design/src/android/support/design/internal/NavigationMenuItemView.java
index 1e2c928..1f70a04 100644
--- a/design/src/android/support/design/internal/NavigationMenuItemView.java
+++ b/design/src/android/support/design/internal/NavigationMenuItemView.java
@@ -23,6 +23,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.StateListDrawable;
 import android.support.design.R;
+import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.widget.TextViewCompat;
 import android.support.v7.view.menu.MenuItemImpl;
@@ -44,6 +45,8 @@
 
     private final int mIconSize;
 
+    private boolean mNeedsEmptyIcon;
+
     private final CheckedTextView mTextView;
 
     private FrameLayout mActionArea;
@@ -52,6 +55,8 @@
 
     private ColorStateList mIconTintList;
 
+    private Drawable mEmptyDrawable;
+
     public NavigationMenuItemView(Context context) {
         this(context, null);
     }
@@ -86,6 +91,32 @@
         setTitle(itemData.getTitle());
         setIcon(itemData.getIcon());
         setActionView(itemData.getActionView());
+        adjustAppearance();
+    }
+
+    private boolean shouldExpandActionArea() {
+        return mItemData.getTitle() == null &&
+                mItemData.getIcon() == null &&
+                mItemData.getActionView() != null;
+    }
+
+    private void adjustAppearance() {
+        if (shouldExpandActionArea()) {
+            // Expand the actionView area
+            mTextView.setVisibility(View.GONE);
+            if (mActionArea != null) {
+                LayoutParams params = (LayoutParams) mActionArea.getLayoutParams();
+                params.width = LayoutParams.MATCH_PARENT;
+                mActionArea.setLayoutParams(params);
+            }
+        } else {
+            mTextView.setVisibility(View.VISIBLE);
+            if (mActionArea != null) {
+                LayoutParams params = (LayoutParams) mActionArea.getLayoutParams();
+                params.width = LayoutParams.WRAP_CONTENT;
+                mActionArea.setLayoutParams(params);
+            }
+        }
     }
 
     public void recycle() {
@@ -96,12 +127,12 @@
     }
 
     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) {
+            if (mActionArea == null) {
+                mActionArea = (FrameLayout) ((ViewStub) findViewById(
+                        R.id.design_menu_item_action_area_stub)).inflate();
+            }
+            mActionArea.removeAllViews();
             mActionArea.addView(actionView);
         }
     }
@@ -150,6 +181,15 @@
             icon = DrawableCompat.wrap(state == null ? icon : state.newDrawable()).mutate();
             icon.setBounds(0, 0, mIconSize, mIconSize);
             DrawableCompat.setTintList(icon, mIconTintList);
+        } else if (mNeedsEmptyIcon) {
+            if (mEmptyDrawable == null) {
+                mEmptyDrawable = ResourcesCompat.getDrawable(getResources(),
+                        R.drawable.navigation_empty_icon, getContext().getTheme());
+                if (mEmptyDrawable != null) {
+                    mEmptyDrawable.setBounds(0, 0, mIconSize, mIconSize);
+                }
+            }
+            icon = mEmptyDrawable;
         }
         TextViewCompat.setCompoundDrawablesRelative(mTextView, icon, null, null, null);
     }
@@ -189,4 +229,8 @@
         mTextView.setTextColor(colors);
     }
 
+    public void setNeedsEmptyIcon(boolean needsEmptyIcon) {
+        mNeedsEmptyIcon = needsEmptyIcon;
+    }
+
 }
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
index afdef82..ec998c2 100644
--- a/design/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -19,8 +19,6 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -39,7 +37,6 @@
 import android.support.v7.widget.RecyclerView;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
-import android.view.MenuItem;
 import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewGroup;
@@ -341,7 +338,6 @@
 
         private final ArrayList<NavigationMenuItem> mItems = new ArrayList<>();
         private MenuItemImpl mCheckedItem;
-        private ColorDrawable mTransparentIcon;
         private boolean mUpdateSuspended;
 
         NavigationMenuAdapter() {
@@ -406,6 +402,7 @@
                     itemView.setBackgroundDrawable(mItemBackground != null ?
                             mItemBackground.getConstantState().newDrawable() : null);
                     NavigationMenuTextItem item = (NavigationMenuTextItem) mItems.get(position);
+                    itemView.setNeedsEmptyIcon(item.needsEmptyIcon);
                     itemView.initialize(item.getMenuItem(), 0);
                     break;
                 }
@@ -506,10 +503,9 @@
                         currentGroupHasIcon = true;
                         appendTransparentIconIfMissing(currentGroupStart, mItems.size());
                     }
-                    if (currentGroupHasIcon && item.getIcon() == null) {
-                        item.setIcon(android.R.color.transparent);
-                    }
-                    mItems.add(new NavigationMenuTextItem(item));
+                    NavigationMenuTextItem textItem = new NavigationMenuTextItem(item);
+                    textItem.needsEmptyIcon = currentGroupHasIcon;
+                    mItems.add(textItem);
                     currentGroupId = groupId;
                 }
             }
@@ -519,13 +515,7 @@
         private void appendTransparentIconIfMissing(int startIndex, int endIndex) {
             for (int i = startIndex; i < endIndex; i++) {
                 NavigationMenuTextItem textItem = (NavigationMenuTextItem) mItems.get(i);
-                MenuItem item = textItem.getMenuItem();
-                if (item.getIcon() == null) {
-                    if (mTransparentIcon == null) {
-                        mTransparentIcon = new ColorDrawable(Color.TRANSPARENT);
-                    }
-                    item.setIcon(mTransparentIcon);
-                }
+                textItem.needsEmptyIcon = true;
             }
         }
 
@@ -611,6 +601,8 @@
 
         private final MenuItemImpl mMenuItem;
 
+        boolean needsEmptyIcon;
+
         private NavigationMenuTextItem(MenuItemImpl item) {
             mMenuItem = item;
         }
diff --git a/design/src/android/support/design/widget/BottomNavigationView.java b/design/src/android/support/design/widget/BottomNavigationView.java
new file mode 100644
index 0000000..1ccb6ff
--- /dev/null
+++ b/design/src/android/support/design/widget/BottomNavigationView.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.R;
+import android.support.design.internal.BottomNavigationMenuView;
+import android.support.design.internal.BottomNavigationPresenter;
+import android.support.v7.content.res.AppCompatResources;
+import android.support.v7.view.SupportMenuInflater;
+import android.support.v7.view.menu.MenuBuilder;
+import android.support.v7.widget.TintTypedArray;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+/**
+ * Represents a standard bottom navigation bar for application. It is an implementation of material
+ * design bottom navigation. See https://material.google.com/components/bottom-navigation.html
+ *
+ * Bottom navigation bars make it easy for users to explore and switch between top-level views in
+ * a single tap. It should be used when application has three to five top-level destinations.
+ *
+ * The bar contents can be populated by specifying a menu resource file. Each menu item title, icon
+ * and enabled state will be used for displaying bottom navigation bar items.
+ *
+ * &lt;android.support.design.widget.BottomNavigationView
+ *     xmlns:android="http://schemas.android.com/apk/res/android"
+ *     xmlns:app="http://schemas.android.com/apk/res-auto"
+ *     android:id="@+id/navigation"
+ *     android:layout_width="wrap_content"
+ *     android:layout_height="match_parent"
+ *     android:layout_gravity="start"
+ *     app:menu="@menu/my_navigation_items" /&gt;
+ */
+public class BottomNavigationView extends FrameLayout {
+
+    private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
+    private static final int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled};
+
+    private final MenuBuilder mMenu;
+    private final BottomNavigationMenuView mMenuView;
+    private final BottomNavigationPresenter mPresenter = new BottomNavigationPresenter();
+    private MenuInflater mMenuInflater;
+
+    private OnNavigationItemSelectedListener mListener;
+
+    public BottomNavigationView(Context context) {
+        this(context, null);
+    }
+
+    public BottomNavigationView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public BottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        ThemeUtils.checkAppCompatTheme(context);
+
+        // Create the menu
+        mMenu = new MenuBuilder(context);
+
+        mMenuView = new BottomNavigationMenuView(context, attrs);
+        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+        mMenuView.setLayoutParams(params);
+
+        mPresenter.setBottomNavigationMenuView(mMenuView);
+        mMenuView.setPresenter(mPresenter);
+        mMenu.addMenuPresenter(mPresenter);
+
+
+        // Custom attributes
+        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
+                R.styleable.BottomNavigationView, defStyleAttr,
+                R.style.Widget_Design_BottomNavigationView);
+
+        if (a.hasValue(R.styleable.BottomNavigationView_itemIconTint)) {
+            mMenuView.setIconTintList(
+                    a.getColorStateList(R.styleable.BottomNavigationView_itemIconTint));
+        } else {
+            mMenuView.setIconTintList(
+                    createDefaultColorStateList(android.R.attr.textColorSecondary));
+        }
+        if (a.hasValue(R.styleable.BottomNavigationView_itemTextColor)) {
+            mMenuView.setItemTextColor(
+                    a.getColorStateList(R.styleable.BottomNavigationView_itemTextColor));
+        } else {
+            mMenuView.setItemTextColor(
+                    createDefaultColorStateList(android.R.attr.textColorSecondary));
+        }
+
+        int itemBackground = a.getResourceId(R.styleable.BottomNavigationView_itemBackground, 0);
+        mMenuView.setItemBackgroundRes(itemBackground);
+
+        if (a.hasValue(R.styleable.BottomNavigationView_menu)) {
+            inflateMenu(a.getResourceId(R.styleable.BottomNavigationView_menu, 0));
+        }
+        a.recycle();
+
+        addView(mMenuView);
+
+        mMenu.setCallback(new MenuBuilder.Callback() {
+            @Override
+            public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+                return mListener != null && mListener.onNavigationItemSelected(item);
+            }
+
+            @Override
+            public void onMenuModeChange(MenuBuilder menu) {}
+        });
+    }
+
+    /**
+     * Set a listener that will be notified when a bottom navigation item is selected.
+     *
+     * @param listener The listener to notify
+     */
+    public void setOnNavigationItemSelectedListener(
+            @Nullable OnNavigationItemSelectedListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // TODO(aurimas): move updateOnSizeChange to a different location that is less expensive.
+        mMenuView.updateOnSizeChange(MeasureSpec.getSize(widthMeasureSpec));
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        mMenuView.updateOnSizeChange(w);
+    }
+
+    /**
+     * Returns the {@link Menu} instance associated with this bottom navigation bar.
+     */
+    @NonNull
+    public Menu getMenu() {
+        return mMenu;
+    }
+
+    /**
+     * Inflate a menu resource into this navigation view.
+     *
+     * <p>Existing items in the menu will not be modified or removed.</p>
+     *
+     * @param resId ID of a menu resource to inflate
+     */
+    public void inflateMenu(int resId) {
+        mPresenter.setUpdateSuspended(true);
+        getMenuInflater().inflate(resId, mMenu);
+        mPresenter.initForMenu(getContext(), mMenu);
+        mPresenter.setUpdateSuspended(false);
+        mPresenter.updateMenuView(true);
+    }
+
+    /**
+     * Returns the tint which is applied to our menu items' icons.
+     *
+     * @see #setItemIconTintList(ColorStateList)
+     *
+     * @attr ref R.styleable#BottomNavigationView_itemIconTint
+     */
+    @Nullable
+    public ColorStateList getItemIconTintList() {
+        return mMenuView.getIconTintList();
+    }
+
+    /**
+     * Set the tint which is applied to our menu items' icons.
+     *
+     * @param tint the tint to apply.
+     *
+     * @attr ref R.styleable#BottomNavigationView_itemIconTint
+     */
+    public void setItemIconTintList(@Nullable ColorStateList tint) {
+        mMenuView.setIconTintList(tint);
+    }
+
+
+    /**
+     * Returns the tint which is applied to menu items' icons.
+     *
+     * @see #setItemTextColor(ColorStateList)
+     *
+     * @attr ref R.styleable#BottomNavigationView_itemTextColor
+     */
+    @Nullable
+    public ColorStateList getItemTextColor() {
+        return mMenuView.getItemTextColor();
+    }
+
+    /**
+     * Set the text color to be used on menu items.
+     *
+     * @see #getItemTextColor()
+     *
+     * @attr ref R.styleable#BottomNavigationView_itemTextColor
+     */
+    public void setItemTextColor(@Nullable ColorStateList textColor) {
+        mMenuView.setItemTextColor(textColor);
+    }
+
+    /**
+     * Set the background of our menu items to the given resource.
+     *
+     * @param resId The identifier of the resource.
+     *
+     * @attr ref R.styleable#BottomNavigationView_itemBackground
+     */
+    public void setItemBackgroundResource(@DrawableRes int resId) {
+        mMenuView.setItemBackgroundRes(resId);
+    }
+
+    /**
+     * Listener for handling events on bottom navigation items.
+     */
+    public interface OnNavigationItemSelectedListener {
+
+        /**
+         * Called when an item in the bottom navigation menu is selected.
+         *
+         * @param item The selected item
+         *
+         * @return true to display the item as the selected item
+         */
+        public boolean onNavigationItemSelected(@NonNull MenuItem item);
+    }
+
+    private MenuInflater getMenuInflater() {
+        if (mMenuInflater == null) {
+            mMenuInflater = new SupportMenuInflater(getContext());
+        }
+        return mMenuInflater;
+    }
+
+    private ColorStateList createDefaultColorStateList(int baseColorThemeAttr) {
+        final TypedValue value = new TypedValue();
+        if (!getContext().getTheme().resolveAttribute(baseColorThemeAttr, value, true)) {
+            return null;
+        }
+        ColorStateList baseColor = AppCompatResources.getColorStateList(
+                getContext(), value.resourceId);
+        if (!getContext().getTheme().resolveAttribute(
+                android.support.v7.appcompat.R.attr.colorPrimary, value, true)) {
+            return null;
+        }
+        int colorPrimary = value.data;
+        int defaultColor = baseColor.getDefaultColor();
+        return new ColorStateList(new int[][]{
+                DISABLED_STATE_SET,
+                CHECKED_STATE_SET,
+                EMPTY_STATE_SET
+        }, new int[]{
+                baseColor.getColorForState(DISABLED_STATE_SET, defaultColor),
+                colorPrimary,
+                defaultColor
+        });
+    }
+}
diff --git a/design/src/android/support/design/widget/CheckableImageButton.java b/design/src/android/support/design/widget/CheckableImageButton.java
new file mode 100644
index 0000000..2e7612b
--- /dev/null
+++ b/design/src/android/support/design/widget/CheckableImageButton.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.support.v4.view.AccessibilityDelegateCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.accessibility.AccessibilityEventCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v7.widget.AppCompatImageButton;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.Checkable;
+
+/**
+ * @hide
+ */
+public class CheckableImageButton extends AppCompatImageButton implements Checkable {
+
+    private static final int[] DRAWABLE_STATE_CHECKED = new int[]{android.R.attr.state_checked};
+
+    private boolean mChecked;
+
+    public CheckableImageButton(Context context) {
+        this(context, null);
+    }
+
+    public CheckableImageButton(Context context, AttributeSet attrs) {
+        this(context, attrs, android.support.v7.appcompat.R.attr.imageButtonStyle);
+    }
+
+    public CheckableImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegateCompat() {
+            @Override
+            public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+                super.onInitializeAccessibilityEvent(host, event);
+                event.setChecked(isChecked());
+            }
+
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host,
+                    AccessibilityNodeInfoCompat info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.setCheckable(true);
+                info.setChecked(isChecked());
+            }
+        });
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        if (mChecked != checked) {
+            mChecked = checked;
+            refreshDrawableState();
+            sendAccessibilityEvent(
+                    AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
+        }
+    }
+
+    @Override
+    public boolean isChecked() {
+        return mChecked;
+    }
+
+    @Override
+    public void toggle() {
+        setChecked(!mChecked);
+    }
+
+    @Override
+    public int[] onCreateDrawableState(int extraSpace) {
+        if (mChecked) {
+            return mergeDrawableStates(
+                    super.onCreateDrawableState(extraSpace + DRAWABLE_STATE_CHECKED.length),
+                    DRAWABLE_STATE_CHECKED);
+        } else {
+            return super.onCreateDrawableState(extraSpace);
+        }
+    }
+}
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index f75bc1a..b25a4b2 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -157,7 +157,7 @@
                 R.styleable.FloatingActionButton, defStyleAttr,
                 R.style.Widget_Design_FloatingActionButton);
         mBackgroundTint = a.getColorStateList(R.styleable.FloatingActionButton_backgroundTint);
-        mBackgroundTintMode = parseTintMode(a.getInt(
+        mBackgroundTintMode = ViewUtils.parseTintMode(a.getInt(
                 R.styleable.FloatingActionButton_backgroundTintMode, -1), null);
         mRippleColor = a.getColor(R.styleable.FloatingActionButton_rippleColor, 0);
         mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, SIZE_AUTO);
@@ -511,23 +511,6 @@
         return result;
     }
 
-    static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
-        switch (value) {
-            case 3:
-                return PorterDuff.Mode.SRC_OVER;
-            case 5:
-                return PorterDuff.Mode.SRC_IN;
-            case 9:
-                return PorterDuff.Mode.SRC_ATOP;
-            case 14:
-                return PorterDuff.Mode.MULTIPLY;
-            case 15:
-                return PorterDuff.Mode.SCREEN;
-            default:
-                return defaultMode;
-        }
-    }
-
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if(getContentRect(mTouchArea) && !mTouchArea.contains((int) ev.getX(), (int) ev.getY())) {
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 171b62b..782e3d0 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -18,18 +18,21 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
+import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.DrawableContainer;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
 import android.support.annotation.StyleRes;
 import android.support.design.R;
 import android.support.v4.content.ContextCompat;
@@ -43,18 +46,23 @@
 import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.widget.Space;
+import android.support.v4.widget.TextViewCompat;
 import android.support.v7.widget.AppCompatDrawableManager;
+import android.support.v7.widget.TintTypedArray;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
+import android.text.method.PasswordTransformationMethod;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.EditText;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -90,12 +98,14 @@
 
     private static final String LOG_TAG = "TextInputLayout";
 
+    private final FrameLayout mInputFrame;
     private EditText mEditText;
 
     private boolean mHintEnabled;
     private CharSequence mHint;
 
     private Paint mTmpPaint;
+    private final Rect mTmpRect = new Rect();
 
     private LinearLayout mIndicatorArea;
     private int mIndicatorsAdded;
@@ -113,6 +123,18 @@
     private int mCounterOverflowTextAppearance;
     private boolean mCounterOverflowed;
 
+    private boolean mPasswordToggleEnabled;
+    private Drawable mPasswordToggleDrawable;
+    private CharSequence mPasswordToggleContentDesc;
+    private CheckableImageButton mPasswordToggleView;
+    private boolean mPasswordToggledVisible;
+    private Drawable mPasswordToggleDummyDrawable;
+
+    private ColorStateList mPasswordToggleTintList;
+    private boolean mHasPasswordToggleTintList;
+    private PorterDuff.Mode mPasswordToggleTintMode;
+    private boolean mHasPasswordToggleTintMode;
+
     private ColorStateList mDefaultTextColor;
     private ColorStateList mFocusedTextColor;
 
@@ -141,11 +163,14 @@
         setWillNotDraw(false);
         setAddStatesFromChildren(true);
 
+        mInputFrame = new FrameLayout(context);
+        addView(mInputFrame);
+
         mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
         mCollapsingTextHelper.setPositionInterpolator(new AccelerateInterpolator());
         mCollapsingTextHelper.setCollapsedTextGravity(Gravity.TOP | GravityCompat.START);
 
-        final TypedArray a = context.obtainStyledAttributes(attrs,
+        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
                 R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);
         mHintEnabled = a.getBoolean(R.styleable.TextInputLayout_hintEnabled, true);
         setHint(a.getText(R.styleable.TextInputLayout_android_hint));
@@ -175,10 +200,28 @@
                 R.styleable.TextInputLayout_counterTextAppearance, 0);
         mCounterOverflowTextAppearance = a.getResourceId(
                 R.styleable.TextInputLayout_counterOverflowTextAppearance, 0);
+
+        mPasswordToggleEnabled = a.getBoolean(
+                R.styleable.TextInputLayout_passwordToggleEnabled, true);
+        mPasswordToggleDrawable = a.getDrawable(R.styleable.TextInputLayout_passwordToggleDrawable);
+        mPasswordToggleContentDesc = a.getText(
+                R.styleable.TextInputLayout_passwordToggleContentDescription);
+        if (a.hasValue(R.styleable.TextInputLayout_passwordToggleTint)) {
+            mHasPasswordToggleTintList = true;
+            mPasswordToggleTintList = a.getColorStateList(
+                    R.styleable.TextInputLayout_passwordToggleTint);
+        }
+        if (a.hasValue(R.styleable.TextInputLayout_passwordToggleTintMode)) {
+            mHasPasswordToggleTintMode = true;
+            mPasswordToggleTintMode = ViewUtils.parseTintMode(
+                    a.getInt(R.styleable.TextInputLayout_passwordToggleTintMode, -1), null);
+        }
+
         a.recycle();
 
         setErrorEnabled(errorEnabled);
         setCounterEnabled(counterEnabled);
+        applyPasswordToggleTint();
 
         if (ViewCompat.getImportantForAccessibility(this)
                 == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
@@ -191,10 +234,16 @@
     }
 
     @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
+    public void addView(View child, int index, final ViewGroup.LayoutParams params) {
         if (child instanceof EditText) {
+            mInputFrame.addView(child, new FrameLayout.LayoutParams(params));
+
+            // Now use the EditText's LayoutParams as our own and update them to make enough space
+            // for the label
+            mInputFrame.setLayoutParams(params);
+            updateInputLayoutMargins();
+
             setEditText((EditText) child);
-            super.addView(child, 0, updateEditTextMargin(params));
         } else {
             // Carry on adding the View...
             super.addView(child, index, params);
@@ -232,8 +281,13 @@
 
         mEditText = editText;
 
+        final boolean hasPasswordTransformation = hasPasswordTransformation();
+
         // Use the EditText's typeface, and it's text size for our expanded text
-        mCollapsingTextHelper.setTypefaces(mEditText.getTypeface());
+        if (!hasPasswordTransformation) {
+            // We don't want a monospace font just because we have a password field
+            mCollapsingTextHelper.setTypefaces(mEditText.getTypeface());
+        }
         mCollapsingTextHelper.setExpandedTextSize(mEditText.getTextSize());
 
         final int editTextGravity = mEditText.getGravity();
@@ -278,14 +332,17 @@
             adjustIndicatorPadding();
         }
 
+        updatePasswordToggleView();
+
         // Update the label visibility with no animation
         updateLabelState(false);
     }
 
-    private LayoutParams updateEditTextMargin(ViewGroup.LayoutParams lp) {
+    private void updateInputLayoutMargins() {
         // Create/update the LayoutParams so that we can add enough top margin
         // to the EditText so make room for the label
-        LayoutParams llp = lp instanceof LayoutParams ? (LayoutParams) lp : new LayoutParams(lp);
+        final LayoutParams lp = (LayoutParams) mInputFrame.getLayoutParams();
+        final int newTopMargin;
 
         if (mHintEnabled) {
             if (mTmpPaint == null) {
@@ -293,12 +350,15 @@
             }
             mTmpPaint.setTypeface(mCollapsingTextHelper.getCollapsedTypeface());
             mTmpPaint.setTextSize(mCollapsingTextHelper.getCollapsedTextSize());
-            llp.topMargin = (int) -mTmpPaint.ascent();
+            newTopMargin = (int) -mTmpPaint.ascent();
         } else {
-            llp.topMargin = 0;
+            newTopMargin = 0;
         }
 
-        return llp;
+        if (newTopMargin != lp.topMargin) {
+            lp.topMargin = newTopMargin;
+            mInputFrame.requestLayout();
+        }
     }
 
     private void updateLabelState(boolean animate) {
@@ -404,8 +464,7 @@
 
             // Now update the EditText top margin
             if (mEditText != null) {
-                final LayoutParams lp = updateEditTextMargin(mEditText.getLayoutParams());
-                mEditText.setLayoutParams(lp);
+                updateInputLayoutMargins();
             }
         }
     }
@@ -432,11 +491,8 @@
 
         if (mEditText != null) {
             updateLabelState(false);
-
             // Text size might have changed so update the top margin
-            LayoutParams lp = updateEditTextMargin(mEditText.getLayoutParams());
-            mEditText.setLayoutParams(lp);
-            mEditText.requestLayout();
+            updateInputLayoutMargins();
         }
     }
 
@@ -874,16 +930,280 @@
     }
 
     @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        updatePasswordToggleView();
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    private void updatePasswordToggleView() {
+        if (shouldShowPasswordIcon()) {
+            if (mPasswordToggleView == null) {
+                mPasswordToggleView = (CheckableImageButton) LayoutInflater.from(getContext())
+                        .inflate(R.layout.design_text_input_password_icon, mInputFrame, false);
+                mPasswordToggleView.setImageDrawable(mPasswordToggleDrawable);
+                mPasswordToggleView.setContentDescription(mPasswordToggleContentDesc);
+                mInputFrame.addView(mPasswordToggleView);
+
+                mPasswordToggleView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View view) {
+                        passwordVisibilityToggleRequested();
+                    }
+                });
+            }
+
+            mPasswordToggleView.setVisibility(VISIBLE);
+
+            // We need to add a dummy drawable as the end compound drawable so that the text is
+            // indented and doesn't display below the toggle view
+            if (mPasswordToggleDummyDrawable == null) {
+                mPasswordToggleDummyDrawable = new ColorDrawable();
+            }
+            mPasswordToggleDummyDrawable.setBounds(0, 0, mPasswordToggleView.getMeasuredWidth(), 1);
+
+            final Drawable[] compounds = TextViewCompat.getCompoundDrawablesRelative(mEditText);
+            TextViewCompat.setCompoundDrawablesRelative(mEditText, compounds[0], compounds[1],
+                    mPasswordToggleDummyDrawable, compounds[2]);
+
+            // Copy over the EditText's padding so that we match
+            mPasswordToggleView.setPadding(mEditText.getPaddingLeft(),
+                    mEditText.getPaddingTop(), mEditText.getPaddingRight(),
+                    mEditText.getPaddingBottom());
+        } else {
+            if (mPasswordToggleView != null && mPasswordToggleView.getVisibility() == VISIBLE) {
+                mPasswordToggleView.setVisibility(View.GONE);
+            }
+
+            // Make sure that we remove the dummy end compound drawable
+            final Drawable[] compounds = TextViewCompat.getCompoundDrawablesRelative(mEditText);
+            TextViewCompat.setCompoundDrawablesRelative(mEditText, compounds[0], compounds[1],
+                    null, compounds[2]);
+        }
+    }
+
+    /**
+     * Set the icon to use for the password visibility toggle button.
+     *
+     * <p>If you use an icon you should also set a description for its action
+     * using {@link #setPasswordVisibilityToggleContentDescription(CharSequence)}.
+     * This is used for accessibility.</p>
+     *
+     * @param resId resource id of the drawable to set, or 0 to clear the icon
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleDrawable
+     */
+    public void setPasswordVisibilityToggleDrawable(@DrawableRes int resId) {
+        setPasswordVisibilityToggleDrawable(resId != 0
+                ? AppCompatDrawableManager.get().getDrawable(getContext(), resId)
+                : null);
+    }
+
+    /**
+     * Set the icon to use for the password visibility toggle button.
+     *
+     * <p>If you use an icon you should also set a description for its action
+     * using {@link #setPasswordVisibilityToggleContentDescription(CharSequence)}.
+     * This is used for accessibility.</p>
+     *
+     * @param icon Drawable to set, may be null to clear the icon
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleDrawable
+     */
+    public void setPasswordVisibilityToggleDrawable(@Nullable Drawable icon) {
+        mPasswordToggleDrawable = icon;
+        if (mPasswordToggleView != null) {
+            mPasswordToggleView.setImageDrawable(icon);
+        }
+    }
+
+    /**
+     * Set a content description for the navigation button if one is present.
+     *
+     * <p>The content description will be read via screen readers or other accessibility
+     * systems to explain the action of the password visibility toggle.</p>
+     *
+     * @param resId Resource ID of a content description string to set,
+     *              or 0 to clear the description
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleContentDescription
+     */
+    public void setPasswordVisibilityToggleContentDescription(@StringRes int resId) {
+        setPasswordVisibilityToggleContentDescription(
+                resId != 0 ? getResources().getText(resId) : null);
+    }
+
+    /**
+     * Set a content description for the navigation button if one is present.
+     *
+     * <p>The content description will be read via screen readers or other accessibility
+     * systems to explain the action of the password visibility toggle.</p>
+     *
+     * @param description Content description to set, or null to clear the content description
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleContentDescription
+     */
+    public void setPasswordVisibilityToggleContentDescription(@Nullable CharSequence description) {
+        mPasswordToggleContentDesc = description;
+        if (mPasswordToggleView != null) {
+            mPasswordToggleView.setContentDescription(description);
+        }
+    }
+
+    /**
+     * Returns the icon currently used for the password visibility toggle button.
+     *
+     * @see #setPasswordVisibilityToggleDrawable(Drawable)
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleDrawable
+     */
+    @Nullable
+    public Drawable getPasswordVisibilityToggleDrawable() {
+        return mPasswordToggleDrawable;
+    }
+
+    /**
+     * Returns the currently configured content description for the password visibility
+     * toggle button.
+     *
+     * <p>This will be used to describe the navigation action to users through mechanisms
+     * such as screen readers.</p>
+     */
+    @Nullable
+    public CharSequence getPasswordVisibilityToggleContentDescription() {
+        return mPasswordToggleContentDesc;
+    }
+
+    /**
+     * Returns whether the password visibility toggle functionality is currently enabled.
+     *
+     * @see #setPasswordVisibilityToggleEnabled(boolean)
+     */
+    public boolean isPasswordVisibilityToggleEnabled() {
+        return mPasswordToggleEnabled;
+    }
+
+    /**
+     * Returns whether the password visibility toggle functionality is enabled or not.
+     *
+     * <p>When enabled, a button is placed at the end of the EditText which enables the user
+     * to switch between the field's input being visibly disguised or not.</p>
+     *
+     * @param enabled true to enable the functionality
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleEnabled
+     */
+    public void setPasswordVisibilityToggleEnabled(final boolean enabled) {
+        if (mPasswordToggleEnabled != enabled) {
+            mPasswordToggleEnabled = enabled;
+
+            if (!enabled && mPasswordToggledVisible) {
+                // If the toggle is no longer enabled, but we remove the PasswordTransformation
+                // to make the password visible, add it back
+                mEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+            }
+
+            // Reset the visibility tracking flag
+            mPasswordToggledVisible = false;
+
+            updatePasswordToggleView();
+        }
+    }
+
+    /**
+     * Applies a tint to the the password visibility toggle drawable. Does not modify the current
+     * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     *
+     * <p>Subsequent calls to {@link #setPasswordVisibilityToggleDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and tint mode using
+     * {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.</p>
+     *
+     * @param tintList the tint to apply, may be null to clear tint
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleTint
+     */
+    public void setPasswordVisibilityToggleTintList(@Nullable ColorStateList tintList) {
+        mPasswordToggleTintList = tintList;
+        mHasPasswordToggleTintList = true;
+        applyPasswordToggleTint();
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setPasswordVisibilityToggleTintList(ColorStateList)} to the password
+     * visibility toggle drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.</p>
+     *
+     * @param mode the blending mode used to apply the tint, may be null to clear tint
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleTintMode
+     */
+    public void setPasswordVisibilityToggleTintMode(@Nullable PorterDuff.Mode mode) {
+        mPasswordToggleTintMode = mode;
+        mHasPasswordToggleTintMode = true;
+        applyPasswordToggleTint();
+    }
+
+   private void passwordVisibilityToggleRequested() {
+       if (mPasswordToggleEnabled) {
+           // Store the current cursor position
+           final int selection = mEditText.getSelectionEnd();
+
+           if (hasPasswordTransformation()) {
+               mEditText.setTransformationMethod(null);
+               mPasswordToggledVisible = true;
+           } else {
+               mEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+               mPasswordToggledVisible = false;
+           }
+
+           mPasswordToggleView.setChecked(mPasswordToggledVisible);
+
+           // And restore the cursor position
+           mEditText.setSelection(selection);
+       }
+    }
+
+    private boolean hasPasswordTransformation() {
+        return mEditText != null
+                && mEditText.getTransformationMethod() instanceof PasswordTransformationMethod;
+    }
+
+    private boolean shouldShowPasswordIcon() {
+        return mPasswordToggleEnabled && (hasPasswordTransformation() || mPasswordToggledVisible);
+    }
+
+    private void applyPasswordToggleTint() {
+        if (mPasswordToggleDrawable != null
+                && (mHasPasswordToggleTintList || mHasPasswordToggleTintMode)) {
+            mPasswordToggleDrawable = DrawableCompat.wrap(mPasswordToggleDrawable).mutate();
+
+            if (mHasPasswordToggleTintList) {
+                DrawableCompat.setTintList(mPasswordToggleDrawable, mPasswordToggleTintList);
+            }
+            if (mHasPasswordToggleTintMode) {
+                DrawableCompat.setTintMode(mPasswordToggleDrawable, mPasswordToggleTintMode);
+            }
+
+            if (mPasswordToggleView != null
+                    && mPasswordToggleView.getDrawable() != mPasswordToggleDrawable) {
+                mPasswordToggleView.setImageDrawable(mPasswordToggleDrawable);
+            }
+        }
+    }
+
+    @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
         if (mHintEnabled && mEditText != null) {
-            final int l = mEditText.getLeft() + mEditText.getCompoundPaddingLeft();
-            final int r = mEditText.getRight() - mEditText.getCompoundPaddingRight();
+            final Rect rect = mTmpRect;
+            ViewGroupUtils.getDescendantRect(this, mEditText, rect);
 
-            mCollapsingTextHelper.setExpandedBounds(l,
-                    mEditText.getTop() + mEditText.getCompoundPaddingTop(),
-                    r, mEditText.getBottom() - mEditText.getCompoundPaddingBottom());
+            final int l = rect.left + mEditText.getCompoundPaddingLeft();
+            final int r = rect.right - mEditText.getCompoundPaddingRight();
+
+            mCollapsingTextHelper.setExpandedBounds(
+                    l, rect.top + mEditText.getCompoundPaddingTop(),
+                    r, rect.bottom - mEditText.getCompoundPaddingBottom());
 
             // Set the collapsed bounds to be the the full height (minus padding) to match the
             // EditText's editable area
diff --git a/design/src/android/support/design/widget/ViewUtils.java b/design/src/android/support/design/widget/ViewUtils.java
index e2eedb2..f49d836 100644
--- a/design/src/android/support/design/widget/ViewUtils.java
+++ b/design/src/android/support/design/widget/ViewUtils.java
@@ -16,6 +16,7 @@
 
 package android.support.design.widget;
 
+import android.graphics.PorterDuff;
 import android.os.Build;
 
 class ViewUtils {
@@ -38,4 +39,21 @@
         return (a == b) || (a != null && a.equals(b));
     }
 
+    static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
+        switch (value) {
+            case 3:
+                return PorterDuff.Mode.SRC_OVER;
+            case 5:
+                return PorterDuff.Mode.SRC_IN;
+            case 9:
+                return PorterDuff.Mode.SRC_ATOP;
+            case 14:
+                return PorterDuff.Mode.MULTIPLY;
+            case 15:
+                return PorterDuff.Mode.SCREEN;
+            default:
+                return defaultMode;
+        }
+    }
+
 }
diff --git a/design/tests/res/layout/action_layout_custom.xml b/design/tests/res/layout/action_layout_custom.xml
new file mode 100644
index 0000000..10a3268
--- /dev/null
+++ b/design/tests/res/layout/action_layout_custom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/lilac_default"
+    android:text="@string/navigate_custom"/>
diff --git a/design/tests/res/layout/design_text_input.xml b/design/tests/res/layout/design_text_input.xml
index 1312b50..79b6446 100644
--- a/design/tests/res/layout/design_text_input.xml
+++ b/design/tests/res/layout/design_text_input.xml
@@ -36,4 +36,19 @@
 
     </android.support.design.widget.TextInputLayout>
 
+    <android.support.design.widget.TextInputLayout
+        android:id="@+id/textinput_password"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:passwordToggleEnabled="true">
+
+        <android.support.design.widget.TextInputEditText
+            android:id="@+id/textinput_edittext_pwd"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="@string/textinput_hint"
+            android:inputType="textPassword"/>
+
+    </android.support.design.widget.TextInputLayout>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/design/tests/res/menu/navigation_view_content.xml b/design/tests/res/menu/navigation_view_content.xml
index 758334f..a8fb464 100644
--- a/design/tests/res/menu/navigation_view_content.xml
+++ b/design/tests/res/menu/navigation_view_content.xml
@@ -29,5 +29,7 @@
               app:actionLayout="@layout/action_layout" />
         <item android:id="@+id/destination_settings"
               android:title="@string/navigate_settings" />
+        <item android:id="@+id/desitination_custom"
+              app:actionLayout="@layout/action_layout_custom" />
     </group>
 </menu>
diff --git a/design/tests/res/values/strings.xml b/design/tests/res/values/strings.xml
index a431e2b..380cbe3 100644
--- a/design/tests/res/values/strings.xml
+++ b/design/tests/res/values/strings.xml
@@ -19,6 +19,7 @@
     <string name="navigate_home">Home</string>
     <string name="navigate_profile">Profile</string>
     <string name="navigate_people">People</string>
+    <string name="navigate_custom">Custom</string>
     <string name="navigate_settings">Settings</string>
 
     <string name="snackbar_text">This is a test message</string>
diff --git a/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java b/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java
index 3360029..92ba267 100755
--- a/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java
+++ b/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java
@@ -23,6 +23,7 @@
 import android.support.test.espresso.ViewAction;
 import android.support.v4.widget.DrawerLayout;
 import android.view.View;
+import android.widget.TextView;
 
 import org.hamcrest.Matcher;
 
@@ -75,4 +76,30 @@
             }
         };
     }
+
+    public static ViewAction setPasswordVisibilityToggleEnabled(final boolean enabled) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextInputLayout.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "Sets the error";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextInputLayout layout = (TextInputLayout) view;
+                layout.setPasswordVisibilityToggleEnabled(enabled);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+
 }
diff --git a/design/tests/src/android/support/design/widget/NavigationViewTest.java b/design/tests/src/android/support/design/widget/NavigationViewTest.java
index 2abce52..97c43c3 100755
--- a/design/tests/src/android/support/design/widget/NavigationViewTest.java
+++ b/design/tests/src/android/support/design/widget/NavigationViewTest.java
@@ -30,6 +30,8 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.widget.TextView;
+
 import org.hamcrest.Matcher;
 import org.junit.Before;
 import org.junit.Test;
@@ -90,7 +92,7 @@
         // Check the contents of the Menu object
         final Menu menu = mNavigationView.getMenu();
         assertNotNull("Menu should not be null", menu);
-        assertEquals("Should have matching number of items", MENU_CONTENT_ITEM_IDS.length,
+        assertEquals("Should have matching number of items", MENU_CONTENT_ITEM_IDS.length + 1,
                 menu.size());
         for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
             final MenuItem currItem = menu.getItem(i);
@@ -549,5 +551,16 @@
         // makes our matcher actually run. If for some reason NavigationView fails to inflate and
         // display our SwitchCompat action layout, the next line will fail in the matcher pass.
         onView(menuItemMatcher).perform(click());
+
+        // Check that the full custom view is displayed without title and icon.
+        final Resources res = mActivityTestRule.getActivity().getResources();
+        Matcher customItemMatcher = allOf(
+                isDescendantOfA(withId(R.id.start_drawer)),
+                isChildOfA(isAssignableFrom(RecyclerView.class)),
+                hasDescendant(withText(res.getString(R.string.navigate_custom))),
+                hasDescendant(allOf(
+                        isAssignableFrom(TextView.class),
+                        withEffectiveVisibility(Visibility.GONE))));
+        onView(customItemMatcher).perform(click());
     }
 }
diff --git a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
index 558f474..d612932 100755
--- a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
@@ -16,22 +16,28 @@
 
 package android.support.design.widget;
 
+import android.app.Activity;
+import android.support.design.test.R;
+import android.support.test.annotation.UiThreadTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.widget.EditText;
+
+import org.junit.Test;
+
+import static android.support.design.testutils.TestUtilsActions.setText;
 import static android.support.design.testutils.TextInputLayoutActions.setError;
 import static android.support.design.testutils.TextInputLayoutActions.setErrorEnabled;
+import static android.support.design.testutils.TextInputLayoutActions.setPasswordVisibilityToggleEnabled;
 import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withChild;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
-
-import static org.hamcrest.CoreMatchers.not;
-
-import android.support.design.test.R;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Test;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 @SmallTest
 public class TextInputLayoutTest extends BaseInstrumentationTestCase<TextInputLayoutActivity> {
@@ -39,6 +45,8 @@
     private static final String ERROR_MESSAGE_1 = "An error has occured";
     private static final String ERROR_MESSAGE_2 = "Some other error has occured";
 
+    private static final String INPUT_TEXT = "Random input text";
+
     public TextInputLayoutTest() {
         super(TextInputLayoutActivity.class);
     }
@@ -73,4 +81,43 @@
         onView(withText(ERROR_MESSAGE_2)).check(matches(isDisplayed()));
     }
 
+    @Test
+    @UiThreadTest
+    public void testPasswordToggleClick() {
+        // Set some text on the EditText
+        onView(withId(R.id.textinput_edittext_pwd)).perform(setText(INPUT_TEXT));
+
+        final Activity activity = mActivityTestRule.getActivity();
+        final EditText textInput = (EditText) activity.findViewById(R.id.textinput_edittext_pwd);
+
+        // Assert that the password is disguised
+        assertNotEquals(INPUT_TEXT, textInput.getLayout().getText());
+
+        // Now click the toggle button
+        onView(withId(R.id.text_input_password_toggle)).perform(click());
+
+        // And assert that the password is not disguised
+        assertEquals(INPUT_TEXT, textInput.getLayout().getText());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPasswordToggleDisable() {
+        final Activity activity = mActivityTestRule.getActivity();
+        final EditText textInput = (EditText) activity.findViewById(R.id.textinput_edittext_pwd);
+
+        // Set some text on the EditText
+        onView(withId(R.id.textinput_edittext_pwd)).perform(setText(INPUT_TEXT));
+        // Assert that the password is disguised
+        assertNotEquals(INPUT_TEXT, textInput.getLayout().getText());
+
+        // Disable the password toggle
+        onView(withId(R.id.text_input_password_toggle))
+                .perform(setPasswordVisibilityToggleEnabled(false));
+
+        // Check that the password toggle view is not visible
+        onView(withId(R.id.text_input_password_toggle)).check(matches(not(isDisplayed())));
+        // ...and that the password is not disguised
+        assertEquals(INPUT_TEXT, textInput.getLayout().getText());
+    }
 }
diff --git a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
index d184f07..0f424d5 100644
--- a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -1514,6 +1514,7 @@
         @Override
         public void setExtras(Bundle extras) {
             mExtras = extras;
+            sendExtras(extras);
         }
 
         // Registers/unregisters the RCC and MediaButtonEventReceiver as needed.
@@ -1729,6 +1730,18 @@
             mControllerCallbacks.finishBroadcast();
         }
 
+        private void sendExtras(Bundle extras) {
+            int size = mControllerCallbacks.beginBroadcast();
+            for (int i = size - 1; i >= 0; i--) {
+                IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
+                try {
+                    cb.onExtrasChanged(extras);
+                } catch (RemoteException e) {
+                }
+            }
+            mControllerCallbacks.finishBroadcast();
+        }
+
         class MediaSessionStub extends IMediaSession.Stub {
             @Override
             public void sendCommand(String command, Bundle args, ResultReceiverWrapper cb) {
diff --git a/samples/SupportDesignDemos/AndroidManifest.xml b/samples/SupportDesignDemos/AndroidManifest.xml
index 4d05bc6..ec97037 100644
--- a/samples/SupportDesignDemos/AndroidManifest.xml
+++ b/samples/SupportDesignDemos/AndroidManifest.xml
@@ -307,5 +307,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".widget.BottomNavigationViewUsage"
+                  android:label="@string/design_bottom_navigation_view"
+                  android:theme="@style/Theme.Design">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.support.design.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
     </application>
 </manifest>
diff --git a/samples/SupportDesignDemos/res/layout/action_layout_custom.xml b/samples/SupportDesignDemos/res/layout/action_layout_custom.xml
new file mode 100644
index 0000000..8ed1c8c
--- /dev/null
+++ b/samples/SupportDesignDemos/res/layout/action_layout_custom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<TextView
+    android:id="@+id/text"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="?attr/colorPrimary"
+    android:gravity="center"
+    android:text="@string/navigation_sub_item_3"
+    android:textAppearance="@style/TextAppearance.AppCompat.Widget.Button.Inverse"/>
diff --git a/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml b/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
new file mode 100644
index 0000000..7e66c79
--- /dev/null
+++ b/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <Button
+            android:id="@+id/button_disable"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/bottomnavigation_disable"/>
+
+    <Button
+        android:id="@+id/button_add"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="50dp"
+        android:text="@string/bottomnavigation_add"/>
+
+
+    <android.support.design.widget.BottomNavigationView
+            android:id="@+id/bottom_navigation"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:layout_gravity="bottom"
+            android:background="#eee"
+            app:menu="@menu/sample_bottom_menu"/>
+
+</FrameLayout>
diff --git a/samples/SupportDesignDemos/res/layout/design_text_input.xml b/samples/SupportDesignDemos/res/layout/design_text_input.xml
index bd5f69b..0729d28 100644
--- a/samples/SupportDesignDemos/res/layout/design_text_input.xml
+++ b/samples/SupportDesignDemos/res/layout/design_text_input.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -14,62 +13,66 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              xmlns:app="http://schemas.android.com/apk/res-auto"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:orientation="vertical"
-              android:padding="16dp">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:padding="16dp">
 
-    <android.support.design.widget.TextInputLayout
+        <android.support.design.widget.TextInputLayout
             android:id="@+id/input_username"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             app:errorEnabled="true">
 
-        <android.support.design.widget.TextInputEditText
+            <android.support.design.widget.TextInputEditText
                 android:id="@+id/edit_username"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:hint="@string/form_username"/>
+                android:hint="@string/form_username" />
 
-    </android.support.design.widget.TextInputLayout>
+        </android.support.design.widget.TextInputLayout>
 
-    <LinearLayout android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="horizontal">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
 
-        <Button
+            <Button
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="@string/show_error"
-                android:onClick="showError"/>
+                android:onClick="showError"
+                android:text="@string/show_error" />
 
-        <Button
+            <Button
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="@string/clear_error"
-                android:onClick="clearError"/>
+                android:onClick="clearError"
+                android:text="@string/clear_error" />
 
-    </LinearLayout>
+        </LinearLayout>
 
-    <android.support.design.widget.TextInputLayout
+        <android.support.design.widget.TextInputLayout
             android:id="@+id/input_email"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="8dp"
             app:errorEnabled="true">
 
-        <android.support.design.widget.TextInputEditText
+            <android.support.design.widget.TextInputEditText
                 android:id="@+id/edit_email"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:hint="@string/form_email"/>
+                android:hint="@string/form_email" />
 
-    </android.support.design.widget.TextInputLayout>
+        </android.support.design.widget.TextInputLayout>
 
-    <android.support.design.widget.TextInputLayout
+        <android.support.design.widget.TextInputLayout
             android:id="@+id/input_description"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -77,12 +80,49 @@
             app:counterEnabled="true"
             app:counterMaxLength="30">
 
-        <EditText
+            <EditText
                 android:id="@+id/edit_description"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:hint="@string/form_description"/>
+                android:hint="@string/form_description" />
 
-    </android.support.design.widget.TextInputLayout>
+        </android.support.design.widget.TextInputLayout>
 
-</LinearLayout>
\ No newline at end of file
+        <android.support.design.widget.TextInputLayout
+            android:id="@+id/input_password"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            app:passwordToggleEnabled="true">
+
+            <EditText
+                android:id="@+id/edit_password"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:hint="@string/form_password"
+                android:inputType="textPassword" />
+
+        </android.support.design.widget.TextInputLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:onClick="setPasswordEnabled"
+                android:text="@string/password_enable" />
+
+            <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:onClick="setPasswordDisabled"
+                android:text="@string/password_disable" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/samples/SupportDesignDemos/res/menu/navigation.xml b/samples/SupportDesignDemos/res/menu/navigation.xml
index 6425b18..de17967 100644
--- a/samples/SupportDesignDemos/res/menu/navigation.xml
+++ b/samples/SupportDesignDemos/res/menu/navigation.xml
@@ -57,6 +57,9 @@
                     android:id="@+id/navigation_sub_item_2"
                     android:icon="@drawable/ic_android"
                     android:title="@string/navigation_sub_item_2"/>
+            <item
+                    android:id="@+id/navigation_sub_item_3"
+                    app:actionLayout="@layout/action_layout_custom"/>
         </menu>
     </item>
 
diff --git a/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml b/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml
new file mode 100644
index 0000000..4294f80
--- /dev/null
+++ b/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 Google Inc.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/action_search"
+          android:title="@string/menu_search"
+          android:icon="@drawable/ic_search"/>
+    <item android:id="@+id/action_settings"
+          android:title="@string/menu_settings"
+          android:icon="@drawable/ic_add"/>
+    <item android:id="@+id/action_navigation"
+          android:title="@string/tab_text"
+          android:icon="@drawable/ic_action_navigation_menu"/>
+</menu>
\ No newline at end of file
diff --git a/samples/SupportDesignDemos/res/values/strings.xml b/samples/SupportDesignDemos/res/values/strings.xml
index 8f21310..ed725ba 100644
--- a/samples/SupportDesignDemos/res/values/strings.xml
+++ b/samples/SupportDesignDemos/res/values/strings.xml
@@ -51,6 +51,7 @@
     <string name="navigation_subheader">Subheader</string>
     <string name="navigation_sub_item_1">Subitem 1</string>
     <string name="navigation_sub_item_2">Subitem 2</string>
+    <string name="navigation_sub_item_3">Subitme 3</string>
 
     <string name="tabs_fixed">Fixed</string>
     <string name="tabs_scrollable">Scrollable</string>
@@ -66,8 +67,11 @@
     <string name="form_username">Username</string>
     <string name="form_email">Email address</string>
     <string name="form_description">Description</string>
+    <string name="form_password">Password field</string>
     <string name="show_error">Show error</string>
     <string name="clear_error">Clear error</string>
+    <string name="password_enable">Enable Password</string>
+    <string name="password_disable">Disable Password</string>
 
     <string name="design_snackbar_basic">Snackbar/Usage</string>
     <string name="design_snackbar_fab">Snackbar/Coordinated with FAB</string>
@@ -106,4 +110,8 @@
     <string name="bottomsheet_show">Show</string>
     <string name="item_n">Item %d</string>
 
+    <string name="design_bottom_navigation_view">Bottom navigation view</string>
+
+    <string name="bottomnavigation_disable">Disable item</string>
+    <string name="bottomnavigation_add">Add item</string>
 </resources>
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
new file mode 100644
index 0000000..3bccda4
--- /dev/null
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.example.android.support.design.widget;
+
+import com.example.android.support.design.R;
+
+import android.os.Bundle;
+import android.support.design.widget.BottomNavigationView;
+import android.support.v7.app.AppCompatActivity;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+
+/**
+ * This demonstrates idiomatic usage of the bottom navigation widget.
+ */
+public class BottomNavigationViewUsage extends AppCompatActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.design_bottom_navigation_view);
+        Button buttonDisable = (Button) findViewById(R.id.button_disable);
+        final BottomNavigationView bottom =
+                (BottomNavigationView) findViewById(R.id.bottom_navigation);
+        buttonDisable.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                bottom.getMenu().getItem(0).setEnabled(!bottom.getMenu().getItem(0).isEnabled());
+            }
+        });
+        Button buttonAdd = (Button) findViewById(R.id.button_add);
+        buttonAdd.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                MenuItem item = bottom.getMenu().add("Bananas");
+                item.setIcon(android.R.drawable.ic_lock_power_off);
+            }
+        });
+    }
+}
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsageBase.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsageBase.java
index 49080c0..fc3fefb 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsageBase.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/NavigationViewUsageBase.java
@@ -65,6 +65,9 @@
             case R.id.navigation_sub_item_2:
                 showToast(R.string.navigation_sub_item_2);
                 return true;
+            case R.id.navigation_sub_item_3:
+                showToast(R.string.navigation_sub_item_3);
+                return true;
             case R.id.navigation_with_icon:
                 showToast(R.string.navigation_item_with_icon);
                 return true;
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java
index 63ac031..a5e382d 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java
@@ -29,6 +29,7 @@
 public class TextInputLayoutUsage extends AppCompatActivity {
 
     private TextInputLayout mUsernameInputLayout;
+    private TextInputLayout mPasswordInputLayout;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -36,6 +37,7 @@
         setContentView(R.layout.design_text_input);
 
         mUsernameInputLayout = (TextInputLayout) findViewById(R.id.input_username);
+        mPasswordInputLayout = (TextInputLayout) findViewById(R.id.input_password);
     }
 
     public void showError(View view) {
@@ -46,4 +48,12 @@
         mUsernameInputLayout.setError(null);
     }
 
+    public void setPasswordEnabled(View view) {
+        mPasswordInputLayout.setPasswordVisibilityToggleEnabled(true);
+    }
+
+    public void setPasswordDisabled(View view) {
+        mPasswordInputLayout.setPasswordVisibilityToggleEnabled(false);
+    }
+
 }
diff --git a/v14/preference/src/android/support/v14/preference/SwitchPreference.java b/v14/preference/src/android/support/v14/preference/SwitchPreference.java
index 3f95d1a..6e4b0d7 100644
--- a/v14/preference/src/android/support/v14/preference/SwitchPreference.java
+++ b/v14/preference/src/android/support/v14/preference/SwitchPreference.java
@@ -217,7 +217,7 @@
             return;
         }
 
-        View switchView = view.findViewById(android.support.v7.preference.R.id.switchWidget);
+        View switchView = view.findViewById(AndroidResources.ANDROID_R_SWITCH_WIDGET);
         syncSwitchView(switchView);
 
         View summaryView = view.findViewById(android.R.id.summary);
diff --git a/v17/leanback/res/animator/lb_playback_now_bar1_animator.xml b/v17/leanback/res/animator/lb_playback_now_bar1_animator.xml
new file mode 100644
index 0000000..10be669
--- /dev/null
+++ b/v17/leanback/res/animator/lb_playback_now_bar1_animator.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+                android:duration="2320"
+                android:repeatCount="infinite"
+                android:interpolator="@android:anim/linear_interpolator">
+    <propertyValuesHolder android:propertyName="scaleY" >
+
+        <keyframe android:value="0.417" />
+        <keyframe android:value="0.25" />
+        <keyframe android:value="0.417" />
+        <keyframe android:value="0.583" />
+        <keyframe android:value="0.75" />
+
+        <keyframe android:value="0.833" />
+        <keyframe android:value="0.917" />
+        <keyframe android:value="1.0" />
+        <keyframe android:value="0.917" />
+        <keyframe android:value="1.0" />
+
+        <keyframe android:value="0.833" />
+        <keyframe android:value="0.667" />
+        <keyframe android:value="0.5" />
+        <keyframe android:value="0.333" />
+        <keyframe android:value="0.167" />
+
+        <keyframe android:value="0.333" />
+        <keyframe android:value="0.5" />
+        <keyframe android:value="0.583" />
+        <keyframe android:value="0.75" />
+        <keyframe android:value="0.917" />
+
+        <keyframe android:value="0.75" />
+        <keyframe android:value="0.583" />
+        <keyframe android:value="0.417" />
+        <keyframe android:value="0.25" />
+        <keyframe android:value="0.417" />
+
+        <keyframe android:value="0.667" />
+        <keyframe android:value="0.417" />
+        <keyframe android:value="0.25" />
+        <keyframe android:value="0.333" />
+        <keyframe android:value="0.417" />
+
+    </propertyValuesHolder>
+</objectAnimator>
\ No newline at end of file
diff --git a/v17/leanback/res/animator/lb_playback_now_bar2_animator.xml b/v17/leanback/res/animator/lb_playback_now_bar2_animator.xml
new file mode 100644
index 0000000..9b583b9
--- /dev/null
+++ b/v17/leanback/res/animator/lb_playback_now_bar2_animator.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+                android:duration="2080"
+                android:repeatCount="infinite"
+                android:interpolator="@android:anim/linear_interpolator">
+    <propertyValuesHolder android:propertyName="scaleY" >
+
+        <keyframe android:value="1.0" />
+        <keyframe android:value="0.917" />
+        <keyframe android:value="0.833" />
+        <keyframe android:value="0.917" />
+        <keyframe android:value="1.0" />
+
+        <keyframe android:value="0.917" />
+        <keyframe android:value="0.75" />
+        <keyframe android:value="0.583" />
+        <keyframe android:value="0.75" />
+        <keyframe android:value="0.917" />
+
+        <keyframe android:value="1.0" />
+        <keyframe android:value="0.833" />
+        <keyframe android:value="0.667" />
+        <keyframe android:value="0.833" />
+        <keyframe android:value="1.0" />
+
+        <keyframe android:value="0.917" />
+        <keyframe android:value="0.75" />
+        <keyframe android:value="0.417" />
+        <keyframe android:value="0.25" />
+        <keyframe android:value="0.417" />
+
+        <keyframe android:value="0.667" />
+        <keyframe android:value="0.833" />
+        <keyframe android:value="1.0" />
+        <keyframe android:value="0.833" />
+        <keyframe android:value="0.75" />
+
+        <keyframe android:value="0.667" />
+        <keyframe android:value="1.0" />
+
+    </propertyValuesHolder>
+</objectAnimator>
\ No newline at end of file
diff --git a/v17/leanback/res/animator/lb_playback_now_bar3_animator.xml b/v17/leanback/res/animator/lb_playback_now_bar3_animator.xml
new file mode 100644
index 0000000..c842100
--- /dev/null
+++ b/v17/leanback/res/animator/lb_playback_now_bar3_animator.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+                android:duration="2000"
+                android:repeatCount="infinite"
+                android:interpolator="@android:anim/linear_interpolator">
+    <propertyValuesHolder android:propertyName="scaleY" >
+
+        <keyframe android:value="0.667" />
+        <keyframe android:value="0.75" />
+        <keyframe android:value="0.833" />
+        <keyframe android:value="1.0" />
+        <keyframe android:value="0.917" />
+
+        <keyframe android:value="0.75" />
+        <keyframe android:value="0.583" />
+        <keyframe android:value="0.417" />
+        <keyframe android:value="0.583" />
+        <keyframe android:value="0.667" />
+
+        <keyframe android:value="0.75" />
+        <keyframe android:value="1.0" />
+        <keyframe android:value="0.917" />
+        <keyframe android:value="1.0" />
+        <keyframe android:value="0.75" />
+
+        <keyframe android:value="0.583" />
+        <keyframe android:value="0.75" />
+        <keyframe android:value="0.917" />
+        <keyframe android:value="1.0" />
+        <keyframe android:value="0.833" />
+
+        <keyframe android:value="0.667" />
+        <keyframe android:value="0.75" />
+        <keyframe android:value="0.583" />
+        <keyframe android:value="0.417" />
+        <keyframe android:value="0.25" />
+
+        <keyframe android:value="0.667" />
+
+    </propertyValuesHolder>
+</objectAnimator>
\ No newline at end of file
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_play_fit.png b/v17/leanback/res/drawable-xhdpi/lb_ic_play_fit.png
new file mode 100644
index 0000000..37d33e7
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_play_fit.png
Binary files differ
diff --git a/v17/leanback/res/drawable/lb_playback_now_playing_bar.xml b/v17/leanback/res/drawable/lb_playback_now_playing_bar.xml
new file mode 100644
index 0000000..33a1c4b
--- /dev/null
+++ b/v17/leanback/res/drawable/lb_playback_now_playing_bar.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <solid android:color="@color/lb_playback_now_playing_bar_color" />
+
+    <size
+            android:height="@dimen/lb_playback_now_playing_bar_height"
+            android:width="@dimen/lb_playback_now_playing_bar_width" />
+
+</shape>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_media_item_number_view_flipper.xml b/v17/leanback/res/layout/lb_media_item_number_view_flipper.xml
new file mode 100644
index 0000000..8625d84
--- /dev/null
+++ b/v17/leanback/res/layout/lb_media_item_number_view_flipper.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+       android:layout_width="match_parent"
+       android:layout_height="match_parent">
+
+    <TextView
+            android:id="@+id/initial"
+            style="?attr/playbackMediaItemNumberStyle"
+    />
+    <ImageView
+            android:id="@+id/paused"
+            android:layout_width="@dimen/lb_playback_play_icon_size"
+            android:layout_height="@dimen/lb_playback_play_icon_size"
+            android:layout_gravity="center_vertical"
+            android:visibility="gone"
+            android:scaleType="fitStart"
+            android:adjustViewBounds="true"
+            android:src="@drawable/lb_ic_play_fit"
+    />
+    <android.support.v17.leanback.widget.MediaNowPlayingView
+            android:id="@+id/playing"
+            android:layout_width="@dimen/lb_playback_now_playing_view_size"
+            android:layout_height="@dimen/lb_playback_now_playing_view_size"
+            android:layout_gravity="center_vertical"
+            android:visibility="gone"/>
+
+
+</merge>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_playback_now_playing_bars.xml b/v17/leanback/res/layout/lb_playback_now_playing_bars.xml
new file mode 100644
index 0000000..d9b30b7
--- /dev/null
+++ b/v17/leanback/res/layout/lb_playback_now_playing_bars.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+       android:layout_width="match_parent"
+       android:layout_height="match_parent">
+
+    <ImageView
+            android:id="@+id/bar1"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_marginTop="@dimen/lb_playback_now_playing_bar_top_margin"
+            android:layout_marginRight="@dimen/lb_playback_now_playing_bar_margin"
+            android:src="@drawable/lb_playback_now_playing_bar"
+    />
+
+    <ImageView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:id="@+id/bar2"
+            android:layout_marginTop="@dimen/lb_playback_now_playing_bar_top_margin"
+            android:layout_marginRight="@dimen/lb_playback_now_playing_bar_margin"
+            android:src="@drawable/lb_playback_now_playing_bar"
+    />
+
+    <ImageView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:id="@+id/bar3"
+            android:layout_marginTop="@dimen/lb_playback_now_playing_bar_top_margin"
+            android:src="@drawable/lb_playback_now_playing_bar"
+    />
+
+</merge>
\ No newline at end of file
diff --git a/v17/leanback/res/layout/lb_row_media_item.xml b/v17/leanback/res/layout/lb_row_media_item.xml
index 72efb09..b25e922 100644
--- a/v17/leanback/res/layout/lb_row_media_item.xml
+++ b/v17/leanback/res/layout/lb_row_media_item.xml
@@ -46,9 +46,9 @@
                     style="?attr/playbackMediaItemDetailsStyle"
             >
 
-                <TextView
-                        android:id="@+id/mediaItemNumber"
-                        style="?attr/playbackMediaItemNumberStyle"/>
+                <ViewFlipper android:id="@+id/mediaItemNumberViewFlipper"
+                             style="?attr/playbackMediaItemNumberViewFlipperStyle">
+                </ViewFlipper>
 
 
                 <TextView
diff --git a/v17/leanback/res/layout/lb_title_view.xml b/v17/leanback/res/layout/lb_title_view.xml
index e1c79ef..762da41 100644
--- a/v17/leanback/res/layout/lb_title_view.xml
+++ b/v17/leanback/res/layout/lb_title_view.xml
@@ -29,7 +29,7 @@
 
     <TextView
         android:id="@+id/title_text"
-        android:layout_width="@dimen/lb_browse_title_text_width"
+        android:layout_width="wrap_content"
         android:layout_height="@dimen/lb_browse_title_height"
         android:layout_gravity="center_vertical|end"
         style="?attr/browseTitleTextStyle"/>
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index 3a0ca19..1ff51f7 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -283,6 +283,8 @@
 
         <attr name="playbackMediaListHeaderTitleStyle" format="reference"/>
         <attr name="playbackMediaItemDetailsStyle" format="reference"/>
+        <attr name="playbackMediaItemNumberViewFlipperStyle" format="reference"/>
+        <attr name="playbackMediaItemNumberViewFlipperLayout" format="reference"/>
         <attr name="playbackMediaItemNumberStyle" format="reference"/>
         <attr name="playbackMediaItemNameStyle" format="reference"/>
         <attr name="playbackMediaItemDurationStyle" format="reference"/>
diff --git a/v17/leanback/res/values/colors.xml b/v17/leanback/res/values/colors.xml
index bbae9ad..ec9102c 100644
--- a/v17/leanback/res/values/colors.xml
+++ b/v17/leanback/res/values/colors.xml
@@ -68,6 +68,7 @@
     <color name="lb_playback_controls_time_text_color">#B2EEEEEE</color>
     <color name="lb_playback_media_row_highlight_color">#1AFFFFFF</color>
     <color name="lb_playback_media_row_separator_highlight_color">#1AFFFFFF</color>
+    <color name="lb_playback_now_playing_bar_color">#FFEEEEEE</color>
 
     <color name="lb_search_plate_hint_text_color">#FFCCCCCC</color>
 
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index c62c281..f6ef557 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -30,7 +30,6 @@
     <dimen name="lb_browse_title_icon_max_width">584dp</dimen>
     <dimen name="lb_browse_title_icon_height">60dp</dimen>
     <dimen name="lb_browse_title_text_size">44sp</dimen>
-    <dimen name="lb_browse_title_text_width">584dp</dimen>
 
     <dimen name="lb_browse_headers_width">270dp</dimen>
     <integer name="lb_browse_headers_transition_delay">150</integer>
@@ -149,6 +148,13 @@
     <dimen name="lb_playback_media_row_radio_selector_width">72dp</dimen>
     <dimen name="lb_playback_media_row_selector_round_rect_radius">36dp</dimen>
     <dimen name="lb_playback_media_row_separator_height">1dp</dimen>
+    <dimen name="lb_playback_now_playing_bar_top_margin">3dp</dimen>
+    <dimen name="lb_playback_now_playing_bar_left_margin">3dp</dimen>
+    <dimen name="lb_playback_now_playing_bar_width">5dp</dimen>
+    <dimen name="lb_playback_now_playing_bar_height">18dp</dimen>
+    <dimen name="lb_playback_now_playing_bar_margin">1dp</dimen>
+    <dimen name="lb_playback_now_playing_view_size">28dp</dimen>
+    <dimen name="lb_playback_play_icon_size">14dp</dimen>
 
     <dimen name="lb_control_button_diameter">90dp</dimen>
     <dimen name="lb_control_button_height">64dp</dimen>
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index 39e1413..8bd9b79 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -353,6 +353,13 @@
         <item name="android:textAppearance">@style/TextAppearance.Leanback.PlaybackMediaListHeaderTitle</item>
     </style>
 
+    <style name="Widget.Leanback.PlaybackMediaItemNumberViewFlipperStyle">
+        <item name="android:layout_width">56dp</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:gravity">center_vertical</item>
+        <item name="android:visibility">gone</item>
+    </style>
+
     <style name="TextAppearance.Leanback.PlaybackMediaItemNumber">
         <item name="android:textColor">#FFFFFF</item>
         <item name="android:textSize">18sp</item>
@@ -360,7 +367,7 @@
     </style>
 
     <style name="Widget.Leanback.PlaybackMediaItemNumberStyle">
-        <item name="android:layout_width">56dp</item>
+        <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:gravity">center_vertical</item>
         <item name="android:visibility">gone</item>
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index fd93ea8..cf3cbef 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -81,6 +81,8 @@
         <item name="playbackMediaItemSeparatorStyle">@style/Widget.Leanback.PlaybackMediaItemSeparatorStyle</item>
         <item name="playbackMediaListHeaderTitleStyle">@style/Widget.Leanback.PlaybackMediaListHeaderTitleStyle</item>
         <item name="playbackMediaItemDetailsStyle">@style/Widget.Leanback.PlaybackMediaItemDetailsStyle</item>
+        <item name="playbackMediaItemNumberViewFlipperStyle">@style/Widget.Leanback.PlaybackMediaItemNumberViewFlipperStyle</item>
+        <item name="playbackMediaItemNumberViewFlipperLayout">@layout/lb_media_item_number_view_flipper</item>
         <item name="playbackMediaItemNumberStyle">@style/Widget.Leanback.PlaybackMediaItemNumberStyle</item>
         <item name="playbackMediaItemNameStyle">@style/Widget.Leanback.PlaybackMediaItemNameStyle</item>
         <item name="playbackMediaItemDurationStyle">@style/Widget.Leanback.PlaybackMediaItemDurationStyle</item>
diff --git a/v17/leanback/src/android/support/v17/leanback/app/ListRowDataAdapter.java b/v17/leanback/src/android/support/v17/leanback/app/ListRowDataAdapter.java
index de4546c..5f18285 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/ListRowDataAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/ListRowDataAdapter.java
@@ -1,9 +1,11 @@
 package android.support.v17.leanback.app;
 
-import android.support.v17.leanback.widget.DividerRow;
-import android.support.v17.leanback.widget.ListRow;
+import android.os.Handler;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.CursorObjectAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
 
 /**
  * Wrapper class for {@link ObjectAdapter} used by {@link BrowseFragment} to initialize
@@ -17,62 +19,67 @@
  * bounds to reflect the latest data.
  */
 class ListRowDataAdapter extends ObjectAdapter {
+    public static final int ON_ITEM_RANGE_CHANGED = 2;
+    public static final int ON_ITEM_RANGE_INSERTED = 4;
+    public static final int ON_ITEM_RANGE_REMOVED = 8;
+    public static final int ON_CHANGED = 16;
+
     private final ObjectAdapter mAdapter;
     private int mLastVisibleRowIndex;
+    private Handler mHandler;
+    private Update mPendingUpdate;
+    private int mPendingUpdateCount;
+
+    private static class Update {
+        int eventType;
+        int positionStart;
+        int itemCount;
+
+        public Update(int type, int positionStart, int itemCount) {
+            this.eventType = type;
+            this.positionStart = positionStart;
+            this.itemCount = itemCount;
+        }
+    }
+
+    private Runnable notificationTask = new Runnable() {
+        @Override
+        public void run() {
+            if (mPendingUpdateCount == 0) {
+                return;
+            } else if (mPendingUpdateCount == 1 && mPendingUpdate != null) {
+                doNotify(
+                        mPendingUpdate.eventType,
+                        mPendingUpdate.positionStart,
+                        mPendingUpdate.itemCount);
+            } else {
+                notifyChanged();
+            }
+            mPendingUpdate = null;
+            mPendingUpdateCount = 0;
+        }
+    };
 
     public ListRowDataAdapter(ObjectAdapter adapter) {
         super(adapter.getPresenterSelector());
         this.mAdapter = adapter;
         initialize();
-        mAdapter.registerObserver(new DataObserver() {
 
-            @Override
-            public void onItemRangeChanged(int positionStart, int itemCount) {
-                if (positionStart <= mLastVisibleRowIndex) {
-                    notifyItemRangeChanged(positionStart,
-                            Math.min(itemCount, mLastVisibleRowIndex - positionStart + 1));
-                }
-            }
-
-            @Override
-            public void onItemRangeInserted(int positionStart, int itemCount) {
-                if (positionStart <= mLastVisibleRowIndex) {
-                    mLastVisibleRowIndex += itemCount;
-                    notifyItemRangeInserted(positionStart, itemCount);
-                    return;
-                }
-
-                int lastVisibleRowIndex = mLastVisibleRowIndex;
-                initialize();
-                if (mLastVisibleRowIndex > lastVisibleRowIndex) {
-                    int totalItems = mLastVisibleRowIndex - lastVisibleRowIndex;
-                    notifyItemRangeInserted(lastVisibleRowIndex + 1, totalItems);
-                }
-            }
-
-            @Override
-            public void onItemRangeRemoved(int positionStart, int itemCount) {
-                if (positionStart + itemCount -  1 < mLastVisibleRowIndex) {
-                    mLastVisibleRowIndex -= itemCount;
-                    notifyItemRangeRemoved(positionStart, itemCount);
-                    return;
-                }
-
-                int lastVisibleRowIndex = mLastVisibleRowIndex;
-                initialize();
-                int totalItems = lastVisibleRowIndex - mLastVisibleRowIndex;
-                if (totalItems > 0) {
-                    notifyItemRangeRemoved(
-                            Math.min(lastVisibleRowIndex + 1, positionStart), totalItems);
-                }
-            }
-
-            @Override
-            public void onChanged() {
-                initialize();
-                notifyChanged();
-            }
-        });
+        // If an user implements its own ObjectAdapter, notification corresponding to data
+        // updates can be batched e.g. remove, add might be followed by notifyRemove, notifyAdd.
+        // But underlying data would have changed during the notifyRemove call by the previous add
+        // operation. To handle this case, we enqueue the updates in a queue and have a worker
+        // service that queue. The common case will be to have a single pending update in the queue.
+        // But in case the worker encounters multiple updates in the queue, it will send a
+        // notifyChanged() call to RecyclerView forcing it to do a full refresh.
+        if ((adapter instanceof ArrayObjectAdapter)
+                || (adapter instanceof CursorObjectAdapter)
+                || (adapter instanceof SparseArrayObjectAdapter)) {
+            mAdapter.registerObserver(new SimpleDataObserver());
+        } else {
+            mHandler = new Handler();
+            mAdapter.registerObserver(new QueueBasedDataObserver());
+        }
     }
 
     private void initialize() {
@@ -96,4 +103,120 @@
     public Object get(int index) {
         return mAdapter.get(index);
     }
-}
\ No newline at end of file
+
+    private void doNotify(int eventType, int positionStart, int itemCount) {
+        switch (eventType) {
+            case ON_ITEM_RANGE_CHANGED:
+                notifyItemRangeChanged(positionStart, itemCount);
+                break;
+            case ON_ITEM_RANGE_INSERTED:
+                notifyItemRangeInserted(positionStart, itemCount);
+                break;
+            case ON_ITEM_RANGE_REMOVED:
+                notifyItemRangeRemoved(positionStart, itemCount);
+                break;
+            case ON_CHANGED:
+                notifyChanged();
+            default:
+                throw new IllegalArgumentException("Invalid event type " + eventType);
+        }
+    }
+
+    private class SimpleDataObserver extends DataObserver {
+
+        @Override
+        public void onItemRangeChanged(int positionStart, int itemCount) {
+            if (positionStart <= mLastVisibleRowIndex) {
+                onEventFired(ON_ITEM_RANGE_CHANGED, positionStart,
+                        Math.min(itemCount, mLastVisibleRowIndex - positionStart + 1));
+            }
+        }
+
+        @Override
+        public void onItemRangeInserted(int positionStart, int itemCount) {
+            if (positionStart <= mLastVisibleRowIndex) {
+                mLastVisibleRowIndex += itemCount;
+                onEventFired(ON_ITEM_RANGE_INSERTED, positionStart, itemCount);
+                return;
+            }
+
+            int lastVisibleRowIndex = mLastVisibleRowIndex;
+            initialize();
+            if (mLastVisibleRowIndex > lastVisibleRowIndex) {
+                int totalItems = mLastVisibleRowIndex - lastVisibleRowIndex;
+                onEventFired(ON_ITEM_RANGE_INSERTED, lastVisibleRowIndex + 1, totalItems);
+            }
+        }
+
+        @Override
+        public void onItemRangeRemoved(int positionStart, int itemCount) {
+            if (positionStart + itemCount - 1 < mLastVisibleRowIndex) {
+                mLastVisibleRowIndex -= itemCount;
+                onEventFired(ON_ITEM_RANGE_REMOVED, positionStart, itemCount);
+                return;
+            }
+
+            int lastVisibleRowIndex = mLastVisibleRowIndex;
+            initialize();
+            int totalItems = lastVisibleRowIndex - mLastVisibleRowIndex;
+            if (totalItems > 0) {
+                onEventFired(ON_ITEM_RANGE_REMOVED,
+                        Math.min(lastVisibleRowIndex + 1, positionStart),
+                        totalItems);
+            }
+        }
+
+        @Override
+        public void onChanged() {
+            initialize();
+            onEventFired(ON_CHANGED, -1, -1);
+        }
+
+        protected void onEventFired(int eventType, int positionStart, int itemCount) {
+            doNotify(eventType, positionStart, itemCount);
+        }
+    }
+
+    private class QueueBasedDataObserver extends SimpleDataObserver {
+
+        @Override
+        public void onItemRangeChanged(int positionStart, int itemCount) {
+            incrementAndPost();
+            super.onItemRangeChanged(positionStart, itemCount);
+        }
+
+        @Override
+        public void onItemRangeInserted(int positionStart, int itemCount) {
+            incrementAndPost();
+            super.onItemRangeInserted(positionStart, itemCount);
+        }
+
+        @Override
+        public void onItemRangeRemoved(int positionStart, int itemCount) {
+            incrementAndPost();
+            super.onItemRangeRemoved(positionStart, itemCount);
+        }
+
+        @Override
+        public void onChanged() {
+            incrementAndPost();
+            super.onChanged();
+        }
+
+        @Override
+        protected void onEventFired(
+                final int eventType, final int positionStart, final int itemCount) {
+
+            if (mPendingUpdateCount == 1) {
+                mPendingUpdate = new Update(eventType, positionStart, itemCount);
+            }
+        }
+
+        private void incrementAndPost() {
+            mPendingUpdateCount++;
+            if (mPendingUpdateCount == 1) {
+                mHandler.post(notificationTask);
+            }
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
index 120ffa7..b896d41 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
@@ -824,6 +824,7 @@
 
     /**
      * Must be called appropriately by a subclass when the playback state has changed.
+     * It updates the playback state displayed on the media player.
      */
     protected void onStateChanged() {
         if (DEBUG) Log.v(TAG, "onStateChanged");
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java
index f0deb2c..937b510 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java
@@ -826,6 +826,7 @@
 
     /**
      * Must be called appropriately by a subclass when the playback state has changed.
+     * It updates the playback state displayed on the media player.
      */
     protected void onStateChanged() {
         if (DEBUG) Log.v(TAG, "onStateChanged");
diff --git a/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
index af2c1b6..27c4099 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
@@ -234,7 +234,6 @@
         }
     };
 
-    @Override
     public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                            int[] grantResults) {
         if (requestCode == AUDIO_PERMISSION_REQUEST_CODE && permissions.length > 0) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
index db88ba9..e744206 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
@@ -20,12 +20,14 @@
 import android.graphics.Rect;
 import android.support.v17.leanback.R;
 import android.support.v4.view.ViewCompat;
+import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.TextView;
+import android.widget.ViewFlipper;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -39,6 +41,14 @@
  * Each media item's details and actions are separately focusable.
  * The appearance of each one of the media row components can be controlled through setting
  * theme's attributes.
+ * Each media item row provides a view flipper for switching between different views depending on
+ * the playback state.
+ * A default layout is provided by this presenter for rendering different playback states, or a
+ * custom layout can be provided by the user by overriding the
+ * playbackMediaItemNumberViewFlipperLayout attribute in the currently specified theme.
+ * Subclasses should also override {@link #getMediaPlayState(Object)} to provide the current play
+ * state of their media item model in case they wish to use different views depending on the
+ * playback state.
  * The presenter can optionally provide line separators between media rows by setting
  * {@link #setHasMediaRowSeparator(boolean)} to true.
  * <p>
@@ -57,6 +67,23 @@
  */
 public abstract class AbstractMediaItemPresenter extends RowPresenter {
 
+    /**
+     * Different playback states of a media item
+     */
+
+    /**
+     * Indicating that the media item is currently neither playing nor paused.
+     */
+    public static final int PLAY_STATE_INITIAL = 0;
+    /**
+     * Indicating that the media item is currently paused.
+     */
+    public static final int PLAY_STATE_PAUSED = 1;
+    /**
+     * Indicating that the media item is currently playing
+     */
+    public static final int PLAY_STATE_PLAYING = 2;
+
     final static Rect sTempRect = new Rect();
     private int mBackgroundColor = Color.TRANSPARENT;
     private boolean mBackgroundColorSet;
@@ -129,7 +156,11 @@
         private final View mMediaRowView;
         private final View mSelectorView;
         private final View mMediaItemDetailsView;
+        private final ViewFlipper mMediaItemNumberViewFlipper;
         private final TextView mMediaItemNumberView;
+        private final View mMediaItemPausedView;
+
+        private final View mMediaItemPlayingView;
         private final TextView mMediaItemNameView;
         private final TextView mMediaItemDurationView;
         private final View mMediaItemRowSeparator;
@@ -144,7 +175,6 @@
             mSelectorView = view.findViewById(R.id.mediaRowSelector);
             mMediaRowView  = view.findViewById(R.id.mediaItemRow);
             mMediaItemDetailsView = view.findViewById(R.id.mediaItemDetails);
-            mMediaItemNumberView = (TextView) view.findViewById(R.id.mediaItemNumber);
             mMediaItemNameView = (TextView) view.findViewById(R.id.mediaItemName);
             mMediaItemDurationView = (TextView) view.findViewById(R.id.mediaItemDuration);
             mMediaItemRowSeparator = view.findViewById(R.id.mediaRowSeparator);
@@ -167,7 +197,20 @@
                             true);
                 }
             });
+            mMediaItemNumberViewFlipper =
+                    (ViewFlipper) view.findViewById(R.id.mediaItemNumberViewFlipper);
 
+            TypedValue typedValue = new TypedValue();
+            boolean found = view.getContext().getTheme().resolveAttribute(
+                    R.attr.playbackMediaItemNumberViewFlipperLayout, typedValue, true);
+            View mergeView = LayoutInflater.from(view.getContext()).
+                    inflate(found ? typedValue.resourceId :
+                            R.layout.lb_media_item_number_view_flipper,
+                            mMediaItemNumberViewFlipper, true);
+
+            mMediaItemNumberView = (TextView) mergeView.findViewById(R.id.initial);
+            mMediaItemPausedView = mergeView.findViewById(R.id.paused);
+            mMediaItemPlayingView = mergeView.findViewById(R.id.playing);
         }
 
         /**
@@ -271,6 +314,16 @@
             mRowPresenter.onBindMediaDetails(this, getRowObject());
         }
 
+        /**
+         * Notifies the playback state of the media item row has changed. This in turn triggers
+         * updating of the UI for that media item row if corresponding views are specified for each
+         * playback state.
+         * By default, 3 views are provided for each playback state, or these views can be provided
+         * by the user.
+         */
+        public void notifyPlayStateChanged() {
+            mRowPresenter.onBindMediaPlayState(this);
+        }
 
         /**
          * @return The SelectorView responsible for highlighting the in-focus view within each
@@ -281,28 +334,65 @@
         }
 
         /**
-         * @return The TextView responsible for rendering the track number
+         * @return The FlipperView responsible for flipping between different media item number
+         * views depending on the playback state
+         */
+        public ViewFlipper getMediaItemNumberViewFlipper() {
+            return mMediaItemNumberViewFlipper;
+        }
+
+        /**
+         * @return The TextView responsible for rendering the media item number.
+         * This view is rendered when the media item row is neither playing nor paused.
          */
         public TextView getMediaItemNumberView() {
             return mMediaItemNumberView;
         }
 
         /**
-         * @return The TextView responsible for rendering the track name
+         * @return The view rendered when the media item row is paused.
+         */
+        public View getMediaItemPausedView() {
+            return mMediaItemPausedView;
+        }
+
+        /**
+         * @return The view rendered when the media item row is playing.
+         */
+        public View getMediaItemPlayingView() {
+            return mMediaItemPlayingView;
+        }
+
+
+        /**
+         * Flips to the view at index 'position' specified in the layout file for media item number
+         * view.
+         * @param position The position of the new view within the layout file to which the media
+         *                 item number view is flipped.
+         */
+        public void setSelectedMediaItemNumberView(int position) {
+            if (position >= 0 & position < mMediaItemNumberViewFlipper.getChildCount()) {
+                mMediaItemNumberViewFlipper.setDisplayedChild(position);
+            }
+        }
+        /**
+         * Returns the view displayed when the media item is neither playing nor paused,
+         * corresponding to the playback state of PLAY_STATE_INITIAL.
+         * @return The TextView responsible for rendering the media item name.
          */
         public TextView getMediaItemNameView() {
             return mMediaItemNameView;
         }
 
         /**
-         * @return The TextView responsible for rendering the track duration
+         * @return The TextView responsible for rendering the media item duration
          */
         public TextView getMediaItemDurationView() {
             return mMediaItemDurationView;
         }
 
         /**
-         * @return The view container of track details
+         * @return The view container of media item details
          */
         public View getMediaItemDetailsView() {
             return mMediaItemDetailsView;
@@ -322,6 +412,9 @@
             return mMediaItemActionsContainer;
         }
 
+        /**
+         * @return Array of MultiActions displayed for this media item row
+         */
         public MultiActionsProvider.MultiAction[] getMediaItemRowActions() {
             return mMediaItemRowActions;
         }
@@ -364,6 +457,7 @@
         mvh.getMediaItemRowSeparator().setVisibility(hasMediaRowSeparator() ? View.VISIBLE :
                 View.GONE);
 
+        onBindMediaPlayState(mvh);
         onBindMediaDetails((ViewHolder) vh, item);
     }
 
@@ -422,6 +516,60 @@
     }
 
     /**
+     * Binds the media item number view to the appropriate play state view of the media item.
+     * The play state of the media item is extracted by calling {@link #getMediaPlayState(Object)} for
+     * the media item embedded within this view.
+     * This method triggers updating of the playback state UI if corresponding views are specified
+     * for the current playback state.
+     * By default, 3 views are provided for each playback state, or these views can be provided
+     * by the user.
+     */
+    public void onBindMediaPlayState(ViewHolder vh) {
+        int childIndex = calculateMediaItemNumberFlipperIndex(vh);
+        if (childIndex != -1 && vh.mMediaItemNumberViewFlipper.getDisplayedChild() != childIndex) {
+            vh.mMediaItemNumberViewFlipper.setDisplayedChild(childIndex);
+        }
+    }
+
+    static int calculateMediaItemNumberFlipperIndex(ViewHolder vh) {
+        int childIndex = -1;
+        int newPlayState = vh.mRowPresenter.getMediaPlayState(vh.getRowObject());
+        switch (newPlayState) {
+            case PLAY_STATE_INITIAL:
+                childIndex = (vh.mMediaItemNumberView == null) ? -1 :
+                        vh.mMediaItemNumberViewFlipper.indexOfChild(vh.mMediaItemNumberView);
+                break;
+            case PLAY_STATE_PAUSED:
+                childIndex = (vh.mMediaItemPausedView == null) ? -1 :
+                        vh.mMediaItemNumberViewFlipper.indexOfChild(vh.mMediaItemPausedView);
+                break;
+            case PLAY_STATE_PLAYING:
+                childIndex = (vh.mMediaItemPlayingView == null) ? -1 :
+                        vh.mMediaItemNumberViewFlipper.indexOfChild(vh.mMediaItemPlayingView);
+        }
+        return childIndex;
+    }
+
+    /**
+     * Unbinds the play state view from the given ViewHolder
+     * @param vh The ViewHolder to unbind from
+     */
+    public void onUnbindMediaPlayState(ViewHolder vh) {
+    }
+
+    /**
+     * Returns the current play state of the given media item. By default, this method returns
+     * PLAY_STATE_INITIAL which causes the media item number
+     * {@link ViewHolder#getMediaItemNameView()} to be displayed for different
+     * playback states. Users of this class should override this method in order to provide the
+     * play state of their custom media item data model.
+     * @param item The media item
+     * @return The current play state of this media item
+     */
+    protected int getMediaPlayState(Object item) {
+        return PLAY_STATE_INITIAL;
+    }
+    /**
      * Each media item row can have multiple focusable elements; the details on the left and a set
      * of optional custom actions on the right.
      * The selector is a highlight that moves to highlight to cover whichever views is in focus.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/MediaNowPlayingView.java b/v17/leanback/src/android/support/v17/leanback/widget/MediaNowPlayingView.java
new file mode 100644
index 0000000..d619d8c
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/MediaNowPlayingView.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT 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.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.support.v17.leanback.R;
+
+/**
+ * The view displaying 3 animated peak meters next to each other when a media item is playing.
+ */
+public class MediaNowPlayingView extends LinearLayout{
+
+    private final ImageView mImage1;
+    private final ImageView mImage2;
+    private final ImageView mImage3;
+    private final ObjectAnimator mObjectAnimator1;
+    private final ObjectAnimator mObjectAnimator2;
+    private final ObjectAnimator mObjectAnimator3;
+
+    public MediaNowPlayingView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        LayoutInflater.from(context).inflate(R.layout.lb_playback_now_playing_bars, this, true);
+        mImage1 = (ImageView) findViewById(R.id.bar1);
+        mImage2 = (ImageView) findViewById(R.id.bar2);
+        mImage3 = (ImageView) findViewById(R.id.bar3);
+
+        mImage1.setPivotY(mImage1.getDrawable().getIntrinsicHeight());
+        mImage2.setPivotY(mImage2.getDrawable().getIntrinsicHeight());
+        mImage3.setPivotY(mImage3.getDrawable().getIntrinsicHeight());
+
+        setDropScale(mImage1);
+        setDropScale(mImage2);
+        setDropScale(mImage3);
+
+        mObjectAnimator1 = (ObjectAnimator) AnimatorInflater.loadAnimator(context,
+                R.animator.lb_playback_now_bar1_animator);
+        mObjectAnimator1.setTarget(mImage1);
+
+        mObjectAnimator2 = (ObjectAnimator) AnimatorInflater.loadAnimator(context,
+                R.animator.lb_playback_now_bar2_animator);
+        mObjectAnimator2.setTarget(mImage2);
+
+        mObjectAnimator3 = (ObjectAnimator) AnimatorInflater.loadAnimator(context,
+                R.animator.lb_playback_now_bar3_animator);
+        mObjectAnimator3.setTarget(mImage3);
+    }
+
+    static void setDropScale(View view) {
+        view.setScaleY(1f / 12f);
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        super.setVisibility(visibility);
+        if (visibility == View.GONE) {
+            stopAnimation();
+        } else {
+            startAnimation();
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (getVisibility() == View.VISIBLE)
+            startAnimation();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        stopAnimation();
+    }
+
+    private void startAnimation() {
+        startAnimation(mObjectAnimator1);
+        startAnimation(mObjectAnimator2);
+        startAnimation(mObjectAnimator3);
+        mImage1.setVisibility(View.VISIBLE);
+        mImage2.setVisibility(View.VISIBLE);
+        mImage3.setVisibility(View.VISIBLE);
+    }
+
+    private void stopAnimation() {
+        stopAnimation(mObjectAnimator1, mImage1);
+        stopAnimation(mObjectAnimator2, mImage2);
+        stopAnimation(mObjectAnimator3, mImage3);
+        mImage1.setVisibility(View.GONE);
+        mImage2.setVisibility(View.GONE);
+        mImage3.setVisibility(View.GONE);
+    }
+
+    private void startAnimation(Animator animator) {
+        if (!animator.isStarted()) {
+            animator.start();
+        }
+    }
+
+    private void stopAnimation(Animator animator, View view) {
+        if (animator.isStarted()) {
+            animator.cancel();
+            setDropScale(view);
+        }
+    }
+}
diff --git a/v7/appcompat/res/drawable-mdpi/abc_textfield_default_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_textfield_default_mtrl_alpha.9.png
index 0eb61f1..5d7ad2f 100644
--- a/v7/appcompat/res/drawable-mdpi/abc_textfield_default_mtrl_alpha.9.png
+++ b/v7/appcompat/res/drawable-mdpi/abc_textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java b/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java
index b6dad92..fa4463b 100644
--- a/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java
+++ b/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java
@@ -109,7 +109,7 @@
             return;
         }
 
-        View checkboxView = view.findViewById(android.support.v7.appcompat.R.id.checkbox);
+        View checkboxView = view.findViewById(android.R.id.checkbox);
         syncCheckboxView(checkboxView);
 
         View summaryView = view.findViewById(android.R.id.summary);