MediaRouter: Fix theme may be overridden by dialogTheme
If the given theme ID is 0, AppCompatDialog will use dialogTheme as
default theme, which may override style settings of
MediaRouteChooserDialog. Always give theme ID in
MediaRouteChooserDialog to prevent this happens.
Test: ./gradlew support-mediarouter-v7:connectedCheck
Bug: 38498797
Change-Id: I9df47adc5c047bfcdb70fee93094e624861cbd73
diff --git a/v7/mediarouter/build.gradle b/v7/mediarouter/build.gradle
index d0448dd..093c6c5 100644
--- a/v7/mediarouter/build.gradle
+++ b/v7/mediarouter/build.gradle
@@ -5,7 +5,13 @@
compile project(":support-appcompat-v7")
compile project(":support-palette-v7")
- androidTestCompile libs.test_runner
+ androidTestCompile (libs.test_runner) {
+ exclude module: 'support-annotations'
+ }
+ androidTestCompile (libs.espresso_core) {
+ exclude module: 'support-annotations'
+ }
+ androidTestCompile libs.mockito_core
}
android {
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
index 9f2d396..0ab2eb1 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
@@ -92,7 +92,10 @@
}
public MediaRouteChooserDialog(Context context, int theme) {
- super(MediaRouterThemeHelper.createThemedContext(context, theme), theme);
+ // If we pass theme ID of 0 to AppCompatDialog, it will apply dialogTheme on the context,
+ // which may override our style settings. Passes our uppermost theme ID to prevent this.
+ super(MediaRouterThemeHelper.createThemedContext(context, theme),
+ theme == 0 ? MediaRouterThemeHelper.createThemeForDialog(context, theme) : theme);
context = getContext();
mRouter = MediaRouter.getInstance(context);
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
index a7bceec..86f4753 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
@@ -201,8 +201,12 @@
}
public MediaRouteControllerDialog(Context context, int theme) {
+ // If we pass theme ID of 0 to AppCompatDialog, it will apply dialogTheme on the context,
+ // which may override our style settings. Passes our uppermost theme ID to prevent this.
super(MediaRouterThemeHelper.createThemedContext(context,
- MediaRouterThemeHelper.getAlertDialogResolvedTheme(context, theme)), theme);
+ MediaRouterThemeHelper.getAlertDialogResolvedTheme(context, theme)), theme == 0
+ ? MediaRouterThemeHelper.createThemeForDialog(context, MediaRouterThemeHelper
+ .getAlertDialogResolvedTheme(context, theme)) : theme);
mContext = getContext();
mControllerCallback = new MediaControllerCallback();
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
index 23f3bad..9ef218e 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
@@ -54,29 +54,21 @@
* {@code 0} to use the parent {@code context}'s default theme.
* @return The themed context.
*/
- public static Context createThemedContext(Context context, int style) {
+ static Context createThemedContext(Context context, int style) {
// First, apply dialog property overlay.
+ Context themedContext =
+ new ContextThemeWrapper(context, getStyledRouterThemeId(context, style));
+ int customizedThemeId = getThemeResource(context, R.attr.mediaRouteTheme);
+ return customizedThemeId == 0 ? themedContext
+ : new ContextThemeWrapper(themedContext, customizedThemeId);
+ }
- int theme;
- if (isLightTheme(context)) {
- if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
- theme = R.style.Theme_MediaRouter_Light;
- } else {
- theme = R.style.Theme_MediaRouter_Light_DarkControlPanel;
- }
- } else {
- if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
- theme = R.style.Theme_MediaRouter_LightControlPanel;
- } else {
- theme = R.style.Theme_MediaRouter;
- }
- }
- int mediaRouteThemeResId = getThemeResource(context, R.attr.mediaRouteTheme);
- Context themedContext = new ContextThemeWrapper(context, theme);
- if (mediaRouteThemeResId != 0) {
- themedContext = new ContextThemeWrapper(themedContext, mediaRouteThemeResId);
- }
- return themedContext;
+ /**
+ * Creates the theme resource ID intended to be used by dialogs.
+ */
+ static int createThemeForDialog(Context context, int style) {
+ int customizedThemeId = getThemeResource(context, R.attr.mediaRouteTheme);
+ return customizedThemeId != 0 ? customizedThemeId : getStyledRouterThemeId(context, style);
}
public static int getThemeResource(Context context, int attr) {
@@ -180,4 +172,22 @@
}
return value.data;
}
+
+ private static int getStyledRouterThemeId(Context context, int style) {
+ int themeId;
+ if (isLightTheme(context)) {
+ if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
+ themeId = R.style.Theme_MediaRouter_Light;
+ } else {
+ themeId = R.style.Theme_MediaRouter_Light_DarkControlPanel;
+ }
+ } else {
+ if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
+ themeId = R.style.Theme_MediaRouter_LightControlPanel;
+ } else {
+ themeId = R.style.Theme_MediaRouter;
+ }
+ }
+ return themeId;
+ }
}
diff --git a/v7/mediarouter/tests/AndroidManifest.xml b/v7/mediarouter/tests/AndroidManifest.xml
index 576c652..06b6090 100644
--- a/v7/mediarouter/tests/AndroidManifest.xml
+++ b/v7/mediarouter/tests/AndroidManifest.xml
@@ -25,6 +25,11 @@
android.support.test.espresso, android.support.test.espresso.idling"/>
<application android:supportsRtl="true">
+ <activity
+ android:name="android.support.v7.app.MediaRouteChooserDialogTestActivity"
+ android:label="MediaRouteChooserDialogTestActivity"
+ android:theme="@style/Theme.AppCompat" />
+
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
diff --git a/v7/mediarouter/tests/res/layout/mr_chooser_dialog_activity.xml b/v7/mediarouter/tests/res/layout/mr_chooser_dialog_activity.xml
new file mode 100644
index 0000000..484bd23
--- /dev/null
+++ b/v7/mediarouter/tests/res/layout/mr_chooser_dialog_activity.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</FrameLayout>
+
diff --git a/v7/mediarouter/tests/res/values/themes.xml b/v7/mediarouter/tests/res/values/themes.xml
new file mode 100644
index 0000000..31ae4ce
--- /dev/null
+++ b/v7/mediarouter/tests/res/values/themes.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+ <style name="HasWindowTitle">
+ <item name="windowNoTitle">false</item>
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTest.java b/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTest.java
index 7a21cdb..415ec1f 100644
--- a/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTest.java
+++ b/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTest.java
@@ -16,28 +16,76 @@
package android.support.v7.app;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.v7.media.MediaRouter.RouteInfo;
import android.support.v7.media.TestUtils;
+import android.support.v7.mediarouter.test.R;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
-@SmallTest
public class MediaRouteChooserDialogTest {
+
+ @Rule
+ public final ActivityTestRule<MediaRouteChooserDialogTestActivity> mActivityTestRule;
private MediaRouteChooserDialog.RouteComparator mComparator;
+ public MediaRouteChooserDialogTest() {
+ mActivityTestRule = new ActivityTestRule<>(MediaRouteChooserDialogTestActivity.class);
+ }
+
@Before
public void setup() {
mComparator = new MediaRouteChooserDialog.RouteComparator();
}
@Test
+ @SmallTest
+ @UiThreadTest
+ public void testWindowNoTitle() {
+ final Context context = mActivityTestRule.getActivity();
+ TypedArray typedArray;
+
+ // Without any base theme or customized theme
+ MediaRouteChooserDialog dialog = new MediaRouteChooserDialog(context);
+ typedArray = dialog.getContext().obtainStyledAttributes(R.styleable.AppCompatTheme);
+ assertTrue(typedArray.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false));
+ typedArray.recycle();
+
+ // No base theme, with a customized theme (has window title)
+ dialog = new MediaRouteChooserDialog(context, R.style.HasWindowTitle);
+ typedArray = dialog.getContext().obtainStyledAttributes(R.styleable.AppCompatTheme);
+ assertFalse(typedArray.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false));
+ typedArray.recycle();
+
+ // With base theme (has window title), no customized theme
+ context.setTheme(R.style.HasWindowTitle);
+ dialog = new MediaRouteChooserDialog(context);
+ typedArray = dialog.getContext().obtainStyledAttributes(R.styleable.AppCompatTheme);
+ assertTrue(typedArray.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false));
+ typedArray.recycle();
+
+ // With base theme and a customized theme (both has window title)
+ dialog = new MediaRouteChooserDialog(context, R.style.HasWindowTitle);
+ typedArray = dialog.getContext().obtainStyledAttributes(R.styleable.AppCompatTheme);
+ assertFalse(typedArray.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false));
+ typedArray.recycle();
+
+ context.setTheme(0);
+ }
+
+ @Test
public void testRouteComparatorWithSameRouteName() {
RouteInfo routeInfo1 = TestUtils.createRouteInfo("ROUTE_ID_1", "ROUTE_NAME_1");
RouteInfo routeInfo2 = TestUtils.createRouteInfo("ROUTE_ID_2", "ROUTE_NAME_1");
diff --git a/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTestActivity.java b/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTestActivity.java
new file mode 100644
index 0000000..39e4d7c
--- /dev/null
+++ b/v7/mediarouter/tests/src/android/support/v7/app/MediaRouteChooserDialogTestActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os.Bundle;
+import android.support.v7.mediarouter.test.R;
+
+public class MediaRouteChooserDialogTestActivity extends AppCompatActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.mr_chooser_dialog_activity);
+ }
+}