• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import static com.google.android.setupdesign.util.ThemeHelper.trySetSuwTheme;
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     /** Returns {@code true} if the SUW theme is set. */
setSuwTheme(Context context)77     public boolean setSuwTheme(Context context) {
78         requireNonNull(context);
79         return trySetSuwTheme(context);
80     }
81     /**
82      * Sets up theme-specific colors. Must be called after {@link
83      * #inferThemeResId(Context, Intent)}.
84      */
setupDynamicColors(Context context)85     public void setupDynamicColors(Context context) {
86         requireNonNull(context);
87         trySetDynamicColor(context);
88     }
89 
90     /** Returns {@code true} if this {@code context} should applied Glif expressive style. */
shouldApplyGlifExpressiveStyle(Context context)91     public static boolean shouldApplyGlifExpressiveStyle(Context context) {
92         requireNonNull(context);
93         return
94             com.google.android.setupdesign.util.ThemeHelper.shouldApplyGlifExpressiveStyle(context);
95     }
96 
97     /**
98      * Returns the appropriate day or night mode, depending on the setup wizard flags.
99      *
100      * @return {@link AppCompatDelegate#MODE_NIGHT_YES} or {@link AppCompatDelegate#MODE_NIGHT_NO}
101      */
getDefaultNightMode(Context context, Intent intent)102     public int getDefaultNightMode(Context context, Intent intent) {
103         requireNonNull(context);
104         if (TextUtils.isEmpty(getProvidedTheme(intent))) {
105             return isSystemNightMode(context)
106                     ? AppCompatDelegate.MODE_NIGHT_YES
107                     : AppCompatDelegate.MODE_NIGHT_NO;
108         }
109         if (shouldSuppressDayNight(context)) {
110             return AppCompatDelegate.MODE_NIGHT_NO;
111         }
112         if (isSystemNightMode(context)) {
113             return AppCompatDelegate.MODE_NIGHT_YES;
114         }
115         return AppCompatDelegate.MODE_NIGHT_NO;
116     }
117 
118     /**
119      * Forces the web pages shown by the {@link android.webkit.WebView} which has the
120      * supplied {@code webSettings} to have the appropriate day/night mode depending
121      * on the app theme.
122      */
applyWebSettingsDayNight(Context context, WebSettings webSettings, Intent intent)123     public void applyWebSettingsDayNight(Context context, WebSettings webSettings, Intent intent) {
124         requireNonNull(context);
125         requireNonNull(webSettings);
126         if (!WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
127             return;
128         }
129         WebSettingsCompat.setForceDark(webSettings, getForceDarkMode(context, intent));
130     }
131 
132     /**
133      * Updates the relevant animation with theme-specific colors.
134      * <p>If the supplied {@link LottieAnimationView} does not have a loaded {@link
135      * LottieComposition}, it asynchronously waits for it to load and then applies the colors.
136      */
setupAnimationDynamicColors( Context context, LottieAnimationView lottieAnimationView, Intent intent)137     public void setupAnimationDynamicColors(
138             Context context, LottieAnimationView lottieAnimationView, Intent intent) {
139         mAnimationDynamicColorsHelper.setupAnimationDynamicColors(
140                 new LottieAnimationWrapper(lottieAnimationView),
141                 getDefaultNightMode(context, intent));
142     }
143 
getForceDarkMode(Context context, Intent intent)144     private int getForceDarkMode(Context context, Intent intent) {
145         if (getDefaultNightMode(context, intent) == AppCompatDelegate.MODE_NIGHT_YES) {
146             return WebSettingsCompat.FORCE_DARK_ON;
147         } else {
148             return WebSettingsCompat.FORCE_DARK_OFF;
149         }
150     }
151 
shouldSuppressDayNight(Context context)152     private boolean shouldSuppressDayNight(Context context) {
153         if (!FLAG_ENABLE_LIGHT_DARK_MODE) {
154             return true;
155         }
156         return !mSetupWizardBridge.isSetupWizardDayNightEnabled(context);
157     }
158 
isSystemNightMode(Context context)159     private boolean isSystemNightMode(Context context) {
160         return mNightModeChecker.isSystemNightMode(context);
161     }
162 
getDefaultThemeName(Context context, Intent intent)163     private String getDefaultThemeName(Context context, Intent intent) {
164         String theme = getProvidedTheme(intent);
165         if (TextUtils.isEmpty(theme)) {
166             if (isSystemNightMode(context)) {
167                 theme = com.google.android.setupdesign.util.ThemeHelper.THEME_GLIF_V4;
168             } else {
169                 theme = com.google.android.setupdesign.util.ThemeHelper.THEME_GLIF_V4_LIGHT;
170             }
171         }
172         return theme;
173     }
174 
getProvidedTheme(Intent intent)175     private String getProvidedTheme(Intent intent) {
176         String theme = intent.getStringExtra(WizardManagerHelper.EXTRA_THEME);
177         if (TextUtils.isEmpty(theme)) {
178             return mSetupWizardBridge.getSystemPropertySetupWizardTheme();
179         }
180         return theme;
181     }
182 
183     interface SetupWizardBridge {
isSetupWizardDayNightEnabled(Context context)184         boolean isSetupWizardDayNightEnabled(Context context);
185 
getSystemPropertySetupWizardTheme()186         String getSystemPropertySetupWizardTheme();
187 
resolveTheme(int defaultTheme, String themeName, boolean suppressDayNight)188         int resolveTheme(int defaultTheme, String themeName, boolean suppressDayNight);
189     }
190 
191     interface NightModeChecker {
isSystemNightMode(Context context)192         boolean isSystemNightMode(Context context);
193     }
194 
195     /**
196      * Default implementation of {@link NightModeChecker}.
197      */
198     public static class DefaultNightModeChecker implements NightModeChecker {
199         @Override
isSystemNightMode(Context context)200         public boolean isSystemNightMode(Context context) {
201             return (context.getResources().getConfiguration().uiMode & UI_MODE_NIGHT_MASK)
202                     == UI_MODE_NIGHT_YES;
203         }
204     }
205 
206     /**
207      * Default implementation of {@link SetupWizardBridge}.
208      */
209     public static class DefaultSetupWizardBridge implements SetupWizardBridge {
210         @Override
isSetupWizardDayNightEnabled(Context context)211         public boolean isSetupWizardDayNightEnabled(Context context) {
212             return com.google.android.setupdesign.util.ThemeHelper
213                     .isSetupWizardDayNightEnabled(context);
214         }
215 
216         @Override
getSystemPropertySetupWizardTheme()217         public String getSystemPropertySetupWizardTheme() {
218             return SYSTEM_PROPERTY_SETUPWIZARD_THEME;
219         }
220 
221         @Override
resolveTheme(int defaultTheme, String themeName, boolean suppressDayNight)222         public int resolveTheme(int defaultTheme, String themeName, boolean suppressDayNight) {
223             ThemeResolver themeResolver = new ThemeResolver.Builder(ThemeResolver.getDefault())
224                     .setDefaultTheme(defaultTheme)
225                     .setUseDayNight(true)
226                     .build();
227             return themeResolver.resolve(
228                     themeName,
229                     suppressDayNight);
230         }
231     }
232 }
233