Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2018 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.google.android.setupdesign.util;
     18 
     19 import android.app.Activity;
     20 import android.content.Intent;
     21 import androidx.annotation.Nullable;
     22 import androidx.annotation.StyleRes;
     23 import com.google.android.setupcompat.util.WizardManagerHelper;
     24 import com.google.android.setupdesign.R;
     25 
     26 /**
     27  * A resolver to resolve the theme from a string or an activity intent, setting options like the
     28  * default theme and the oldest supported theme. Apps can share the resolver across the entire
     29  * process by calling {@link #setDefault(ThemeResolver)} in {@link
     30  * android.app.Application#onCreate()}. If an app needs more granular sharing of the theme default
     31  * values, additional instances of {@link ThemeResolver} can be created using the builder.
     32  */
     33 public class ThemeResolver {
     34 
     35   @StyleRes private final int defaultTheme;
     36   @Nullable private final String oldestSupportedTheme;
     37   private final boolean useDayNight;
     38   @Nullable private final ThemeSupplier defaultThemeSupplier;
     39 
     40   @Nullable private static ThemeResolver defaultResolver;
     41 
     42   /**
     43    * Sets the default instance used for the whole process. Can be null to reset the default to the
     44    * preset one.
     45    */
     46   public static void setDefault(@Nullable ThemeResolver resolver) {
     47     defaultResolver = resolver;
     48   }
     49 
     50   /**
     51    * Returns the default instance, which can be changed using {@link #setDefault(ThemeResolver)}.
     52    */
     53   public static ThemeResolver getDefault() {
     54     if (defaultResolver == null) {
     55       defaultResolver =
     56           new ThemeResolver.Builder()
     57               .setDefaultTheme(R.style.SudThemeGlif_DayNight)
     58               .setUseDayNight(true)
     59               .build();
     60     }
     61     return defaultResolver;
     62   }
     63 
     64   private ThemeResolver(
     65       int defaultTheme,
     66       @Nullable String oldestSupportedTheme,
     67       @Nullable ThemeSupplier defaultThemeSupplier,
     68       boolean useDayNight) {
     69     this.defaultTheme = defaultTheme;
     70     this.oldestSupportedTheme = oldestSupportedTheme;
     71     this.defaultThemeSupplier = defaultThemeSupplier;
     72     this.useDayNight = useDayNight;
     73   }
     74 
     75   /**
     76    * Returns the style for the theme specified in the intent extra. If the specified string theme is
     77    * older than the oldest supported theme, the default will be returned instead. Note that the
     78    * default theme is returned without processing -- it may not be a DayNight theme even if {@link
     79    * #useDayNight} is true.
     80    */
     81   @StyleRes
     82   public int resolve(Intent intent) {
     83     return resolve(
     84         intent.getStringExtra(WizardManagerHelper.EXTRA_THEME),
     85         /* suppressDayNight= */ WizardManagerHelper.isAnySetupWizard(intent));
     86   }
     87 
     88   /**
     89    * Returns the style for the given string theme. If the specified string theme is older than the
     90    * oldest supported theme, the default will be returned instead. Note that the default theme is
     91    * returned without processing -- it may not be a DayNight theme even if {@link #useDayNight} is
     92    * true.
     93    *
     94    * @deprecated Use {@link #resolve(String, boolean)} instead
     95    */
     96   @Deprecated
     97   @StyleRes
     98   public int resolve(@Nullable String theme) {
     99     return resolve(theme, /* suppressDayNight= */ false);
    100   }
    101 
    102   /**
    103    * Returns the style for the given string theme. If the specified string theme is older than the
    104    * oldest supported theme, the default will be returned instead. Note that the default theme is
    105    * returned without processing -- it may not be a DayNight theme even if {@link #useDayNight} is
    106    * true.
    107    */
    108   @StyleRes
    109   public int resolve(@Nullable String theme, boolean suppressDayNight) {
    110     int themeResource =
    111         useDayNight && !suppressDayNight ? getDayNightThemeRes(theme) : getThemeRes(theme);
    112     if (themeResource == 0) {
    113       if (defaultThemeSupplier != null) {
    114         theme = defaultThemeSupplier.getTheme();
    115         themeResource =
    116             useDayNight && !suppressDayNight ? getDayNightThemeRes(theme) : getThemeRes(theme);
    117       }
    118       if (themeResource == 0) {
    119         return defaultTheme;
    120       }
    121     }
    122 
    123     if (oldestSupportedTheme != null && compareThemes(theme, oldestSupportedTheme) < 0) {
    124       return defaultTheme;
    125     }
    126     return themeResource;
    127   }
    128 
    129   /** Reads the theme from the intent, and applies the resolved theme to the activity. */
    130   public void applyTheme(Activity activity) {
    131     activity.setTheme(resolve(activity.getIntent()));
    132   }
    133 
    134   /**
    135    * Returns the corresponding DayNight theme resource ID for the given string theme. DayNight
    136    * themes are themes that will be either light or dark depending on the system setting. For
    137    * example, the string {@link ThemeHelper#THEME_GLIF_LIGHT} will return
    138    * {@code @style/SudThemeGlif.DayNight}.
    139    */
    140   @StyleRes
    141   private static int getDayNightThemeRes(@Nullable String theme) {
    142     if (theme != null) {
    143       switch (theme) {
    144         case ThemeHelper.THEME_GLIF_V3_LIGHT:
    145         case ThemeHelper.THEME_GLIF_V3:
    146           return R.style.SudThemeGlifV3_DayNight;
    147         case ThemeHelper.THEME_GLIF_V2_LIGHT:
    148         case ThemeHelper.THEME_GLIF_V2:
    149           return R.style.SudThemeGlifV2_DayNight;
    150         case ThemeHelper.THEME_GLIF_LIGHT:
    151         case ThemeHelper.THEME_GLIF:
    152           return R.style.SudThemeGlif_DayNight;
    153         case ThemeHelper.THEME_MATERIAL_LIGHT:
    154         case ThemeHelper.THEME_MATERIAL:
    155           return R.style.SudThemeMaterial_DayNight;
    156         default:
    157           // fall through
    158       }
    159     }
    160     return 0;
    161   }
    162 
    163   /**
    164    * Returns the theme resource ID for the given string theme. For example, the string {@link
    165    * ThemeHelper#THEME_GLIF_LIGHT} will return {@code @style/SudThemeGlif.Light}.
    166    */
    167   @StyleRes
    168   private static int getThemeRes(@Nullable String theme) {
    169     if (theme != null) {
    170       switch (theme) {
    171         case ThemeHelper.THEME_GLIF_V3_LIGHT:
    172           return R.style.SudThemeGlifV3_Light;
    173         case ThemeHelper.THEME_GLIF_V3:
    174           return R.style.SudThemeGlifV3;
    175         case ThemeHelper.THEME_GLIF_V2_LIGHT:
    176           return R.style.SudThemeGlifV2_Light;
    177         case ThemeHelper.THEME_GLIF_V2:
    178           return R.style.SudThemeGlifV2;
    179         case ThemeHelper.THEME_GLIF_LIGHT:
    180           return R.style.SudThemeGlif_Light;
    181         case ThemeHelper.THEME_GLIF:
    182           return R.style.SudThemeGlif;
    183         case ThemeHelper.THEME_MATERIAL_LIGHT:
    184           return R.style.SudThemeMaterial_Light;
    185         case ThemeHelper.THEME_MATERIAL:
    186           return R.style.SudThemeMaterial;
    187         default:
    188           // fall through
    189       }
    190     }
    191     return 0;
    192   }
    193 
    194   /** Compares whether the versions of {@code theme1} and {@code theme2} to check which is newer. */
    195   private static int compareThemes(String theme1, String theme2) {
    196     return Integer.valueOf(getThemeVersion(theme1)).compareTo(getThemeVersion(theme2));
    197   }
    198 
    199   /**
    200    * Returns the version of the theme. The absolute number of the theme version is not defined, but
    201    * a larger number in the version indicates a newer theme.
    202    */
    203   private static int getThemeVersion(String theme) {
    204     if (theme != null) {
    205       switch (theme) {
    206         case ThemeHelper.THEME_GLIF_V3_LIGHT:
    207         case ThemeHelper.THEME_GLIF_V3:
    208           return 4;
    209         case ThemeHelper.THEME_GLIF_V2_LIGHT:
    210         case ThemeHelper.THEME_GLIF_V2:
    211           return 3;
    212         case ThemeHelper.THEME_GLIF_LIGHT:
    213         case ThemeHelper.THEME_GLIF:
    214           return 2;
    215         case ThemeHelper.THEME_MATERIAL_LIGHT:
    216         case ThemeHelper.THEME_MATERIAL:
    217           return 1;
    218         default:
    219           // fall through
    220       }
    221     }
    222     return -1;
    223   }
    224 
    225   /** Builder class for {@link ThemeResolver}. */
    226   public static class Builder {
    227     private ThemeSupplier defaultThemeSupplier;
    228     @StyleRes private int defaultTheme = R.style.SudThemeGlif_DayNight;
    229     @Nullable private String oldestSupportedTheme = null;
    230     private boolean useDayNight = true;
    231 
    232     public Builder() {}
    233 
    234     public Builder(ThemeResolver themeResolver) {
    235       this.defaultTheme = themeResolver.defaultTheme;
    236       this.oldestSupportedTheme = themeResolver.oldestSupportedTheme;
    237       this.useDayNight = themeResolver.useDayNight;
    238     }
    239 
    240     public Builder setDefaultThemeSupplier(ThemeSupplier defaultThemeSupplier) {
    241       this.defaultThemeSupplier = defaultThemeSupplier;
    242       return this;
    243     }
    244 
    245     public Builder setDefaultTheme(@StyleRes int defaultTheme) {
    246       this.defaultTheme = defaultTheme;
    247       return this;
    248     }
    249 
    250     public Builder setOldestSupportedTheme(String oldestSupportedTheme) {
    251       this.oldestSupportedTheme = oldestSupportedTheme;
    252       return this;
    253     }
    254 
    255     public Builder setUseDayNight(boolean useDayNight) {
    256       this.useDayNight = useDayNight;
    257       return this;
    258     }
    259 
    260     public ThemeResolver build() {
    261       return new ThemeResolver(
    262           defaultTheme, oldestSupportedTheme, defaultThemeSupplier, useDayNight);
    263     }
    264   }
    265 
    266   public interface ThemeSupplier {
    267     String getTheme();
    268   }
    269 }
    270