1 /* 2 * Copyright (C) 2015 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 17 package com.google.android.setupdesign.util; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.content.Intent; 22 import androidx.annotation.NonNull; 23 import androidx.annotation.StyleRes; 24 import com.google.android.setupcompat.PartnerCustomizationLayout; 25 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper; 26 import com.google.android.setupcompat.util.BuildCompatUtils; 27 import com.google.android.setupcompat.util.Logger; 28 import com.google.android.setupcompat.util.WizardManagerHelper; 29 import com.google.android.setupdesign.R; 30 import java.util.Objects; 31 32 /** The helper class holds the constant names of themes and util functions */ 33 public final class ThemeHelper { 34 35 private static final Logger LOG = new Logger("ThemeHelper"); 36 37 /** 38 * Passed in a setup wizard intent as {@link WizardManagerHelper#EXTRA_THEME}. This is the dark 39 * variant of the theme used in setup wizard for Nougat MR1. 40 */ 41 public static final String THEME_GLIF = "glif"; 42 43 /** 44 * Passed in a setup wizard intent as {@link WizardManagerHelper#EXTRA_THEME}. This is the default 45 * theme used in setup wizard for Nougat MR1. 46 */ 47 public static final String THEME_GLIF_LIGHT = "glif_light"; 48 49 /** 50 * Passed in a setup wizard intent as {@link WizardManagerHelper#EXTRA_THEME}. This is the dark 51 * variant of the theme used in setup wizard for O DR. 52 */ 53 public static final String THEME_GLIF_V2 = "glif_v2"; 54 55 /** 56 * Passed in a setup wizard intent as {@link WizardManagerHelper#EXTRA_THEME}. This is the default 57 * theme used in setup wizard for O DR. 58 */ 59 public static final String THEME_GLIF_V2_LIGHT = "glif_v2_light"; 60 61 /** 62 * Passed in a setup wizard intent as {@link WizardManagerHelper#EXTRA_THEME}. This is the dark 63 * variant of the theme used in setup wizard for P. 64 */ 65 public static final String THEME_GLIF_V3 = "glif_v3"; 66 67 /** 68 * Passed in a setup wizard intent as {@link WizardManagerHelper#EXTRA_THEME}. This is the default 69 * theme used in setup wizard for P. 70 */ 71 public static final String THEME_GLIF_V3_LIGHT = "glif_v3_light"; 72 73 /** 74 * Placeholder, not avirailed yet. 75 */ 76 public static final String THEME_GLIF_V4 = "glif_v4"; 77 78 /** 79 * Placeholder, not avirailed yet. 80 */ 81 public static final String THEME_GLIF_V4_LIGHT = "glif_v4_light"; 82 83 public static final String THEME_HOLO = "holo"; 84 public static final String THEME_HOLO_LIGHT = "holo_light"; 85 public static final String THEME_MATERIAL = "material"; 86 public static final String THEME_MATERIAL_LIGHT = "material_light"; 87 88 /** 89 * Checks the intent whether the extra indicates that the light theme should be used or not. If 90 * the theme is not specified in the intent, or the theme specified is unknown, the value def will 91 * be returned. Note that day-night themes are not taken into account by this method. 92 * 93 * @param intent The intent used to start the activity, which the theme extra will be read from. 94 * @param def The default value if the theme is not specified. 95 * @return True if the activity started by the given intent should use light theme. 96 */ isLightTheme(Intent intent, boolean def)97 public static boolean isLightTheme(Intent intent, boolean def) { 98 final String theme = intent.getStringExtra(WizardManagerHelper.EXTRA_THEME); 99 return isLightTheme(theme, def); 100 } 101 102 /** 103 * Checks whether {@code theme} represents a light or dark theme. If the theme specified is 104 * unknown, the value def will be returned. Note that day-night themes are not taken into account 105 * by this method. 106 * 107 * @param theme The theme as specified from an intent sent from setup wizard. 108 * @param def The default value if the theme is not known. 109 * @return True if {@code theme} represents a light theme. 110 */ isLightTheme(String theme, boolean def)111 public static boolean isLightTheme(String theme, boolean def) { 112 if (THEME_HOLO_LIGHT.equals(theme) 113 || THEME_MATERIAL_LIGHT.equals(theme) 114 || THEME_GLIF_LIGHT.equals(theme) 115 || THEME_GLIF_V2_LIGHT.equals(theme) 116 || THEME_GLIF_V3_LIGHT.equals(theme) 117 || THEME_GLIF_V4_LIGHT.equals(theme)) { 118 return true; 119 } else if (THEME_HOLO.equals(theme) 120 || THEME_MATERIAL.equals(theme) 121 || THEME_GLIF.equals(theme) 122 || THEME_GLIF_V2.equals(theme) 123 || THEME_GLIF_V3.equals(theme) 124 || THEME_GLIF_V4.equals(theme)) { 125 return false; 126 } else { 127 return def; 128 } 129 } 130 131 /** 132 * Reads the theme from the intent, and applies the theme to the activity as resolved by {@link 133 * ThemeResolver#getDefault()}. 134 * 135 * <p>If you require extra theme attributes, consider overriding {@link 136 * android.app.Activity#onApplyThemeResource} in your activity and call {@link 137 * android.content.res.Resources.Theme#applyStyle(int, boolean)} using your theme overlay. 138 * 139 * <pre>{@code 140 * protected void onApplyThemeResource(Theme theme, int resid, boolean first) { 141 * super.onApplyThemeResource(theme, resid, first); 142 * theme.applyStyle(R.style.MyThemeOverlay, true); 143 * } 144 * }</pre> 145 * 146 * @param activity the activity to get the intent from and apply the resulting theme to. 147 */ applyTheme(Activity activity)148 public static void applyTheme(Activity activity) { 149 ThemeResolver.getDefault().applyTheme(activity); 150 } 151 152 /** 153 * Checks whether SetupWizard supports the DayNight theme during setup flow; if it returns false, 154 * setup flow is always light theme. 155 * 156 * @return true if the SetupWizard is listening to system DayNight theme setting. 157 */ isSetupWizardDayNightEnabled(@onNull Context context)158 public static boolean isSetupWizardDayNightEnabled(@NonNull Context context) { 159 return PartnerConfigHelper.isSetupWizardDayNightEnabled(context); 160 } 161 162 /** 163 * Returns true if the partner provider of SetupWizard is ready to support more partner configs. 164 */ shouldApplyExtendedPartnerConfig(@onNull Context context)165 public static boolean shouldApplyExtendedPartnerConfig(@NonNull Context context) { 166 return PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context); 167 } 168 169 /** 170 * Returns {@code true} if the partner provider of SetupWizard is ready to support dynamic color. 171 */ isSetupWizardDynamicColorEnabled(@onNull Context context)172 public static boolean isSetupWizardDynamicColorEnabled(@NonNull Context context) { 173 return PartnerConfigHelper.isSetupWizardDynamicColorEnabled(context); 174 } 175 176 /** Returns {@code true} if this {@code context} should apply dynamic color. */ shouldApplyDynamicColor(@onNull Context context)177 public static boolean shouldApplyDynamicColor(@NonNull Context context) { 178 return shouldApplyExtendedPartnerConfig(context) && isSetupWizardDynamicColorEnabled(context); 179 } 180 181 /** 182 * Returns a theme resource id if the {@link com.google.android.setupdesign.GlifLayout} should 183 * apply dynamic color. 184 * 185 * <p>Otherwise returns {@code 0}. 186 */ 187 @StyleRes getDynamicColorTheme(@onNull Context context)188 public static int getDynamicColorTheme(@NonNull Context context) { 189 @StyleRes int resId = 0; 190 191 Activity activity; 192 try { 193 activity = PartnerCustomizationLayout.lookupActivityFromContext(context); 194 } catch (IllegalArgumentException ex) { 195 LOG.e(Objects.requireNonNull(ex.getMessage())); 196 return resId; 197 } 198 199 boolean isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent()); 200 boolean isDayNightEnabled = isSetupWizardDayNightEnabled(context); 201 202 if (isSetupFlow) { 203 // return theme for inside setup flow 204 resId = 205 isDayNightEnabled 206 ? R.style.SudDynamicColorThemeGlifV3_DayNight 207 : R.style.SudDynamicColorThemeGlifV3_Light; 208 } else { 209 // return theme for outside setup flow 210 resId = 211 isDayNightEnabled 212 ? R.style.SudFullDynamicColorThemeGlifV3_DayNight 213 : R.style.SudFullDynamicColorThemeGlifV3_Light; 214 LOG.atInfo( 215 "Return " 216 + (isDayNightEnabled 217 ? "SudFullDynamicColorThemeGlifV3_DayNight" 218 : "SudFullDynamicColorThemeGlifV3_Light")); 219 } 220 221 LOG.atDebug( 222 "Gets the dynamic accentColor: [Light] " 223 + colorIntToHex(context, R.color.sud_dynamic_color_accent_glif_v3_light) 224 + ", " 225 + (BuildCompatUtils.isAtLeastS() 226 ? colorIntToHex(context, android.R.color.system_accent1_600) 227 : "n/a") 228 + ", [Dark] " 229 + colorIntToHex(context, R.color.sud_dynamic_color_accent_glif_v3_dark) 230 + ", " 231 + (BuildCompatUtils.isAtLeastS() 232 ? colorIntToHex(context, android.R.color.system_accent1_100) 233 : "n/a")); 234 235 return resId; 236 } 237 238 /** Returns {@code true} if the dynamic color is set. */ trySetDynamicColor(@onNull Context context)239 public static boolean trySetDynamicColor(@NonNull Context context) { 240 if (!shouldApplyExtendedPartnerConfig(context)) { 241 LOG.w("SetupWizard does not supports the extended partner configs."); 242 return false; 243 } 244 245 if (!isSetupWizardDynamicColorEnabled(context)) { 246 LOG.w("SetupWizard does not support the dynamic color or supporting status unknown."); 247 return false; 248 } 249 250 Activity activity; 251 try { 252 activity = PartnerCustomizationLayout.lookupActivityFromContext(context); 253 } catch (IllegalArgumentException ex) { 254 LOG.e(Objects.requireNonNull(ex.getMessage())); 255 return false; 256 } 257 258 @StyleRes int resId = getDynamicColorTheme(context); 259 if (resId != 0) { 260 activity.setTheme(resId); 261 } else { 262 LOG.w("Error occurred on getting dynamic color theme."); 263 return false; 264 } 265 266 return true; 267 } 268 colorIntToHex(Context context, int colorInt)269 private static String colorIntToHex(Context context, int colorInt) { 270 return String.format("#%06X", (0xFFFFFF & context.getResources().getColor(colorInt))); 271 } 272 ThemeHelper()273 private ThemeHelper() {} 274 } 275