MediaRouter: New color scheme for main and group media controls
Instead of showing dark controls in a possibly dark (i.e. the primary
dark), modeled the white dialog and used the the primary color for the
group controls. Also, reverted opacity values back to 87% for better
contrast in a light background. In addition,
- Fixed a bug that the volume slider for a disabled media route is still
movable.
- Fixed a bug that the volume sliders are always tinted with the primary
color.
- Fixed a bug that the ripple drawables for the play/pause and group
collapse/expand buttons are cut off by an invisible divider.
- Corrected the inconsistent disabled alpha values.
- Ensured the volume slider color is opaque so that the underlying
progress bar is not seen through the thumb.
Bug: 24336474, Bug: 24779916, Bug: 24779734, Bug: 24779945
Change-Id: Ic163d73dd38558994ff5e0bf8b4c3d4b66800c23
diff --git a/v7/mediarouter/res/drawable/mr_ic_audiotrack_light.xml b/v7/mediarouter/res/drawable/mr_ic_audiotrack_light.xml
index 96093d1..05b4a8a 100644
--- a/v7/mediarouter/res/drawable/mr_ic_audiotrack_light.xml
+++ b/v7/mediarouter/res/drawable/mr_ic_audiotrack_light.xml
@@ -16,4 +16,4 @@
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_audiotrack_light"
- android:alpha="0.54" />
+ android:alpha="0.87" />
diff --git a/v7/mediarouter/res/drawable/mr_ic_close_light.xml b/v7/mediarouter/res/drawable/mr_ic_close_light.xml
index af0445e..c663ae8 100644
--- a/v7/mediarouter/res/drawable/mr_ic_close_light.xml
+++ b/v7/mediarouter/res/drawable/mr_ic_close_light.xml
@@ -18,7 +18,7 @@
<item>
<bitmap
android:src="@drawable/ic_close_light"
- android:alpha="0.54" />
+ android:alpha="0.87" />
</item>
</selector>
diff --git a/v7/mediarouter/res/drawable/mr_ic_pause_light.xml b/v7/mediarouter/res/drawable/mr_ic_pause_light.xml
index 88eda3e..0cea425 100644
--- a/v7/mediarouter/res/drawable/mr_ic_pause_light.xml
+++ b/v7/mediarouter/res/drawable/mr_ic_pause_light.xml
@@ -18,6 +18,6 @@
<item>
<bitmap
android:src="@drawable/ic_pause_light"
- android:alpha="0.54" />
+ android:alpha="0.87" />
</item>
</selector>
diff --git a/v7/mediarouter/res/drawable/mr_ic_play_light.xml b/v7/mediarouter/res/drawable/mr_ic_play_light.xml
index 487a5d5..48a7e03 100644
--- a/v7/mediarouter/res/drawable/mr_ic_play_light.xml
+++ b/v7/mediarouter/res/drawable/mr_ic_play_light.xml
@@ -18,6 +18,6 @@
<item>
<bitmap
android:src="@drawable/ic_play_light"
- android:alpha="0.54" />
+ android:alpha="0.87" />
</item>
</selector>
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 232eeae..b44c1dc 100644
--- a/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
+++ b/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
@@ -72,14 +72,12 @@
android:orientation="vertical"
android:paddingTop="16dp"
android:paddingBottom="16dp"
- android:background="?attr/colorPrimary"
android:layout_gravity="bottom">
<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" />
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
index fe33882..526230a 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
@@ -83,7 +83,7 @@
// route descriptor.
private static final int VOLUME_UPDATE_DELAY_MILLIS = 250;
private static final int VOLUME_SLIDER_TAG_MASTER = 0;
- private static final int VOLUME_SLIDER_TAG_BASE = 100;
+ private static final int VOLUME_SLIDER_TAG_GROUP_BASE = 100;
private static final int BUTTON_NEUTRAL_RES_ID = android.R.id.button3;
private static final int BUTTON_DISCONNECT_RES_ID = android.R.id.button2;
@@ -325,8 +325,11 @@
mVolumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener);
mVolumeGroupList = (ListView) findViewById(R.id.mr_volume_group_list);
- mVolumeGroupList.setBackgroundColor(
- MediaRouterThemeHelper.getVolumeGroupListBackgroundColor(mContext));
+ MediaRouterThemeHelper.setMediaControlsBackgroundColor(mContext,
+ mMediaMainControlLayout, mVolumeGroupList, getGroup() != null);
+ MediaRouterThemeHelper.setVolumeSliderColor(mContext,
+ (MediaRouteVolumeSlider) mVolumeSlider, mMediaMainControlLayout);
+
mGroupExpandCollapseButton =
(MediaRouteExpandCollapseButton) findViewById(R.id.mr_group_expand_collapse);
mGroupExpandCollapseButton.setOnClickListener(new View.OnClickListener() {
@@ -875,19 +878,22 @@
int tag = (int) seekBar.getTag();
if (tag == VOLUME_SLIDER_TAG_MASTER) {
mRoute.requestSetVolume(progress);
- } else if (tag - VOLUME_SLIDER_TAG_BASE >= 0
- && tag - VOLUME_SLIDER_TAG_BASE < getGroup().getRouteCount()) {
- getGroup().getRouteAt(tag - VOLUME_SLIDER_TAG_BASE).requestSetVolume(progress);
+ } else {
+ int index = tag - VOLUME_SLIDER_TAG_GROUP_BASE;
+ if (index >= 0 && index < getGroup().getRouteCount()) {
+ getGroup().getRouteAt(index).requestSetVolume(progress);
+ }
}
}
}
}
private class VolumeGroupAdapter extends ArrayAdapter<MediaRouter.RouteInfo> {
- final static float DISABLED_ALPHA = .3f;
+ final float mDisabledAlpha;
public VolumeGroupAdapter(Context context, List<MediaRouter.RouteInfo> objects) {
super(context, 0, objects);
+ mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context);
}
@Override
@@ -910,14 +916,16 @@
MediaRouteVolumeSlider volumeSlider =
(MediaRouteVolumeSlider) v.findViewById(R.id.mr_volume_slider);
- volumeSlider.setTag(VOLUME_SLIDER_TAG_BASE + position);
+ MediaRouterThemeHelper.setVolumeSliderColor(
+ mContext, volumeSlider, mVolumeGroupList);
+ volumeSlider.setTag(VOLUME_SLIDER_TAG_GROUP_BASE + position);
volumeSlider.setHideThumb(!isEnabled);
+ volumeSlider.setEnabled(isEnabled);
if (isEnabled) {
if (isVolumeControlAvailable(route)) {
volumeSlider.setMax(route.getVolumeMax());
volumeSlider.setProgress(route.getVolume());
volumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener);
- volumeSlider.setEnabled(true);
} else {
volumeSlider.setMax(100);
volumeSlider.setProgress(100);
@@ -927,7 +935,7 @@
ImageView volumeItemIcon =
(ImageView) v.findViewById(R.id.mr_volume_item_icon);
- volumeItemIcon.setAlpha(isEnabled ? 255 : (int) (255 * DISABLED_ALPHA));
+ volumeItemIcon.setAlpha(isEnabled ? 0xFF : (int) (0xFF * mDisabledAlpha));
}
return v;
}
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java
index 88dbcc5..a7aafd2 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java
@@ -17,24 +17,28 @@
package android.support.v7.app;
import android.content.Context;
-import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.v7.mediarouter.R;
import android.support.v7.widget.AppCompatSeekBar;
import android.util.AttributeSet;
+import android.util.Log;
/**
* Volume slider with showing, hiding, and applying alpha supports to the thumb.
*/
class MediaRouteVolumeSlider extends AppCompatSeekBar {
+ private static final String TAG = "MediaRouteVolumeSlider";
+
+ private final float mDisabledAlpha;
+
private boolean mHideThumb;
private Drawable mThumb;
private int mColor;
- private float mDisabledAlpha;
public MediaRouteVolumeSlider(Context context) {
- super(context, null);
+ this(context, null);
}
public MediaRouteVolumeSlider(Context context, AttributeSet attrs) {
@@ -43,23 +47,13 @@
public MediaRouteVolumeSlider(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mColor = MediaRouterThemeHelper.getVolumeSliderColor(context);
- TypedArray ta = context.obtainStyledAttributes(
- attrs, new int[] {android.R.attr.disabledAlpha}, defStyleAttr, 0);
- mDisabledAlpha = ta.getFloat(0, 0.5f);
- ta.recycle();
- }
-
- @Override
- public void setThumb(Drawable thumb) {
- mThumb = thumb;
- super.setThumb(mHideThumb ? null : mThumb);
+ mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context);
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
- int alpha = isEnabled() ? 0xFF : (int)(mDisabledAlpha * 0xFF);
+ int alpha = isEnabled() ? 0xFF : (int) (0xFF * mDisabledAlpha);
// The thumb drawable is a collection of drawables and its current drawables are changed per
// state. Apply the color filter and alpha on every state change.
@@ -70,8 +64,14 @@
getProgressDrawable().setAlpha(alpha);
}
+ @Override
+ public void setThumb(Drawable thumb) {
+ mThumb = thumb;
+ super.setThumb(mHideThumb ? null : mThumb);
+ }
+
/**
- * Sets whether to show/hide thumb.
+ * Sets whether to show or hide thumb.
*/
public void setHideThumb(boolean hideThumb) {
if (mHideThumb == hideThumb) {
@@ -80,4 +80,21 @@
mHideThumb = hideThumb;
super.setThumb(mHideThumb ? null : mThumb);
}
+
+ /**
+ * Sets the volume slider color. The change takes effect next time drawable state is changed.
+ * <p>
+ * The color cannot be translucent, otherwise the underlying progress bar will be seen through
+ * the thumb.
+ * </p>
+ */
+ public void setColor(int color) {
+ if (mColor == color) {
+ return;
+ }
+ if (Color.alpha(color) != 0xFF) {
+ Log.e(TAG, "Volume slider color cannot be translucent: #" + Integer.toHexString(color));
+ }
+ mColor = color;
+ }
}
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
index fb37f4e..f5a258c 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
@@ -18,27 +18,24 @@
import android.content.Context;
import android.graphics.Color;
-import android.graphics.drawable.Drawable;
import android.support.annotation.IntDef;
import android.support.v4.graphics.ColorUtils;
import android.support.v7.mediarouter.R;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
+import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
final class MediaRouterThemeHelper {
private static final float MIN_CONTRAST = 3.0f;
- private static final float MIN_CONTRAST_GROUP_VOLUMES = 4.5f;
-
- private static final float COLOR_LIGHTNESS_MULTIPLIER = 1.15f;
@IntDef({COLOR_DARK_ON_LIGHT_BACKGROUND, COLOR_WHITE_ON_DARK_BACKGROUND})
@Retention(RetentionPolicy.SOURCE)
private @interface ControllerColorType {}
- private static final int COLOR_DARK_ON_LIGHT_BACKGROUND = 0x8A000000; /* Opacity of 54% */
+ private static final int COLOR_DARK_ON_LIGHT_BACKGROUND = 0xDE000000; /* Opacity of 87% */
private static final int COLOR_WHITE_ON_DARK_BACKGROUND = Color.WHITE;
private MediaRouterThemeHelper() {
@@ -67,9 +64,10 @@
return context.getTheme().resolveAttribute(attr, value, true) ? value.resourceId : 0;
}
- public static Drawable getThemeDrawable(Context context, int attr) {
- int res = getThemeResource(context, attr);
- return res != 0 ? context.getResources().getDrawable(res) : null;
+ public static float getDisabledAlpha(Context context) {
+ TypedValue value = new TypedValue();
+ return context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true)
+ ? value.getFloat() : 0.5f;
}
public static @ControllerColorType int getControllerColor(Context context) {
@@ -81,45 +79,46 @@
return COLOR_DARK_ON_LIGHT_BACKGROUND;
}
- public static int getVolumeSliderColor(Context context) {
- int primaryColor = getThemeColor(context, R.attr.colorPrimary);
- if (ColorUtils.calculateContrast(COLOR_WHITE_ON_DARK_BACKGROUND, primaryColor)
- >= MIN_CONTRAST) {
- return COLOR_WHITE_ON_DARK_BACKGROUND;
- }
- // Composite with the background in order not to show the underlying progress bar through
- // the thumb.
- // TODO: Use the actual background color instead and ensure the resulting color is opaque.
- return ColorUtils.compositeColors(COLOR_DARK_ON_LIGHT_BACKGROUND, primaryColor);
- }
-
public static int getButtonTextColor(Context context) {
int primaryColor = getThemeColor(context, R.attr.colorPrimary);
int backgroundColor = getThemeColor(context, android.R.attr.colorBackground);
- double contrast = ColorUtils.calculateContrast(primaryColor, backgroundColor);
- if (contrast < MIN_CONTRAST) {
+ if (ColorUtils.calculateContrast(primaryColor, backgroundColor) < MIN_CONTRAST) {
// Default to colorAccent if the contrast ratio is low.
return getThemeColor(context, R.attr.colorAccent);
}
return primaryColor;
}
- public static int getVolumeGroupListBackgroundColor(Context context) {
+ public static void setMediaControlsBackgroundColor(
+ Context context, View mainControls, View groupControls, boolean hasGroup) {
+ int primaryColor = getThemeColor(context, R.attr.colorPrimary);
int primaryDarkColor = getThemeColor(context, R.attr.colorPrimaryDark);
- if (getControllerColor(context) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
- // We are showing dark volume sliders in a darker background. Check whether they have
- // sufficient contrast.
- double contrast = ColorUtils.calculateContrast(
- COLOR_DARK_ON_LIGHT_BACKGROUND, primaryDarkColor);
- if (contrast < MIN_CONTRAST_GROUP_VOLUMES) {
- // Generate a lighter color based on the 'colorPrimary' and use it instead for
- // better contrast.
- int primaryColor = getThemeColor(context, R.attr.colorPrimary);
- return adjustColorLightness(primaryColor, COLOR_LIGHTNESS_MULTIPLIER);
- }
+ if (hasGroup && ColorUtils.calculateContrast(COLOR_WHITE_ON_DARK_BACKGROUND, primaryColor)
+ < MIN_CONTRAST) {
+ // Instead of showing dark controls in a possibly dark (i.e. the primary dark), model
+ // the white dialog and use the primary color for the group controls.
+ primaryDarkColor = primaryColor;
+ primaryColor = Color.WHITE;
}
- return primaryDarkColor;
+ mainControls.setBackgroundColor(primaryColor);
+ groupControls.setBackgroundColor(primaryDarkColor);
+ // Also store the background colors to the view tags. They are used in
+ // setVolumeSliderColor() below.
+ mainControls.setTag(primaryColor);
+ groupControls.setTag(primaryDarkColor);
+ }
+
+ public static void setVolumeSliderColor(
+ Context context, MediaRouteVolumeSlider volumeSlider, View backgroundView) {
+ int controllerColor = getControllerColor(context);
+ if (Color.alpha(controllerColor) != 0xFF) {
+ // Composite with the background in order not to show the underlying progress bar
+ // through the thumb.
+ int backgroundColor = (int) backgroundView.getTag();
+ controllerColor = ColorUtils.compositeColors(controllerColor, backgroundColor);
+ }
+ volumeSlider.setColor(controllerColor);
}
private static boolean isLightTheme(Context context) {
@@ -128,14 +127,6 @@
&& value.data != 0;
}
- private static int adjustColorLightness(int color, float lightness) {
- float[] hsl = new float[3];
- ColorUtils.colorToHSL(color, hsl);
- // Clip the lightness to 100%
- hsl[2] = Math.min(1f, hsl[2] * lightness);
- return ColorUtils.HSLToColor(hsl);
- }
-
private static int getThemeColor(Context context, int attr) {
TypedValue value = new TypedValue();
context.getTheme().resolveAttribute(attr, value, true);