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