[go: nahoru, domu]

blob: 26df71d3949414bab5264606e0bd210931201f95 [file] [log] [blame]
Aurimas Liutikasac5fe7c2018-03-06 14:40:53 -08001/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.mediarouter.app;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.graphics.Color;
22import androidx.annotation.IntDef;
23import androidx.core.graphics.ColorUtils;
24import androidx.mediarouter.R;
25import android.util.TypedValue;
26import android.view.ContextThemeWrapper;
27import android.view.View;
28
29import java.lang.annotation.Retention;
30import java.lang.annotation.RetentionPolicy;
31
32final class MediaRouterThemeHelper {
33 private static final float MIN_CONTRAST = 3.0f;
34
35 @IntDef({COLOR_DARK_ON_LIGHT_BACKGROUND, COLOR_WHITE_ON_DARK_BACKGROUND})
36 @Retention(RetentionPolicy.SOURCE)
37 private @interface ControllerColorType {}
38
39 static final int COLOR_DARK_ON_LIGHT_BACKGROUND = 0xDE000000; /* Opacity of 87% */
40 static final int COLOR_WHITE_ON_DARK_BACKGROUND = Color.WHITE;
41
42 private MediaRouterThemeHelper() {
43 }
44
45 static Context createThemedButtonContext(Context context) {
46 // Apply base Media Router theme.
47 context = new ContextThemeWrapper(context, getRouterThemeId(context));
48
49 // Apply custom Media Router theme.
50 int style = getThemeResource(context, R.attr.mediaRouteTheme);
51 if (style != 0) {
52 context = new ContextThemeWrapper(context, style);
53 }
54
55 return context;
56 }
57
58 /*
59 * The following two methods are to be used in conjunction. They should be used to prepare
60 * the context and theme for a super class constructor (the latter method relies on the
61 * former method to properly prepare the context):
62 * super(context = createThemedDialogContext(context, theme),
63 * createThemedDialogStyle(context));
64 *
65 * It will apply theme in the following order (style lookups will be done in reverse):
66 * 1) Current theme
67 * 2) Supplied theme
68 * 3) Base Media Router theme
69 * 4) Custom Media Router theme, if provided
70 */
71 static Context createThemedDialogContext(Context context, int theme, boolean alertDialog) {
72 // 1) Current theme is already applied to the context
73
74 // 2) If no theme is supplied, look it up from the context (dialogTheme/alertDialogTheme)
75 if (theme == 0) {
76 theme = getThemeResource(context, !alertDialog
77 ? androidx.appcompat.R.attr.dialogTheme
78 : androidx.appcompat.R.attr.alertDialogTheme);
79 }
80 // Apply it
81 context = new ContextThemeWrapper(context, theme);
82
83 // 3) If a custom Media Router theme is provided then apply the base theme
84 if (getThemeResource(context, R.attr.mediaRouteTheme) != 0) {
85 context = new ContextThemeWrapper(context, getRouterThemeId(context));
86 }
87
88 return context;
89 }
90 // This method should be used in conjunction with the previous method.
91 static int createThemedDialogStyle(Context context) {
92 // 4) Apply the custom Media Router theme
93 int theme = getThemeResource(context, R.attr.mediaRouteTheme);
94 if (theme == 0) {
95 // 3) No custom MediaRouther theme was provided so apply the base theme instead
96 theme = getRouterThemeId(context);
97 }
98
99 return theme;
100 }
101 // END. Previous two methods should be used in conjunction.
102
103 static int getThemeResource(Context context, int attr) {
104 TypedValue value = new TypedValue();
105 return context.getTheme().resolveAttribute(attr, value, true) ? value.resourceId : 0;
106 }
107
108 static float getDisabledAlpha(Context context) {
109 TypedValue value = new TypedValue();
110 return context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true)
111 ? value.getFloat() : 0.5f;
112 }
113
114 static @ControllerColorType int getControllerColor(Context context, int style) {
115 int primaryColor = getThemeColor(context, style,
116 androidx.appcompat.R.attr.colorPrimary);
117 if (ColorUtils.calculateContrast(COLOR_WHITE_ON_DARK_BACKGROUND, primaryColor)
118 >= MIN_CONTRAST) {
119 return COLOR_WHITE_ON_DARK_BACKGROUND;
120 }
121 return COLOR_DARK_ON_LIGHT_BACKGROUND;
122 }
123
124 static int getButtonTextColor(Context context) {
125 int primaryColor = getThemeColor(context, 0,
126 androidx.appcompat.R.attr.colorPrimary);
127 int backgroundColor = getThemeColor(context, 0, android.R.attr.colorBackground);
128
129 if (ColorUtils.calculateContrast(primaryColor, backgroundColor) < MIN_CONTRAST) {
130 // Default to colorAccent if the contrast ratio is low.
131 return getThemeColor(context, 0, androidx.appcompat.R.attr.colorAccent);
132 }
133 return primaryColor;
134 }
135
136 static void setMediaControlsBackgroundColor(
137 Context context, View mainControls, View groupControls, boolean hasGroup) {
138 int primaryColor = getThemeColor(context, 0,
139 androidx.appcompat.R.attr.colorPrimary);
140 int primaryDarkColor = getThemeColor(context, 0,
141 androidx.appcompat.R.attr.colorPrimaryDark);
142 if (hasGroup && getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
143 // Instead of showing dark controls in a possibly dark (i.e. the primary dark), model
144 // the white dialog and use the primary color for the group controls.
145 primaryDarkColor = primaryColor;
146 primaryColor = Color.WHITE;
147 }
148 mainControls.setBackgroundColor(primaryColor);
149 groupControls.setBackgroundColor(primaryDarkColor);
150 // Also store the background colors to the view tags. They are used in
151 // setVolumeSliderColor() below.
152 mainControls.setTag(primaryColor);
153 groupControls.setTag(primaryDarkColor);
154 }
155
156 static void setVolumeSliderColor(
157 Context context, MediaRouteVolumeSlider volumeSlider, View backgroundView) {
158 int controllerColor = getControllerColor(context, 0);
159 if (Color.alpha(controllerColor) != 0xFF) {
160 // Composite with the background in order not to show the underlying progress bar
161 // through the thumb.
162 int backgroundColor = (int) backgroundView.getTag();
163 controllerColor = ColorUtils.compositeColors(controllerColor, backgroundColor);
164 }
165 volumeSlider.setColor(controllerColor);
166 }
167
168 private static boolean isLightTheme(Context context) {
169 TypedValue value = new TypedValue();
170 return context.getTheme().resolveAttribute(androidx.appcompat.R.attr.isLightTheme,
171 value, true) && value.data != 0;
172 }
173
174 private static int getThemeColor(Context context, int style, int attr) {
175 if (style != 0) {
176 int[] attrs = { attr };
177 TypedArray ta = context.obtainStyledAttributes(style, attrs);
178 int color = ta.getColor(0, 0);
179 ta.recycle();
180 if (color != 0) {
181 return color;
182 }
183 }
184 TypedValue value = new TypedValue();
185 context.getTheme().resolveAttribute(attr, value, true);
186 if (value.resourceId != 0) {
187 return context.getResources().getColor(value.resourceId);
188 }
189 return value.data;
190 }
191
192 private static int getRouterThemeId(Context context) {
193 int themeId;
194 if (isLightTheme(context)) {
195 if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
196 themeId = R.style.Theme_MediaRouter_Light;
197 } else {
198 themeId = R.style.Theme_MediaRouter_Light_DarkControlPanel;
199 }
200 } else {
201 if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
202 themeId = R.style.Theme_MediaRouter_LightControlPanel;
203 } else {
204 themeId = R.style.Theme_MediaRouter;
205 }
206 }
207 return themeId;
208 }
209}