• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 package com.android.customization.model.theme;
17 
18 import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
19 import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
20 import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_ANDROID;
21 import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_LAUNCHER;
22 import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SETTINGS;
23 import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SYSUI;
24 import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_THEMEPICKER;
25 import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
26 
27 import android.provider.Settings;
28 import android.text.TextUtils;
29 
30 import androidx.annotation.Nullable;
31 import androidx.fragment.app.FragmentActivity;
32 
33 import com.android.customization.model.CustomizationManager;
34 import com.android.customization.model.ResourceConstants;
35 import com.android.customization.model.theme.custom.CustomTheme;
36 import com.android.customization.module.ThemesUserEventLogger;
37 
38 import org.json.JSONObject;
39 
40 import java.util.HashSet;
41 import java.util.Map;
42 import java.util.Set;
43 
44 public class ThemeManager implements CustomizationManager<ThemeBundle> {
45 
46     public static final Set<String> THEME_CATEGORIES = new HashSet<>();
47     static {
48         THEME_CATEGORIES.add(OVERLAY_CATEGORY_COLOR);
49         THEME_CATEGORIES.add(OVERLAY_CATEGORY_FONT);
50         THEME_CATEGORIES.add(OVERLAY_CATEGORY_SHAPE);
51         THEME_CATEGORIES.add(OVERLAY_CATEGORY_ICON_ANDROID);
52         THEME_CATEGORIES.add(OVERLAY_CATEGORY_ICON_SETTINGS);
53         THEME_CATEGORIES.add(OVERLAY_CATEGORY_ICON_SYSUI);
54         THEME_CATEGORIES.add(OVERLAY_CATEGORY_ICON_LAUNCHER);
55         THEME_CATEGORIES.add(OVERLAY_CATEGORY_ICON_THEMEPICKER);
56     }
57 
58     private final ThemeBundleProvider mProvider;
59     private final OverlayManagerCompat mOverlayManagerCompat;
60 
61     protected final FragmentActivity mActivity;
62     private final ThemesUserEventLogger mEventLogger;
63 
64     private Map<String, String> mCurrentOverlays;
65 
ThemeManager(ThemeBundleProvider provider, FragmentActivity activity, OverlayManagerCompat overlayManagerCompat, ThemesUserEventLogger logger)66     public ThemeManager(ThemeBundleProvider provider, FragmentActivity activity,
67             OverlayManagerCompat overlayManagerCompat,
68             ThemesUserEventLogger logger) {
69         mProvider = provider;
70         mActivity = activity;
71         mOverlayManagerCompat = overlayManagerCompat;
72         mEventLogger = logger;
73     }
74 
75     @Override
isAvailable()76     public boolean isAvailable() {
77         return mOverlayManagerCompat.isAvailable() && mProvider.isAvailable();
78     }
79 
80     @Override
apply(ThemeBundle theme, Callback callback)81     public void apply(ThemeBundle theme, Callback callback) {
82         applyOverlays(theme, callback);
83     }
84 
applyOverlays(ThemeBundle theme, Callback callback)85     private void applyOverlays(ThemeBundle theme, Callback callback) {
86         boolean allApplied = Settings.Secure.putString(mActivity.getContentResolver(),
87                 ResourceConstants.THEME_SETTING, theme.getSerializedPackagesWithTimestamp());
88         if (theme instanceof CustomTheme) {
89             storeCustomTheme((CustomTheme) theme);
90         }
91         mCurrentOverlays = null;
92         if (allApplied) {
93             mEventLogger.logThemeApplied(theme, theme instanceof CustomTheme);
94             callback.onSuccess();
95         } else {
96             callback.onError(null);
97         }
98     }
99 
storeCustomTheme(CustomTheme theme)100     private void storeCustomTheme(CustomTheme theme) {
101         mProvider.storeCustomTheme(theme);
102     }
103 
104     @Override
fetchOptions(OptionsFetchedListener<ThemeBundle> callback, boolean reload)105     public void fetchOptions(OptionsFetchedListener<ThemeBundle> callback, boolean reload) {
106         mProvider.fetch(callback, reload);
107     }
108 
getCurrentOverlays()109     public Map<String, String> getCurrentOverlays() {
110         if (mCurrentOverlays == null) {
111             mCurrentOverlays = mOverlayManagerCompat.getEnabledOverlaysForTargets(
112                     ResourceConstants.getPackagesToOverlay(mActivity));
113             mCurrentOverlays.entrySet().removeIf(
114                     categoryAndPackage -> !THEME_CATEGORIES.contains(categoryAndPackage.getKey()));
115         }
116         return mCurrentOverlays;
117     }
118 
getStoredOverlays()119     public String getStoredOverlays() {
120         return Settings.Secure.getString(mActivity.getContentResolver(),
121                 ResourceConstants.THEME_SETTING);
122     }
123 
removeCustomTheme(CustomTheme theme)124     public void removeCustomTheme(CustomTheme theme) {
125         mProvider.removeCustomTheme(theme);
126     }
127 
128     /**
129      * @return an existing ThemeBundle that matches the same packages as the given one, if one
130      * exists, or {@code null} otherwise.
131      */
132     @Nullable
findThemeByPackages(ThemeBundle other)133     public ThemeBundle findThemeByPackages(ThemeBundle other) {
134         return mProvider.findEquivalent(other);
135     }
136 
137     /**
138      * Store empty theme if no theme has been set yet. This will prevent Settings from showing the
139      * suggestion to select a theme
140      */
storeEmptyTheme()141     public void storeEmptyTheme() {
142         String themeSetting = Settings.Secure.getString(mActivity.getContentResolver(),
143                 ResourceConstants.THEME_SETTING);
144         if (TextUtils.isEmpty(themeSetting)) {
145             Settings.Secure.putString(mActivity.getContentResolver(),
146                     ResourceConstants.THEME_SETTING, new JSONObject().toString());
147         }
148     }
149 }
150