1 /* 2 * Copyright (C) 2021 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.android.managedprovisioning.common; 18 19 import static android.content.res.Configuration.UI_MODE_NIGHT_MASK; 20 import static android.content.res.Configuration.UI_MODE_NIGHT_YES; 21 22 import static com.android.managedprovisioning.provisioning.Constants.FLAG_ENABLE_LIGHT_DARK_MODE; 23 24 import static com.google.android.setupdesign.util.ThemeHelper.trySetDynamicColor; 25 26 import static java.util.Objects.requireNonNull; 27 28 import android.content.Context; 29 import android.content.Intent; 30 import android.os.SystemProperties; 31 import android.text.TextUtils; 32 import android.webkit.WebSettings; 33 34 import androidx.appcompat.app.AppCompatDelegate; 35 import androidx.webkit.WebSettingsCompat; 36 import androidx.webkit.WebViewFeature; 37 38 import com.airbnb.lottie.LottieAnimationView; 39 import com.airbnb.lottie.LottieComposition; 40 import com.google.android.setupcompat.util.WizardManagerHelper; 41 import com.google.android.setupdesign.R; 42 import com.google.android.setupdesign.util.ThemeResolver; 43 44 /** 45 * Helper with utility methods to manage the ManagedProvisioning theme and night mode. 46 */ 47 public class ThemeHelper { 48 private static final String SYSTEM_PROPERTY_SETUPWIZARD_THEME = 49 SystemProperties.get("setupwizard.theme"); 50 51 private final NightModeChecker mNightModeChecker; 52 private final SetupWizardBridge mSetupWizardBridge; 53 private final AnimationDynamicColorsHelper mAnimationDynamicColorsHelper; 54 ThemeHelper(NightModeChecker nightModeChecker, SetupWizardBridge setupWizardBridge)55 public ThemeHelper(NightModeChecker nightModeChecker, SetupWizardBridge setupWizardBridge) { 56 mNightModeChecker = requireNonNull(nightModeChecker); 57 mSetupWizardBridge = requireNonNull(setupWizardBridge); 58 // TODO(b/190182035): Tidy up tests after adding dependency injection support 59 mAnimationDynamicColorsHelper = new AnimationDynamicColorsHelper(); 60 } 61 62 /** 63 * Infers the correct theme resource id. 64 */ inferThemeResId(Context context, Intent intent)65 public int inferThemeResId(Context context, Intent intent) { 66 requireNonNull(context); 67 requireNonNull(intent); 68 String themeName = getDefaultThemeName(context, intent); 69 int defaultTheme = mSetupWizardBridge.isSetupWizardDayNightEnabled(context) 70 ? R.style.SudThemeGlifV4_DayNight 71 : R.style.SudThemeGlifV4_Light; 72 return mSetupWizardBridge 73 .resolveTheme(defaultTheme, themeName, shouldSuppressDayNight(context)); 74 } 75 76 /** 77 * Sets up theme-specific colors. Must be called after {@link 78 * #inferThemeResId(Context, Intent)}. 79 */ setupDynamicColors(Context context)80 public void setupDynamicColors(Context context) { 81 requireNonNull(context); 82 trySetDynamicColor(context); 83 } 84 85 /** 86 * Returns the appropriate day or night mode, depending on the setup wizard flags. 87 * 88 * @return {@link AppCompatDelegate#MODE_NIGHT_YES} or {@link AppCompatDelegate#MODE_NIGHT_NO} 89 */ getDefaultNightMode(Context context, Intent intent)90 public int getDefaultNightMode(Context context, Intent intent) { 91 requireNonNull(context); 92 if (TextUtils.isEmpty(getProvidedTheme(intent))) { 93 return isSystemNightMode(context) 94 ? AppCompatDelegate.MODE_NIGHT_YES 95 : AppCompatDelegate.MODE_NIGHT_NO; 96 } 97 if (shouldSuppressDayNight(context)) { 98 return AppCompatDelegate.MODE_NIGHT_NO; 99 } 100 if (isSystemNightMode(context)) { 101 return AppCompatDelegate.MODE_NIGHT_YES; 102 } 103 return AppCompatDelegate.MODE_NIGHT_NO; 104 } 105 106 /** 107 * Forces the web pages shown by the {@link android.webkit.WebView} which has the 108 * supplied {@code webSettings} to have the appropriate day/night mode depending 109 * on the app theme. 110 */ applyWebSettingsDayNight(Context context, WebSettings webSettings, Intent intent)111 public void applyWebSettingsDayNight(Context context, WebSettings webSettings, Intent intent) { 112 requireNonNull(context); 113 requireNonNull(webSettings); 114 if (!WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { 115 return; 116 } 117 WebSettingsCompat.setForceDark(webSettings, getForceDarkMode(context, intent)); 118 } 119 120 /** 121 * Updates the relevant animation with theme-specific colors. 122 * <p>If the supplied {@link LottieAnimationView} does not have a loaded {@link 123 * LottieComposition}, it asynchronously waits for it to load and then applies the colors. 124 */ setupAnimationDynamicColors( Context context, LottieAnimationView lottieAnimationView, Intent intent)125 public void setupAnimationDynamicColors( 126 Context context, LottieAnimationView lottieAnimationView, Intent intent) { 127 mAnimationDynamicColorsHelper.setupAnimationDynamicColors( 128 new LottieAnimationWrapper(lottieAnimationView), 129 getDefaultNightMode(context, intent)); 130 } 131 getForceDarkMode(Context context, Intent intent)132 private int getForceDarkMode(Context context, Intent intent) { 133 if (getDefaultNightMode(context, intent) == AppCompatDelegate.MODE_NIGHT_YES) { 134 return WebSettingsCompat.FORCE_DARK_ON; 135 } else { 136 return WebSettingsCompat.FORCE_DARK_OFF; 137 } 138 } 139 shouldSuppressDayNight(Context context)140 private boolean shouldSuppressDayNight(Context context) { 141 if (!FLAG_ENABLE_LIGHT_DARK_MODE) { 142 return true; 143 } 144 return !mSetupWizardBridge.isSetupWizardDayNightEnabled(context); 145 } 146 isSystemNightMode(Context context)147 private boolean isSystemNightMode(Context context) { 148 return mNightModeChecker.isSystemNightMode(context); 149 } 150 getDefaultThemeName(Context context, Intent intent)151 private String getDefaultThemeName(Context context, Intent intent) { 152 String theme = getProvidedTheme(intent); 153 if (TextUtils.isEmpty(theme)) { 154 if (isSystemNightMode(context)) { 155 theme = com.google.android.setupdesign.util.ThemeHelper.THEME_GLIF_V4; 156 } else { 157 theme = com.google.android.setupdesign.util.ThemeHelper.THEME_GLIF_V4_LIGHT; 158 } 159 } 160 return theme; 161 } 162 getProvidedTheme(Intent intent)163 private String getProvidedTheme(Intent intent) { 164 String theme = intent.getStringExtra(WizardManagerHelper.EXTRA_THEME); 165 if (TextUtils.isEmpty(theme)) { 166 return mSetupWizardBridge.getSystemPropertySetupWizardTheme(); 167 } 168 return theme; 169 } 170 171 interface SetupWizardBridge { isSetupWizardDayNightEnabled(Context context)172 boolean isSetupWizardDayNightEnabled(Context context); 173 getSystemPropertySetupWizardTheme()174 String getSystemPropertySetupWizardTheme(); 175 resolveTheme(int defaultTheme, String themeName, boolean suppressDayNight)176 int resolveTheme(int defaultTheme, String themeName, boolean suppressDayNight); 177 } 178 179 interface NightModeChecker { isSystemNightMode(Context context)180 boolean isSystemNightMode(Context context); 181 } 182 183 /** 184 * Default implementation of {@link NightModeChecker}. 185 */ 186 public static class DefaultNightModeChecker implements NightModeChecker { 187 @Override isSystemNightMode(Context context)188 public boolean isSystemNightMode(Context context) { 189 return (context.getResources().getConfiguration().uiMode & UI_MODE_NIGHT_MASK) 190 == UI_MODE_NIGHT_YES; 191 } 192 } 193 194 /** 195 * Default implementation of {@link SetupWizardBridge}. 196 */ 197 public static class DefaultSetupWizardBridge implements SetupWizardBridge { 198 @Override isSetupWizardDayNightEnabled(Context context)199 public boolean isSetupWizardDayNightEnabled(Context context) { 200 return com.google.android.setupdesign.util.ThemeHelper 201 .isSetupWizardDayNightEnabled(context); 202 } 203 204 @Override getSystemPropertySetupWizardTheme()205 public String getSystemPropertySetupWizardTheme() { 206 return SYSTEM_PROPERTY_SETUPWIZARD_THEME; 207 } 208 209 @Override resolveTheme(int defaultTheme, String themeName, boolean suppressDayNight)210 public int resolveTheme(int defaultTheme, String themeName, boolean suppressDayNight) { 211 ThemeResolver themeResolver = new ThemeResolver.Builder(ThemeResolver.getDefault()) 212 .setDefaultTheme(defaultTheme) 213 .setUseDayNight(true) 214 .build(); 215 return themeResolver.resolve( 216 themeName, 217 suppressDayNight); 218 } 219 } 220 } 221