• 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.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