| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.managedprovisioning.common; |
| |
| import static android.content.res.Configuration.UI_MODE_NIGHT_MASK; |
| import static android.content.res.Configuration.UI_MODE_NIGHT_YES; |
| |
| import static com.android.managedprovisioning.provisioning.Constants.FLAG_ENABLE_LIGHT_DARK_MODE; |
| |
| import static com.google.android.setupdesign.util.ThemeHelper.trySetDynamicColor; |
| |
| import static java.util.Objects.requireNonNull; |
| |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.SystemProperties; |
| import android.text.TextUtils; |
| import android.webkit.WebSettings; |
| |
| import androidx.appcompat.app.AppCompatDelegate; |
| import androidx.webkit.WebSettingsCompat; |
| import androidx.webkit.WebViewFeature; |
| |
| import com.airbnb.lottie.LottieAnimationView; |
| import com.airbnb.lottie.LottieComposition; |
| import com.google.android.setupcompat.util.WizardManagerHelper; |
| import com.google.android.setupdesign.R; |
| import com.google.android.setupdesign.util.ThemeResolver; |
| |
| /** |
| * Helper with utility methods to manage the ManagedProvisioning theme and night mode. |
| */ |
| public class ThemeHelper { |
| private static final String SYSTEM_PROPERTY_SETUPWIZARD_THEME = |
| SystemProperties.get("setupwizard.theme"); |
| |
| private final NightModeChecker mNightModeChecker; |
| private final SetupWizardBridge mSetupWizardBridge; |
| private final AnimationDynamicColorsHelper mAnimationDynamicColorsHelper; |
| |
| public ThemeHelper(NightModeChecker nightModeChecker, SetupWizardBridge setupWizardBridge) { |
| mNightModeChecker = requireNonNull(nightModeChecker); |
| mSetupWizardBridge = requireNonNull(setupWizardBridge); |
| // TODO(b/190182035): Tidy up tests after adding dependency injection support |
| mAnimationDynamicColorsHelper = new AnimationDynamicColorsHelper(); |
| } |
| |
| /** |
| * Infers the correct theme resource id. |
| */ |
| public int inferThemeResId(Context context, Intent intent) { |
| requireNonNull(context); |
| requireNonNull(intent); |
| String themeName = getDefaultThemeName(context, intent); |
| int defaultTheme = mSetupWizardBridge.isSetupWizardDayNightEnabled(context) |
| ? R.style.SudThemeGlifV4_DayNight |
| : R.style.SudThemeGlifV4_Light; |
| return mSetupWizardBridge |
| .resolveTheme(defaultTheme, themeName, shouldSuppressDayNight(context)); |
| } |
| |
| /** |
| * Sets up theme-specific colors. Must be called after {@link |
| * #inferThemeResId(Context, Intent)}. |
| */ |
| public void setupDynamicColors(Context context) { |
| requireNonNull(context); |
| trySetDynamicColor(context); |
| } |
| |
| /** |
| * Returns the appropriate day or night mode, depending on the setup wizard flags. |
| * |
| * @return {@link AppCompatDelegate#MODE_NIGHT_YES} or {@link AppCompatDelegate#MODE_NIGHT_NO} |
| */ |
| public int getDefaultNightMode(Context context, Intent intent) { |
| requireNonNull(context); |
| if (TextUtils.isEmpty(getProvidedTheme(intent))) { |
| return isSystemNightMode(context) |
| ? AppCompatDelegate.MODE_NIGHT_YES |
| : AppCompatDelegate.MODE_NIGHT_NO; |
| } |
| if (shouldSuppressDayNight(context)) { |
| return AppCompatDelegate.MODE_NIGHT_NO; |
| } |
| if (isSystemNightMode(context)) { |
| return AppCompatDelegate.MODE_NIGHT_YES; |
| } |
| return AppCompatDelegate.MODE_NIGHT_NO; |
| } |
| |
| /** |
| * Forces the web pages shown by the {@link android.webkit.WebView} which has the |
| * supplied {@code webSettings} to have the appropriate day/night mode depending |
| * on the app theme. |
| */ |
| public void applyWebSettingsDayNight(Context context, WebSettings webSettings, Intent intent) { |
| requireNonNull(context); |
| requireNonNull(webSettings); |
| if (!WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { |
| return; |
| } |
| WebSettingsCompat.setForceDark(webSettings, getForceDarkMode(context, intent)); |
| } |
| |
| /** |
| * Updates the relevant animation with theme-specific colors. |
| * <p>If the supplied {@link LottieAnimationView} does not have a loaded {@link |
| * LottieComposition}, it asynchronously waits for it to load and then applies the colors. |
| */ |
| public void setupAnimationDynamicColors( |
| Context context, LottieAnimationView lottieAnimationView, Intent intent) { |
| mAnimationDynamicColorsHelper.setupAnimationDynamicColors( |
| new LottieAnimationWrapper(lottieAnimationView), |
| getDefaultNightMode(context, intent)); |
| } |
| |
| private int getForceDarkMode(Context context, Intent intent) { |
| if (getDefaultNightMode(context, intent) == AppCompatDelegate.MODE_NIGHT_YES) { |
| return WebSettingsCompat.FORCE_DARK_ON; |
| } else { |
| return WebSettingsCompat.FORCE_DARK_OFF; |
| } |
| } |
| |
| private boolean shouldSuppressDayNight(Context context) { |
| if (!FLAG_ENABLE_LIGHT_DARK_MODE) { |
| return true; |
| } |
| return !mSetupWizardBridge.isSetupWizardDayNightEnabled(context); |
| } |
| |
| private boolean isSystemNightMode(Context context) { |
| return mNightModeChecker.isSystemNightMode(context); |
| } |
| |
| private String getDefaultThemeName(Context context, Intent intent) { |
| String theme = getProvidedTheme(intent); |
| if (TextUtils.isEmpty(theme)) { |
| if (isSystemNightMode(context)) { |
| theme = com.google.android.setupdesign.util.ThemeHelper.THEME_GLIF_V4; |
| } else { |
| theme = com.google.android.setupdesign.util.ThemeHelper.THEME_GLIF_V4_LIGHT; |
| } |
| } |
| return theme; |
| } |
| |
| private String getProvidedTheme(Intent intent) { |
| String theme = intent.getStringExtra(WizardManagerHelper.EXTRA_THEME); |
| if (TextUtils.isEmpty(theme)) { |
| return mSetupWizardBridge.getSystemPropertySetupWizardTheme(); |
| } |
| return theme; |
| } |
| |
| interface SetupWizardBridge { |
| boolean isSetupWizardDayNightEnabled(Context context); |
| |
| String getSystemPropertySetupWizardTheme(); |
| |
| int resolveTheme(int defaultTheme, String themeName, boolean suppressDayNight); |
| } |
| |
| interface NightModeChecker { |
| boolean isSystemNightMode(Context context); |
| } |
| |
| /** |
| * Default implementation of {@link NightModeChecker}. |
| */ |
| public static class DefaultNightModeChecker implements NightModeChecker { |
| @Override |
| public boolean isSystemNightMode(Context context) { |
| return (context.getResources().getConfiguration().uiMode & UI_MODE_NIGHT_MASK) |
| == UI_MODE_NIGHT_YES; |
| } |
| } |
| |
| /** |
| * Default implementation of {@link SetupWizardBridge}. |
| */ |
| public static class DefaultSetupWizardBridge implements SetupWizardBridge { |
| @Override |
| public boolean isSetupWizardDayNightEnabled(Context context) { |
| return com.google.android.setupdesign.util.ThemeHelper |
| .isSetupWizardDayNightEnabled(context); |
| } |
| |
| @Override |
| public String getSystemPropertySetupWizardTheme() { |
| return SYSTEM_PROPERTY_SETUPWIZARD_THEME; |
| } |
| |
| @Override |
| public int resolveTheme(int defaultTheme, String themeName, boolean suppressDayNight) { |
| ThemeResolver themeResolver = new ThemeResolver.Builder(ThemeResolver.getDefault()) |
| .setDefaultTheme(defaultTheme) |
| .setUseDayNight(true) |
| .build(); |
| return themeResolver.resolve( |
| themeName, |
| suppressDayNight); |
| } |
| } |
| } |