Merge "Ensure Compat MediaStyle Large Icons are shown pre-API 21" into mnc-ub-dev
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 2926606..3fb8b65 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -309,6 +309,7 @@
                             0, ViewCompat.getPaddingEnd(mEditText), mEditText.getPaddingBottom());
                 }
             } else {
+                updateEditTextBackground(false);
                 removeView(mErrorView);
                 mErrorView = null;
             }
@@ -364,11 +365,8 @@
                     .start();
 
             // Set the EditText's background tint to the error color
-            ViewCompat.setBackgroundTintList(mEditText,
-                    ColorStateList.valueOf(mErrorView.getCurrentTextColor()));
-
+            updateEditTextBackground(true);
             updateLabelVisibility(true);
-
         } else {
             if (mErrorView.getVisibility() == VISIBLE) {
                 ViewCompat.animate(mErrorView)
@@ -385,15 +383,25 @@
                         }).start();
 
                 // Restore the 'original' tint, using colorControlNormal and colorControlActivated
-                final TintManager tintManager = TintManager.get(getContext());
-                ViewCompat.setBackgroundTintList(mEditText,
-                        tintManager.getTintList(R.drawable.abc_edit_text_material));
+                updateEditTextBackground(false);
             }
         }
 
         sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
     }
 
+    private void updateEditTextBackground(boolean errorShown) {
+        if (errorShown && mErrorView != null) {
+            // Set the EditText's background tint to the error color
+            ViewCompat.setBackgroundTintList(mEditText,
+                    ColorStateList.valueOf(mErrorView.getCurrentTextColor()));
+        } else {
+            final TintManager tintManager = TintManager.get(getContext());
+            ViewCompat.setBackgroundTintList(mEditText,
+                    tintManager.getTintList(R.drawable.abc_edit_text_material));
+        }
+    }
+
     /**
      * Returns the error message that was set to be displayed with
      * {@link #setError(CharSequence)}, or <code>null</code> if no error was set
diff --git a/v4/java/android/support/v4/widget/SwipeRefreshLayout.java b/v4/java/android/support/v4/widget/SwipeRefreshLayout.java
index d6b35ef..4daab70 100644
--- a/v4/java/android/support/v4/widget/SwipeRefreshLayout.java
+++ b/v4/java/android/support/v4/widget/SwipeRefreshLayout.java
@@ -728,7 +728,8 @@
 
     @Override
     public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
-        if (isEnabled() && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0) {
+        if (isEnabled() && !mReturningToStart && !canChildScrollUp() && !mRefreshing
+                && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0) {
             // Dispatch up to the nested parent
             startNestedScroll(nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL);
             return true;
@@ -800,16 +801,6 @@
     // NestedScrollingChild
 
     @Override
-    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
-        return false;
-    }
-
-    @Override
-    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
-        return false;
-    }
-
-    @Override
     public void setNestedScrollingEnabled(boolean enabled) {
         mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled);
     }
@@ -847,6 +838,18 @@
     }
 
     @Override
+    public boolean onNestedPreFling(View target, float velocityX,
+            float velocityY) {
+        return dispatchNestedPreFling(velocityX, velocityY);
+    }
+
+    @Override
+    public boolean onNestedFling(View target, float velocityX, float velocityY,
+            boolean consumed) {
+        return dispatchNestedFling(velocityX, velocityY, consumed);
+    }
+
+    @Override
     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
         return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
     }
@@ -1020,18 +1023,6 @@
         mCircleView.startAnimation(mAnimateToCorrectPosition);
     }
 
-    private void peek(int from, AnimationListener listener) {
-        mFrom = from;
-        mPeek.reset();
-        mPeek.setDuration(500);
-        mPeek.setInterpolator(mDecelerateInterpolator);
-        if (listener != null) {
-            mCircleView.setAnimationListener(listener);
-        }
-        mCircleView.clearAnimation();
-        mCircleView.startAnimation(mPeek);
-    }
-
     private void animateOffsetToStartPosition(int from, AnimationListener listener) {
         if (mScale) {
             // Scale the item back down
@@ -1066,23 +1057,6 @@
         }
     };
 
-    private final Animation mPeek = new Animation() {
-        @Override
-        public void applyTransformation(float interpolatedTime, Transformation t) {
-            int targetTop = 0;
-            int endTarget = 0;
-            if (!mUsingCustomStart) {
-                endTarget = (int) (mSpinnerFinalOffset - Math.abs(mOriginalOffsetTop));
-            } else {
-                endTarget = (int) mSpinnerFinalOffset; //mSpinnerFinalOffset;
-            }
-            targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));
-            int offset = targetTop - mCircleView.getTop();
-            setTargetOffsetTopAndBottom(offset, false /* requires update */);
-            mProgress.setArrowScale(1 - interpolatedTime);
-        }
-    };
-
     private void moveToStart(float interpolatedTime) {
         int targetTop = 0;
         targetTop = (mFrom + (int) ((mOriginalOffsetTop - mFrom) * interpolatedTime));
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index 298196f..1b3d8c5 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -111,8 +111,10 @@
 
     <!-- Base platform-dependent theme providing an action bar in a dark-themed activity. -->
     <style name="Base.V7.Theme.AppCompat" parent="Platform.AppCompat">
+        <item name="windowNoTitle">false</item>
         <item name="windowActionBar">true</item>
         <item name="windowActionBarOverlay">false</item>
+        <item name="windowActionModeOverlay">false</item>
         <item name="actionBarPopupTheme">@null</item>
 
         <!-- Used by MediaRouter -->
@@ -262,8 +264,10 @@
 
     <!-- Base platform-dependent theme providing an action bar in a light-themed activity. -->
     <style name="Base.V7.Theme.AppCompat.Light" parent="Platform.AppCompat.Light">
+        <item name="windowNoTitle">false</item>
         <item name="windowActionBar">true</item>
         <item name="windowActionBarOverlay">false</item>
+        <item name="windowActionModeOverlay">false</item>
         <item name="actionBarPopupTheme">@null</item>
 
         <!-- Used by MediaRouter -->
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java b/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
index 7a84b08..7b68805 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
@@ -704,7 +704,9 @@
         @Override
         public void onDismiss() {
             super.onDismiss();
-            mMenu.close();
+            if (mMenu != null) {
+                mMenu.close();
+            }
             mOverflowPopup = null;
         }
     }
diff --git a/v7/mediarouter/res/layout/mr_chooser_list_item.xml b/v7/mediarouter/res/layout/mr_chooser_list_item.xml
index a363ab0..d578560 100644
--- a/v7/mediarouter/res/layout/mr_chooser_list_item.xml
+++ b/v7/mediarouter/res/layout/mr_chooser_list_item.xml
@@ -30,7 +30,7 @@
 
     <LinearLayout android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
-                  android:layout_marginBottom="2.5dp"
+                  android:layout_marginBottom="1dp"
                   android:orientation="vertical" >
 
         <TextView android:id="@+id/mr_chooser_route_name"
diff --git a/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml b/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
index 153d5df..9b9d05c 100644
--- a/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
+++ b/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
@@ -15,14 +15,14 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
+              android:layout_width="fill_parent"
               android:layout_height="wrap_content"
               android:orientation="vertical">
     <LinearLayout android:id="@+id/mr_title_bar"
-                  android:layout_width="match_parent"
+                  android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
                   android:paddingLeft="24dp"
-                  android:paddingRight="24dp"
+                  android:paddingRight="12dp"
                   android:orientation="horizontal" >
         <TextView android:id="@+id/mr_name"
                   android:layout_width="0dp"
@@ -33,64 +33,56 @@
                   android:ellipsize="end"
                   android:textAppearance="?attr/mediaRouteControllerTitleTextStyle" />
         <ImageButton android:id="@+id/mr_close"
-                     android:layout_width="24dp"
-                     android:layout_height="24dp"
-                     android:layout_marginLeft="12dp"
+                     android:layout_width="48dp"
+                     android:layout_height="48dp"
                      android:layout_gravity="center_vertical"
                      android:contentDescription="@string/mr_controller_close_description"
                      android:src="?attr/mediaRouteCloseDrawable"
                      android:background="?attr/selectableItemBackgroundBorderless" />
     </LinearLayout>
     <FrameLayout android:id="@+id/mr_custom_control"
-                 android:layout_width="match_parent"
+                 android:layout_width="fill_parent"
                  android:layout_height="wrap_content"
                  android:visibility="gone" />
-    <ImageView android:id="@+id/mr_art"
-               android:layout_width="match_parent"
-               android:layout_height="wrap_content"
-               android:adjustViewBounds="true"
-               android:scaleType="fitCenter"
-               android:background="?attr/colorPrimary"
-               android:visibility="gone" />
-    <LinearLayout android:id="@+id/mr_control"
-                  android:layout_width="fill_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="vertical"
-                  android:paddingTop="16dp"
-                  android:paddingBottom="16dp"
-                  android:background="?attr/colorPrimary">
-        <include android:id="@+id/mr_playback_control"
-                 layout="@layout/mr_playback_control" />
-        <View android:id="@+id/mr_control_divider"
-              android:layout_width="fill_parent"
-              android:layout_height="8dp"
-              android:visibility="gone"
-              android:background="?attr/colorPrimary" />
-        <include android:id="@+id/mr_volume_control"
-                 layout="@layout/mr_volume_control" />
-    </LinearLayout>
-    <ListView android:id="@+id/mr_volume_group_list"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:background="?attr/colorPrimaryDark"
-              android:visibility="gone" />
-    <LinearLayout android:id="@+id/mr_buttons"
-                  android:layout_width="fill_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="horizontal">
-        <Button android:id="@+id/mr_button_disconnect"
-                android:layout_width="0dp"
-                android:layout_height="48dp"
-                android:gravity="center"
-                android:background="?attr/selectableItemBackgroundBorderless"
-                android:text="@string/mr_controller_disconnect"
-                android:visibility="invisible" />
-        <Button android:id="@+id/mr_button_stop"
-                android:layout_width="0dp"
-                android:layout_height="48dp"
-                android:gravity="center"
-                android:textColor="?attr/colorAccent"
-                android:background="?attr/selectableItemBackgroundBorderless"
-                android:text="@string/mr_controller_stop" />
-    </LinearLayout>
+    <FrameLayout android:id="@+id/mr_default_control"
+                 android:layout_width="fill_parent"
+                 android:layout_height="wrap_content">
+        <ImageView android:id="@+id/mr_art"
+                   android:layout_width="fill_parent"
+                   android:layout_height="wrap_content"
+                   android:adjustViewBounds="true"
+                   android:scaleType="fitXY"
+                   android:background="?attr/colorPrimary"
+                   android:layout_gravity="top"
+                   android:visibility="gone" />
+        <LinearLayout android:id="@+id/mr_media_control"
+                      android:layout_width="fill_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="vertical"
+                      android:layout_gravity="bottom">
+            <LinearLayout android:layout_width="fill_parent"
+                          android:layout_height="wrap_content"
+                          android:orientation="vertical"
+                          android:paddingTop="16dp"
+                          android:paddingBottom="16dp"
+                          android:background="?attr/colorPrimary">
+                <include android:id="@+id/mr_playback_control"
+                         layout="@layout/mr_playback_control" />
+                <View android:id="@+id/mr_control_divider"
+                      android:layout_width="fill_parent"
+                      android:layout_height="8dp"
+                      android:background="?attr/colorPrimary"
+                      android:visibility="gone" />
+                <include android:id="@+id/mr_volume_control"
+                         layout="@layout/mr_volume_control" />
+            </LinearLayout>
+            <ListView android:id="@+id/mr_volume_group_list"
+                      android:layout_width="fill_parent"
+                      android:layout_height="wrap_content"
+                      android:background="?attr/colorPrimaryDark"
+                      android:visibility="gone" />
+        </LinearLayout>
+    </FrameLayout>
+
+    <include layout="@layout/abc_alert_dialog_button_bar_material" />
 </LinearLayout>
diff --git a/v7/mediarouter/res/layout/mr_controller_volume_item.xml b/v7/mediarouter/res/layout/mr_controller_volume_item.xml
index 8202810..501d5ce 100644
--- a/v7/mediarouter/res/layout/mr_controller_volume_item.xml
+++ b/v7/mediarouter/res/layout/mr_controller_volume_item.xml
@@ -17,27 +17,28 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="fill_parent"
               android:layout_height="64dp"
-              android:padding="8dp">
+              android:paddingLeft="24dp"
+              android:paddingRight="60dp"
+              android:paddingTop="8dp"
+              android:paddingBottom="8dp">
     <TextView android:id="@+id/mr_name"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:layout_alignParentTop="true"
-              android:layout_alignParentLeft="true" />
+              android:layout_alignParentLeft="true"
+              android:textAppearance="?attr/mediaRouteControllerSecondaryTextStyle"
+              android:singleLine="true" />
     <ImageView android:id="@+id/mr_volume_item_icon"
-               android:layout_width="48dp"
-               android:layout_height="48dp"
-               android:layout_below="@id/mr_name"
-               android:layout_alignParentLeft="true"
+               android:layout_width="24dp"
+               android:layout_height="24dp"
+               android:layout_marginTop="12dp"
                android:src="?attr/mediaRouteAudioTrackDrawable"
-               android:gravity="center"
-               android:scaleType="center" />
+               android:layout_below="@id/mr_name"
+               android:layout_gravity="center_vertical" />
     <SeekBar android:id="@+id/mr_volume_slider"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
-             android:layout_below="@id/mr_name"
-             android:layout_toRightOf="@id/mr_volume_item_icon"
-             android:layout_gravity="center_vertical"
              android:layout_marginTop="8dp"
-             android:layout_marginLeft="8dp"
-             android:layout_marginRight="8dp" />
+             android:layout_below="@id/mr_name"
+             android:layout_toRightOf="@id/mr_volume_item_icon" />
 </RelativeLayout>
diff --git a/v7/mediarouter/res/layout/mr_playback_control.xml b/v7/mediarouter/res/layout/mr_playback_control.xml
index b619bed..9741dbe 100644
--- a/v7/mediarouter/res/layout/mr_playback_control.xml
+++ b/v7/mediarouter/res/layout/mr_playback_control.xml
@@ -15,27 +15,27 @@
 -->
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_width="match_parent"
+                android:layout_width="fill_parent"
                 android:layout_height="wrap_content"
                 android:orientation="horizontal"
+                android:paddingLeft="24dp"
+                android:paddingRight="12dp"
                 android:background="?attr/colorPrimary">
     <ImageButton android:id="@+id/mr_control_play_pause"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_marginLeft="12dp"
-                 android:layout_marginRight="12dp"
                  android:layout_alignParentRight="true"
-                 android:layout_centerInParent="true"
+                 android:layout_centerVertical="true"
                  android:contentDescription="@string/mr_controller_play"
                  android:background="?attr/selectableItemBackgroundBorderless"
                  android:visibility="gone" />
     <LinearLayout android:orientation="vertical"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
-                  android:layout_marginLeft="24dp"
-                  android:layout_alignParentLeft="true"
                   android:layout_toLeftOf="@id/mr_control_play_pause"
-                  android:layout_centerInParent="true">
+                  android:layout_alignParentLeft="true"
+                  android:layout_centerVertical="true">
         <TextView android:id="@+id/mr_control_title"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
diff --git a/v7/mediarouter/res/layout/mr_volume_control.xml b/v7/mediarouter/res/layout/mr_volume_control.xml
index ab53352..4e0abb8 100644
--- a/v7/mediarouter/res/layout/mr_volume_control.xml
+++ b/v7/mediarouter/res/layout/mr_volume_control.xml
@@ -24,7 +24,7 @@
               android:background="?attr/colorPrimary">
     <ImageView android:layout_width="24dp"
                android:layout_height="24dp"
-               android:src="?attr/mediaRouteCastDrawable"
+               android:src="?attr/mediaRouteAudioTrackDrawable"
                android:gravity="center"
                android:scaleType="center" />
     <SeekBar android:id="@+id/mr_volume_slider"
diff --git a/v7/mediarouter/res/values-sw600dp/dimens.xml b/v7/mediarouter/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..ee07732
--- /dev/null
+++ b/v7/mediarouter/res/values-sw600dp/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- The platform's desired fixed width for a dialog along the major axis
+         (the screen is in landscape). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="mr_dialog_fixed_width_major">60%</item>
+    <!-- The platform's desired fixed width for a dialog along the minor axis
+         (the screen is in portrait). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="mr_dialog_fixed_width_minor">90%</item>
+</resources>
diff --git a/v7/mediarouter/res/values-sw720dp/dimens.xml b/v7/mediarouter/res/values-sw720dp/dimens.xml
new file mode 100644
index 0000000..ab6131f
--- /dev/null
+++ b/v7/mediarouter/res/values-sw720dp/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- The platform's desired fixed width for a dialog along the major axis
+         (the screen is in landscape). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="mr_dialog_fixed_width_major">50%</item>
+    <!-- The platform's desired fixed width for a dialog along the minor axis
+         (the screen is in portrait). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="mr_dialog_fixed_width_minor">70%</item>
+</resources>
diff --git a/v7/mediarouter/res/values/dimens.xml b/v7/mediarouter/res/values/dimens.xml
index 4fb8218..24585c0 100644
--- a/v7/mediarouter/res/values/dimens.xml
+++ b/v7/mediarouter/res/values/dimens.xml
@@ -15,7 +15,11 @@
 -->
 
 <resources>
-    <dimen name="mr_dialog_content_width_portrait">288dp</dimen>
-    <dimen name="mr_dialog_content_width_landscape">356dp</dimen>
-    <dimen name="mr_media_route_controller_art_max_height">320dp</dimen>
+    <!-- The platform's desired fixed width for a dialog along the major axis
+         (the screen is in landscape). This may be either a fraction or a dimension.-->
+    <dimen name="mr_dialog_fixed_width_major">320dp</dimen>
+    <!-- The platform's desired fixed width for a dialog along the minor axis
+         (the screen is in portrait). This may be either a fraction or a dimension.-->
+    <dimen name="mr_dialog_fixed_width_minor">320dp</dimen>
+    <dimen name="mr_controller_volume_group_list_max_height">256dp</dimen>
 </resources>
diff --git a/v7/mediarouter/res/values/themes.xml b/v7/mediarouter/res/values/themes.xml
index da21998..7f46da7 100644
--- a/v7/mediarouter/res/values/themes.xml
+++ b/v7/mediarouter/res/values/themes.xml
@@ -41,6 +41,17 @@
         <item name="mediaRouteCollapseGroupDrawable">@drawable/ic_keyboard_arrow_up_white</item>
     </style>
 
+    <style name="Theme.MediaRouter.LightControlPanel">
+        <item name="mediaRoutePlayDrawable">@drawable/mr_ic_play_light</item>
+        <item name="mediaRoutePauseDrawable">@drawable/mr_ic_pause_light</item>
+        <item name="mediaRouteCastDrawable">@drawable/mr_ic_cast_light</item>
+        <item name="mediaRouteAudioTrackDrawable">@drawable/ic_audiotrack_light</item>
+        <item name="mediaRouteControllerPrimaryTextStyle">@style/Widget.MediaRouter.ControllerText.Primary.Light</item>
+        <item name="mediaRouteControllerSecondaryTextStyle">@style/Widget.MediaRouter.ControllerText.Secondary.Light</item>
+        <item name="mediaRouteExpandGroupDrawable">@drawable/ic_keyboard_arrow_down_black</item>
+        <item name="mediaRouteCollapseGroupDrawable">@drawable/ic_keyboard_arrow_up_black</item>
+    </style>
+
     <style name="Theme.MediaRouter.Light">
         <item name="mediaRouteButtonStyle">@style/Widget.MediaRouter.Light.MediaRouteButton</item>
 
@@ -65,4 +76,15 @@
         <item name="mediaRouteCollapseGroupDrawable">@drawable/ic_keyboard_arrow_up_black</item>
     </style>
 
+    <style name="Theme.MediaRouter.Light.DarkControlPanel">
+        <item name="mediaRoutePlayDrawable">@drawable/mr_ic_play_dark</item>
+        <item name="mediaRoutePauseDrawable">@drawable/mr_ic_pause_dark</item>
+        <item name="mediaRouteCastDrawable">@drawable/mr_ic_cast_dark</item>
+        <item name="mediaRouteAudioTrackDrawable">@drawable/ic_audiotrack</item>
+        <item name="mediaRouteControllerPrimaryTextStyle">@style/Widget.MediaRouter.ControllerText.Primary.Dark</item>
+        <item name="mediaRouteControllerSecondaryTextStyle">@style/Widget.MediaRouter.ControllerText.Secondary.Dark</item>
+        <item name="mediaRouteExpandGroupDrawable">@drawable/ic_keyboard_arrow_down_white</item>
+        <item name="mediaRouteCollapseGroupDrawable">@drawable/ic_keyboard_arrow_up_white</item>
+    </style>
+
 </resources>
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
index 49f4504..282889e 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
@@ -22,8 +22,6 @@
 import android.app.Dialog;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -79,10 +77,6 @@
     private ListView mListView;
     private boolean mAttachedToWindow;
 
-    private int mOrientation;
-    private int mDialogWidthPortrait;
-    private int mDialogWidthLandscape;
-
     public MediaRouteChooserDialog(Context context) {
         this(context, 0);
     }
@@ -166,13 +160,6 @@
 
         setContentView(R.layout.mr_chooser_dialog);
         setTitle(R.string.mr_chooser_title);
-        View decorView = getWindow().getDecorView();
-        int dialogHorizontalPadding = decorView.getPaddingLeft() + decorView.getPaddingRight();
-        Resources res = getContext().getResources();
-        mDialogWidthPortrait = res.getDimensionPixelSize(
-                R.dimen.mr_dialog_content_width_portrait) + dialogHorizontalPadding;
-        mDialogWidthLandscape = res.getDimensionPixelSize(
-                R.dimen.mr_dialog_content_width_landscape) + dialogHorizontalPadding;
 
         mRoutes = new ArrayList<>();
         mAdapter = new RouteAdapter(getContext(), mRoutes);
@@ -180,23 +167,15 @@
         mListView.setAdapter(mAdapter);
         mListView.setOnItemClickListener(mAdapter);
         mListView.setEmptyView(findViewById(android.R.id.empty));
+
+        updateLayout();
     }
 
     /**
-     * Called by {@link MediaRouteChooserDialogFragment} when the device configuration is changed.
+     * Sets the width of the dialog. Also called when configuration changes.
      */
-    void onConfigurationChanged(Configuration newConfig) {
-        onOrientationChanged(newConfig.orientation);
-    }
-
-    private void onOrientationChanged(int orientation) {
-        if (!mAttachedToWindow || mOrientation == orientation) {
-            return;
-        }
-        mOrientation = orientation;
-        getWindow().setLayout(
-                mOrientation == Configuration.ORIENTATION_PORTRAIT
-                        ? mDialogWidthPortrait : mDialogWidthLandscape,
+    void updateLayout() {
+        getWindow().setLayout(MediaRouteDialogHelper.getDialogWidth(getContext()),
                 ViewGroup.LayoutParams.WRAP_CONTENT);
     }
 
@@ -206,7 +185,6 @@
 
         mAttachedToWindow = true;
         mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-        onOrientationChanged(getContext().getResources().getConfiguration().orientation);
         refreshRoutes();
     }
 
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialogFragment.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialogFragment.java
index 4d94c58..835b613 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialogFragment.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialogFragment.java
@@ -119,7 +119,7 @@
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         if (mDialog != null) {
-            mDialog.onConfigurationChanged(newConfig);
+            mDialog.updateLayout();
         }
     }
 }
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
index 20d882e..65bf0e7 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
@@ -20,11 +20,10 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -45,9 +44,11 @@
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
@@ -84,16 +85,18 @@
     // route descriptor.
     private static final int VOLUME_UPDATE_DELAY_MILLIS = 250;
 
+    private static final int BUTTON_NEUTRAL_RES_ID = android.R.id.button3;
+    private static final int BUTTON_DISCONNECT_RES_ID = android.R.id.button2;
+    private static final int BUTTON_STOP_RES_ID = android.R.id.button1;
+
     private final MediaRouter mRouter;
     private final MediaRouterCallback mCallback;
     private final MediaRouter.RouteInfo mRoute;
 
     private boolean mCreated;
     private boolean mAttachedToWindow;
-    private int mOrientation;
-    private int mDialogWidthPortrait;
-    private int mDialogWidthLandscape;
-    private int mDialogPaddingHorizontal;
+
+    private int mDialogContentWidth;
 
     private View mCustomControlView;
 
@@ -104,13 +107,14 @@
     private ImageButton mGroupExpandCollapseButton;
 
     private FrameLayout mCustomControlLayout;
+    private FrameLayout mDefaultControlLayout;
     private ImageView mArtView;
     private TextView mTitleView;
     private TextView mSubtitleView;
     private TextView mRouteNameTextView;
 
     private boolean mVolumeControlEnabled = true;
-    private LinearLayout mControlLayout;
+    private LinearLayout mMediaControlLayout;
     private RelativeLayout mPlaybackControl;
     private LinearLayout mVolumeControl;
     private View mDividerView;
@@ -118,6 +122,7 @@
     private ListView mVolumeGroupList;
     private SeekBar mVolumeSlider;
     private boolean mVolumeSliderTouched;
+    private int mVolumeSliderColor;
 
     private MediaControllerCompat mMediaController;
     private MediaControllerCallback mControllerCallback;
@@ -125,6 +130,9 @@
     private MediaDescriptionCompat mDescription;
 
     private FetchArtTask mFetchArtTask;
+    private Bitmap mArtIconBitmap;
+    private Uri mArtIconUri;
+    private boolean mIsGroupExpanded;
 
     public MediaRouteControllerDialog(Context context) {
         this(context, 0);
@@ -247,29 +255,34 @@
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.mr_controller_material_dialog_b);
-        View decorView = getWindow().getDecorView();
-        mDialogPaddingHorizontal = decorView.getPaddingLeft() + decorView.getPaddingRight();
-        Resources res = getContext().getResources();
-        mDialogWidthPortrait = res.getDimensionPixelSize(
-                R.dimen.mr_dialog_content_width_portrait) + mDialogPaddingHorizontal;
-        mDialogWidthLandscape = res.getDimensionPixelSize(
-                R.dimen.mr_dialog_content_width_landscape) + mDialogPaddingHorizontal;
+
+        // Remove the neutral button.
+        findViewById(BUTTON_NEUTRAL_RES_ID).setVisibility(View.GONE);
 
         ClickListener listener = new ClickListener();
 
-        mDisconnectButton = (Button) findViewById(R.id.mr_button_disconnect);
+        mDisconnectButton = (Button) findViewById(BUTTON_DISCONNECT_RES_ID);
+        mDisconnectButton.setText(R.string.mr_controller_disconnect);
         mDisconnectButton.setOnClickListener(listener);
 
-        mStopCastingButton = (Button) findViewById(R.id.mr_button_stop);
+        mStopCastingButton = (Button) findViewById(BUTTON_STOP_RES_ID);
+        mStopCastingButton.setText(R.string.mr_controller_stop);
         mStopCastingButton.setOnClickListener(listener);
 
+        TypedValue value = new TypedValue();
+        if (getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true)) {
+            mDisconnectButton.setTextColor(value.data);
+            mStopCastingButton.setTextColor(value.data);
+        }
+
         mRouteNameTextView = (TextView) findViewById(R.id.mr_name);
         mCloseButton = (ImageButton) findViewById(R.id.mr_close);
         mCloseButton.setOnClickListener(listener);
         mCustomControlLayout = (FrameLayout) findViewById(R.id.mr_custom_control);
+        mDefaultControlLayout = (FrameLayout) findViewById(R.id.mr_default_control);
         mArtView = (ImageView) findViewById(R.id.mr_art);
 
-        mControlLayout = (LinearLayout) findViewById(R.id.mr_control);
+        mMediaControlLayout = (LinearLayout) findViewById(R.id.mr_media_control);
         mDividerView = findViewById(R.id.mr_control_divider);
 
         mPlaybackControl = (RelativeLayout) findViewById(R.id.mr_playback_control);
@@ -315,6 +328,8 @@
                 }
             }
         });
+        mVolumeSliderColor = MediaRouterThemeHelper.getVolumeSliderColor(getContext());
+        setVolumeSliderColor(getContext(), mVolumeSlider, mVolumeSliderColor);
 
         TypedArray styledAttributes = getContext().obtainStyledAttributes(new int[] {
                 R.attr.mediaRouteExpandGroupDrawable,
@@ -327,61 +342,49 @@
         mVolumeGroupList = (ListView)findViewById(R.id.mr_volume_group_list);
         mGroupExpandCollapseButton = (ImageButton) findViewById(R.id.mr_group_expand_collapse);
         mGroupExpandCollapseButton.setOnClickListener(new View.OnClickListener() {
-            private boolean mIsExpanded;
-
             @Override
             public void onClick(View v) {
-                mIsExpanded = !mIsExpanded;
-                if (mIsExpanded) {
+                mIsGroupExpanded = !mIsGroupExpanded;
+                if (mIsGroupExpanded) {
                     mGroupExpandCollapseButton.setImageDrawable(collapseGroupDrawable);
                     mVolumeGroupList.setVisibility(View.VISIBLE);
                     mVolumeGroupList.setAdapter(
                             new VolumeGroupAdapter(getContext(), getGroup().getRoutes()));
+                    setLayoutHeight(mVolumeGroupList,
+                            MediaRouteDialogHelper.getControllerVolumeGroupListHeight(
+                                    getContext(), mVolumeGroupList.getAdapter().getCount()));
                 } else {
                     mGroupExpandCollapseButton.setImageDrawable(expandGroupDrawable);
                     mVolumeGroupList.setVisibility(View.GONE);
                 }
+                updateControlFrameLayout();
             }
         });
 
-        mCreated = true;
         mCustomControlView = onCreateMediaControlView(savedInstanceState);
         if (mCustomControlView != null) {
             mCustomControlLayout.addView(mCustomControlView);
             mCustomControlLayout.setVisibility(View.VISIBLE);
             mArtView.setVisibility(View.GONE);
         }
-        update();
+        mCreated = true;
+        updateLayout();
     }
 
     /**
-     * Called by {@link MediaRouteControllerDialogFragment} when the device configuration
-     * is changed.
+     * Sets the width of the dialog. Also called when configuration changes.
      */
-    void onConfigurationChanged(Configuration newConfig) {
-        onOrientationChanged(newConfig.orientation);
-    }
+    void updateLayout() {
+        int width = MediaRouteDialogHelper.getDialogWidth(getContext());
+        getWindow().setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT);
 
-    private void onOrientationChanged(int orientation) {
-        if (!mAttachedToWindow || mOrientation == orientation) {
-            return;
-        }
-        mOrientation = orientation;
-        int dialogWidth = mOrientation == Configuration.ORIENTATION_LANDSCAPE
-                ? mDialogWidthLandscape : mDialogWidthPortrait;
-        int buttonWidth = (dialogWidth - mDialogPaddingHorizontal) / 2;
-        getWindow().setLayout(dialogWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
+        View decorView = getWindow().getDecorView();
+        mDialogContentWidth = width - decorView.getPaddingLeft() - decorView.getPaddingRight();
 
-        // Manually set the width of buttons to workaround unexpected text alignment changes.
-        ViewGroup.LayoutParams lp = mStopCastingButton.getLayoutParams();
-        lp.width = buttonWidth;
-        mStopCastingButton.setLayoutParams(lp);
-
-        lp = mDisconnectButton.getLayoutParams();
-        lp.width = buttonWidth;
-        mDisconnectButton.setLayoutParams(lp);
-
-        updateArtView();
+        // Ensure the mArtView is updated.
+        mArtIconBitmap = null;
+        mArtIconUri = null;
+        update();
     }
 
     @Override
@@ -392,7 +395,6 @@
         mRouter.addCallback(MediaRouteSelector.EMPTY, mCallback,
                 MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
         setMediaSession(mRouter.getMediaSessionToken());
-        onOrientationChanged(getContext().getResources().getConfiguration().orientation);
     }
 
     @Override
@@ -432,7 +434,7 @@
         }
 
         mRouteNameTextView.setText(mRoute.getName());
-        mDisconnectButton.setVisibility(mRoute.canDisconnect() ? View.VISIBLE : View.INVISIBLE);
+        mDisconnectButton.setVisibility(mRoute.canDisconnect() ? View.VISIBLE : View.GONE);
 
         if (mCustomControlView == null) {
             if (mFetchArtTask != null) {
@@ -490,12 +492,10 @@
                                     ? R.string.mr_controller_no_media_selected
                                     : R.string.mr_controller_no_info_available);
                 }
-                mTitleView.setEnabled(false);
                 mTitleView.setVisibility(View.VISIBLE);
                 mSubtitleView.setVisibility(View.GONE);
             } else {
                 mTitleView.setText(title);
-                mTitleView.setEnabled(hasTitle);
                 mTitleView.setVisibility(hasTitle ? View.VISIBLE : View.GONE);
                 mSubtitleView.setText(subtitle);
                 mSubtitleView.setVisibility(hasSubtitle ? View.VISIBLE : View.GONE);
@@ -535,16 +535,45 @@
         mDividerView.setVisibility((mVolumeControl.getVisibility() == View.VISIBLE
                 && mPlaybackControl.getVisibility() == View.VISIBLE)
                 ? View.VISIBLE : View.GONE);
-        mControlLayout.setVisibility((mVolumeControl.getVisibility() == View.GONE
+        mMediaControlLayout.setVisibility((mVolumeControl.getVisibility() == View.GONE
                 && mPlaybackControl.getVisibility() == View.GONE)
                 ? View.GONE : View.VISIBLE);
     }
 
+    private void updateControlFrameLayout() {
+        int height;
+        if (mArtView.getVisibility() == View.GONE) {
+            height = LinearLayout.LayoutParams.WRAP_CONTENT;
+        } else {
+            measureAndGetDecorView();
+            if (!mIsGroupExpanded) {
+                height = mArtView.getMeasuredHeight() + mMediaControlLayout.getMeasuredHeight();
+            } else {
+                if (mVolumeGroupList.getAdapter().getCount() <= 2
+                        && mArtView.getMeasuredHeight() > mVolumeGroupList.getMeasuredHeight()) {
+                    // Push the controls up and partially cover the artwork.
+                    height = mArtView.getMeasuredHeight() + mMediaControlLayout.getMeasuredHeight()
+                            - mVolumeGroupList.getMeasuredHeight();
+                } else {
+                    // Completely cover the artwork.
+                    height = LinearLayout.LayoutParams.WRAP_CONTENT;
+                }
+            }
+        }
+        setLayoutHeight(mDefaultControlLayout, height);
+    }
+
     private boolean isVolumeControlAvailable() {
         return mVolumeControlEnabled && mRoute.getVolumeHandling() ==
                 MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
     }
 
+    private View measureAndGetDecorView() {
+        View decorView = getWindow().getDecorView();
+        decorView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        return decorView;
+    }
+
     private void updateArtView() {
         if (!(mArtView.getDrawable() instanceof BitmapDrawable)) {
             mArtView.setVisibility(View.GONE);
@@ -557,37 +586,56 @@
         }
         int desiredArtHeight = getDesiredArtHeight(art.getWidth(), art.getHeight());
         DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
-        int dialogWidth = displayMetrics.widthPixels < displayMetrics.heightPixels
-                ? mDialogWidthPortrait : mDialogWidthLandscape;
-        View decorView = getWindow().getDecorView();
-        decorView.measure(dialogWidth, View.MeasureSpec.UNSPECIFIED);
+        View decorView = measureAndGetDecorView();
         // Show art if and only if it fits in the screen.
         if (mArtView.getVisibility() == View.GONE) {
             if (decorView.getMeasuredHeight() + desiredArtHeight <= displayMetrics.heightPixels) {
                 mArtView.setVisibility(View.VISIBLE);
-                mArtView.setMaxHeight(desiredArtHeight);
+                setLayoutHeight(mArtView, desiredArtHeight);
+                mArtView.setScaleType(art.getWidth() >= art.getHeight()
+                        ? ImageView.ScaleType.FIT_XY : ImageView.ScaleType.FIT_CENTER);
             }
         } else {
             if (decorView.getMeasuredHeight() - mArtView.getMeasuredHeight() + desiredArtHeight
                     <= displayMetrics.heightPixels) {
-                mArtView.setMaxHeight(desiredArtHeight);
+                setLayoutHeight(mArtView, desiredArtHeight);
+                mArtView.setScaleType(art.getWidth() >= art.getHeight()
+                        ? ImageView.ScaleType.FIT_XY : ImageView.ScaleType.FIT_CENTER);
             } else {
                 mArtView.setVisibility(View.GONE);
             }
         }
     }
 
+    private static void setLayoutHeight(View view, int height) {
+        ViewGroup.LayoutParams lp = view.getLayoutParams();
+        lp.height = height;
+        view.setLayoutParams(lp);
+    }
+
+    private static void setVolumeSliderColor(Context context, SeekBar volumeSlider, int color) {
+        volumeSlider.getProgressDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
+        if (Build.VERSION.SDK_INT >= 16) {
+            SeekBarJellybean.getThumb(volumeSlider).setColorFilter(color, PorterDuff.Mode.SRC_IN);
+        } else {
+            // In case getThumb() isn't available, use the thumb drawable from AppCompat.
+            Drawable thumb =
+                    context.getResources().getDrawable(R.drawable.abc_seekbar_thumb_material);
+            thumb.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+            volumeSlider.setThumb(thumb);
+        }
+    }
+
     /**
      * Returns desired art height to fit into controller dialog.
      */
     private int getDesiredArtHeight(int originalWidth, int originalHeight) {
-        int dialogWidth = getWindow().getAttributes().width - mDialogPaddingHorizontal;
         if (originalWidth >= originalHeight) {
             // For landscape art, fit width to dialog width.
-            return (int) ((float) dialogWidth * originalHeight / originalWidth + 0.5f);
+            return (int) ((float) mDialogContentWidth * originalHeight / originalWidth + 0.5f);
         }
         // For portrait art, fit height to 16:9 ratio case's height.
-        return (int) ((float) dialogWidth * 9 / 16 + 0.5f);
+        return (int) ((float) mDialogContentWidth * 9 / 16 + 0.5f);
     }
 
     private final class MediaRouterCallback extends MediaRouter.Callback {
@@ -635,9 +683,9 @@
         @Override
         public void onClick(View v) {
             int id = v.getId();
-            if (id == R.id.mr_button_stop || id == R.id.mr_button_disconnect) {
+            if (id == BUTTON_STOP_RES_ID || id == BUTTON_DISCONNECT_RES_ID) {
                 if (mRoute.isSelected()) {
-                    mRouter.unselect(id == R.id.mr_button_stop ?
+                    mRouter.unselect(id == BUTTON_STOP_RES_ID ?
                             MediaRouter.UNSELECT_REASON_STOPPED :
                             MediaRouter.UNSELECT_REASON_DISCONNECTED);
                 }
@@ -689,6 +737,8 @@
             if (v == null) {
                 v = LayoutInflater.from(getContext()).inflate(
                         R.layout.mr_controller_volume_item, null);
+                setVolumeSliderColor(getContext(), (SeekBar) v.findViewById(R.id.mr_volume_slider),
+                        mVolumeSliderColor);
             }
             MediaRouter.RouteInfo route = getItem(position);
             if (route != null) {
@@ -727,19 +777,30 @@
     }
 
     private class FetchArtTask extends AsyncTask<Void, Void, Bitmap> {
-        private int mBackgroundColor;
+        final Bitmap mIconBitmap;
+        final Uri mIconUri;
+        int mBackgroundColor;
+
+        FetchArtTask() {
+            mIconBitmap = mDescription == null ? null : mDescription.getIconBitmap();
+            mIconUri = mDescription == null ? null : mDescription.getIconUri();
+        }
+
+        @Override
+        protected void onPreExecute() {
+            if (mArtIconBitmap == mIconBitmap && mArtIconUri == mIconUri) {
+                // Already handled the current art.
+                cancel(true);
+            }
+        }
 
         @Override
         protected Bitmap doInBackground(Void... arg) {
             Bitmap art = null;
-            if (mDescription == null) {
-                return null;
-            }
-            if (mDescription.getIconBitmap() != null) {
-                art = mDescription.getIconBitmap();
-            } else if (mDescription.getIconUri() != null) {
-                Uri iconUri = mDescription.getIconUri();
-                String scheme = iconUri.getScheme();
+            if (mIconBitmap != null) {
+                art = mIconBitmap;
+            } else if (mIconUri != null) {
+                String scheme = mIconUri.getScheme();
                 if (!(ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
                         || ContentResolver.SCHEME_CONTENT.equals(scheme)
                         || ContentResolver.SCHEME_FILE.equals(scheme))) {
@@ -749,7 +810,7 @@
                 BufferedInputStream stream = null;
                 try {
                     stream = new BufferedInputStream(
-                            getContext().getContentResolver().openInputStream(iconUri));
+                            getContext().getContentResolver().openInputStream(mIconUri));
 
                     // Query art size.
                     BitmapFactory.Options options = new BitmapFactory.Options();
@@ -765,7 +826,7 @@
                         // Failed to rewind the stream, try to reopen it.
                         stream.close();
                         stream = new BufferedInputStream(getContext().getContentResolver()
-                                .openInputStream(iconUri));
+                                .openInputStream(mIconUri));
                     }
                     // Calculate required size to decode the art and possibly resize it.
                     options.inJustDecodeBounds = false;
@@ -777,7 +838,7 @@
                     }
                     art = BitmapFactory.decodeStream(stream, null, options);
                 } catch (IOException e){
-                    Log.w(TAG, "Unable to open content: " + iconUri, e);
+                    Log.w(TAG, "Unable to open: " + mIconUri, e);
                 } finally {
                     if (stream != null) {
                         try {
@@ -790,7 +851,7 @@
             if (art != null && art.getWidth() < art.getHeight()) {
                 // Portrait art requires background color.
                 Palette palette = new Palette.Builder(art).maximumColorCount(1).generate();
-                mBackgroundColor = (palette.getSwatches() == null)
+                mBackgroundColor = palette.getSwatches().isEmpty()
                         ? 0 : palette.getSwatches().get(0).getRgb();
             }
             return art;
@@ -804,9 +865,15 @@
         @Override
         protected void onPostExecute(Bitmap art) {
             mFetchArtTask = null;
-            mArtView.setImageBitmap(art);
-            mArtView.setBackgroundColor(mBackgroundColor);
-            updateArtView();
+            if (mArtIconBitmap != mIconBitmap || mArtIconUri != mIconUri) {
+                mArtIconBitmap = mIconBitmap;
+                mArtIconUri = mIconUri;
+
+                mArtView.setImageBitmap(art);
+                mArtView.setBackgroundColor(mBackgroundColor);
+                updateArtView();
+                updateControlFrameLayout();
+            }
         }
     }
 }
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialogFragment.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialogFragment.java
index 8156492..6cadb9a 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialogFragment.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialogFragment.java
@@ -53,15 +53,16 @@
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        if (mDialog != null) {
-            mDialog.onConfigurationChanged(newConfig);
-        }
-    }
-
-    @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         mDialog = onCreateControllerDialog(getActivity(), savedInstanceState);
         return mDialog;
     }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mDialog != null) {
+            mDialog.updateLayout();
+        }
+    }
 }
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteDialogHelper.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteDialogHelper.java
new file mode 100644
index 0000000..be2e9e1
--- /dev/null
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteDialogHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.app;
+
+import android.content.Context;
+import android.support.v7.mediarouter.R;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+final class MediaRouteDialogHelper {
+    /**
+     * The framework should set the dialog width properly, but somehow it doesn't work, hence
+     * duplicating a similar logic here to determine the appropriate dialog width.
+     */
+    public static int getDialogWidth(Context context) {
+        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
+
+        TypedValue value = new TypedValue();
+        context.getResources().getValue(isPortrait ? R.dimen.mr_dialog_fixed_width_minor
+                : R.dimen.mr_dialog_fixed_width_major, value, true);
+        if (value.type == TypedValue.TYPE_DIMENSION) {
+            return (int) value.getDimension(metrics);
+        } else if (value.type == TypedValue.TYPE_FRACTION) {
+            return (int) value.getFraction(metrics.widthPixels, metrics.widthPixels);
+        }
+        return ViewGroup.LayoutParams.WRAP_CONTENT;
+    }
+
+    /**
+     * Returns the height of the volume group list in the controller dialog.
+     */
+    public static int getControllerVolumeGroupListHeight(Context context, int itemCount) {
+        if (itemCount >= 4) {
+            return context.getResources().getDimensionPixelSize(
+                    R.dimen.mr_controller_volume_group_list_max_height);
+        }
+        return LinearLayout.LayoutParams.WRAP_CONTENT;
+    }
+}
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
index 09999a1..ee6a3da 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
@@ -17,6 +17,7 @@
 package android.support.v7.app;
 
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.support.v7.mediarouter.R;
 import android.util.TypedValue;
@@ -27,9 +28,21 @@
     }
 
     public static Context createThemedContext(Context context) {
-        boolean isLightTheme = isLightTheme(context);
-        return new ContextThemeWrapper(context, isLightTheme ?
-                R.style.Theme_MediaRouter_Light : R.style.Theme_MediaRouter);
+        int style;
+        if (isLightTheme(context)) {
+            if (isPrimaryColorLight(context)) {
+                style = R.style.Theme_MediaRouter_Light;
+            } else {
+                style = R.style.Theme_MediaRouter_Light_DarkControlPanel;
+            }
+        } else {
+            if (isPrimaryColorLight(context)) {
+                style = R.style.Theme_MediaRouter_LightControlPanel;
+            } else {
+                style = R.style.Theme_MediaRouter;
+            }
+        }
+        return new ContextThemeWrapper(context, style);
     }
 
     public static int getThemeResource(Context context, int attr) {
@@ -42,9 +55,29 @@
         return res != 0 ? context.getResources().getDrawable(res) : null;
     }
 
+    public static int getVolumeSliderColor(Context context) {
+        return isPrimaryColorLight(context) ? Color.BLACK : Color.WHITE;
+    }
+
     private static boolean isLightTheme(Context context) {
         TypedValue value = new TypedValue();
         return context.getTheme().resolveAttribute(R.attr.isLightTheme, value, true)
                 && value.data != 0;
     }
+
+    private static boolean isPrimaryColorLight(Context context) {
+        TypedValue value = new TypedValue();
+        context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+        return luminance(value.data) >= 0.5;
+    }
+
+    private static float luminance(int color) {
+        double red = Color.red(color) / 255.0;
+        red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4);
+        double green = Color.green(color) / 255.0;
+        green = green < 0.03928 ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4);
+        double blue = Color.blue(color) / 255.0;
+        blue = blue < 0.03928 ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4);
+        return (float) ((0.2126 * red) + (0.7152 * green) + (0.0722 * blue));
+    }
 }
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
index eec60e2..55bd1ca 100644
--- a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
+++ b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
@@ -24,6 +24,7 @@
 import android.support.annotation.Nullable;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v4.os.AsyncTaskCompat;
+import android.util.Log;
 import android.util.TimingLogger;
 
 import java.util.ArrayList;
@@ -659,7 +660,12 @@
                     new AsyncTask<Bitmap, Void, Palette>() {
                         @Override
                         protected Palette doInBackground(Bitmap... params) {
-                            return generate();
+                            try {
+                                return generate();
+                            } catch (Exception e) {
+                                Log.e(LOG_TAG, "Exception thrown during async generate", e);
+                                return null;
+                            }
                         }
 
                         @Override