• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.customization.model.theme;
2 
3 import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
4 import static com.android.customization.model.ResourceConstants.ICONS_FOR_PREVIEW;
5 import static com.android.customization.model.ResourceConstants.SETTINGS_PACKAGE;
6 import static com.android.customization.model.ResourceConstants.SYSUI_PACKAGE;
7 
8 import android.content.Context;
9 import android.content.om.OverlayInfo;
10 import android.content.om.OverlayManager;
11 import android.content.pm.ApplicationInfo;
12 import android.content.pm.PackageManager.NameNotFoundException;
13 import android.content.res.Resources;
14 import android.content.res.Resources.NotFoundException;
15 import android.graphics.Typeface;
16 import android.graphics.drawable.Drawable;
17 import android.os.UserHandle;
18 import android.text.TextUtils;
19 import android.util.Log;
20 
21 import androidx.annotation.Dimension;
22 import androidx.annotation.Nullable;
23 
24 import com.android.customization.model.ResourceConstants;
25 import com.android.customization.model.theme.ThemeBundle.Builder;
26 import com.android.customization.model.theme.ThemeBundle.PreviewInfo.ShapeAppIcon;
27 import com.android.wallpaper.R;
28 
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.function.Consumer;
34 
35 class OverlayThemeExtractor {
36 
37     private static final String TAG = "OverlayThemeExtractor";
38 
39     private final Context mContext;
40     private final Map<String, OverlayInfo> mOverlayInfos = new HashMap<>();
41     // List of packages
42     private final String[] mShapePreviewIconPackages;
43 
OverlayThemeExtractor(Context context)44     OverlayThemeExtractor(Context context) {
45         mContext = context;
46         OverlayManager om = context.getSystemService(OverlayManager.class);
47         if (om != null) {
48             Consumer<OverlayInfo> addToMap = overlayInfo -> mOverlayInfos.put(
49                     overlayInfo.getPackageName(), overlayInfo);
50 
51             UserHandle user = UserHandle.of(UserHandle.myUserId());
52             om.getOverlayInfosForTarget(ANDROID_PACKAGE, user).forEach(addToMap);
53             om.getOverlayInfosForTarget(SYSUI_PACKAGE, user).forEach(addToMap);
54             om.getOverlayInfosForTarget(SETTINGS_PACKAGE, user).forEach(addToMap);
55             om.getOverlayInfosForTarget(ResourceConstants.getLauncherPackage(context), user)
56                     .forEach(addToMap);
57             om.getOverlayInfosForTarget(context.getPackageName(), user).forEach(addToMap);
58         }
59         mShapePreviewIconPackages = context.getResources().getStringArray(
60                 R.array.icon_shape_preview_packages);
61     }
62 
isAvailable()63     boolean isAvailable() {
64         return !mOverlayInfos.isEmpty();
65     }
66 
addColorOverlay(Builder builder, String colorOverlayPackage)67     void addColorOverlay(Builder builder, String colorOverlayPackage)
68             throws NameNotFoundException {
69         if (!TextUtils.isEmpty(colorOverlayPackage)) {
70             builder.addOverlayPackage(getOverlayCategory(colorOverlayPackage),
71                     colorOverlayPackage)
72                     .setColorAccentLight(loadColor(ResourceConstants.ACCENT_COLOR_LIGHT_NAME,
73                             colorOverlayPackage))
74                     .setColorAccentDark(loadColor(ResourceConstants.ACCENT_COLOR_DARK_NAME,
75                             colorOverlayPackage));
76         } else {
77             addSystemDefaultColor(builder);
78         }
79     }
80 
addShapeOverlay(Builder builder, String shapeOverlayPackage)81     void addShapeOverlay(Builder builder, String shapeOverlayPackage)
82             throws NameNotFoundException {
83         addShapeOverlay(builder, shapeOverlayPackage, true);
84     }
85 
addShapeOverlay(Builder builder, String shapeOverlayPackage, boolean addPreview)86     void addShapeOverlay(Builder builder, String shapeOverlayPackage, boolean addPreview)
87             throws NameNotFoundException {
88         if (!TextUtils.isEmpty(shapeOverlayPackage)) {
89             builder.addOverlayPackage(getOverlayCategory(shapeOverlayPackage),
90                     shapeOverlayPackage)
91                     .setShapePath(
92                             loadString(ResourceConstants.CONFIG_ICON_MASK, shapeOverlayPackage))
93                     .setBottomSheetCornerRadius(
94                             loadDimen(ResourceConstants.CONFIG_CORNERRADIUS, shapeOverlayPackage));
95         } else {
96             addSystemDefaultShape(builder);
97         }
98         if (addPreview) {
99             addShapePreviewIcons(builder);
100         }
101     }
102 
addShapePreviewIcons(Builder builder)103     private void addShapePreviewIcons(Builder builder) {
104         List<ShapeAppIcon> icons = new ArrayList<>();
105         for (String packageName : mShapePreviewIconPackages) {
106             Drawable icon = null;
107             CharSequence name = null;
108             try {
109                 icon = mContext.getPackageManager().getApplicationIcon(packageName);
110                 // Add the shape icon app name.
111                 ApplicationInfo appInfo = mContext.getPackageManager()
112                         .getApplicationInfo(packageName, /* flag= */ 0);
113                 name = mContext.getPackageManager().getApplicationLabel(appInfo);
114             } catch (NameNotFoundException e) {
115                 Log.d(TAG, "Couldn't find app " + packageName
116                         + ", won't use it for icon shape preview");
117             } finally {
118                 if (icon != null && !TextUtils.isEmpty(name)) {
119                     icons.add(new ShapeAppIcon(icon, name));
120                 }
121             }
122         }
123         builder.setShapePreviewIcons(icons);
124     }
125 
addNoPreviewIconOverlay(Builder builder, String overlayPackage)126     void addNoPreviewIconOverlay(Builder builder, String overlayPackage) {
127         if (!TextUtils.isEmpty(overlayPackage)) {
128             builder.addOverlayPackage(getOverlayCategory(overlayPackage),
129                     overlayPackage);
130         }
131     }
132 
addSysUiIconOverlay(Builder builder, String iconSysUiOverlayPackage)133     void addSysUiIconOverlay(Builder builder, String iconSysUiOverlayPackage)
134             throws NameNotFoundException {
135         if (!TextUtils.isEmpty(iconSysUiOverlayPackage)) {
136             addIconOverlay(builder, iconSysUiOverlayPackage);
137         }
138     }
139 
addAndroidIconOverlay(Builder builder, String iconAndroidOverlayPackage)140     void addAndroidIconOverlay(Builder builder, String iconAndroidOverlayPackage)
141             throws NameNotFoundException {
142         if (!TextUtils.isEmpty(iconAndroidOverlayPackage)) {
143             addIconOverlay(builder, iconAndroidOverlayPackage, ICONS_FOR_PREVIEW);
144         } else {
145             addSystemDefaultIcons(builder, ANDROID_PACKAGE, ICONS_FOR_PREVIEW);
146         }
147     }
148 
addIconOverlay(Builder builder, String packageName, String... previewIcons)149     void addIconOverlay(Builder builder, String packageName, String... previewIcons)
150             throws NameNotFoundException {
151         builder.addOverlayPackage(getOverlayCategory(packageName), packageName);
152         for (String iconName : previewIcons) {
153             builder.addIcon(loadIconPreviewDrawable(iconName, packageName, false));
154         }
155     }
156 
addFontOverlay(Builder builder, String fontOverlayPackage)157     void addFontOverlay(Builder builder, String fontOverlayPackage)
158             throws NameNotFoundException {
159         if (!TextUtils.isEmpty(fontOverlayPackage)) {
160             builder.addOverlayPackage(getOverlayCategory(fontOverlayPackage),
161                     fontOverlayPackage)
162                     .setBodyFontFamily(loadTypeface(
163                             ResourceConstants.CONFIG_BODY_FONT_FAMILY,
164                             fontOverlayPackage))
165                     .setHeadlineFontFamily(loadTypeface(
166                             ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY,
167                             fontOverlayPackage));
168         } else {
169             addSystemDefaultFont(builder);
170         }
171     }
172 
addSystemDefaultIcons(Builder builder, String packageName, String... previewIcons)173     void addSystemDefaultIcons(Builder builder, String packageName,
174             String... previewIcons) {
175         try {
176             for (String iconName : previewIcons) {
177                 builder.addIcon(loadIconPreviewDrawable(iconName, packageName, true));
178             }
179         } catch (NameNotFoundException | NotFoundException e) {
180             Log.w(TAG, "Didn't find android package icons, will skip preview", e);
181         }
182     }
183 
addSystemDefaultShape(Builder builder)184     void addSystemDefaultShape(Builder builder) {
185         Resources system = Resources.getSystem();
186         String iconMaskPath = system.getString(
187                 system.getIdentifier(ResourceConstants.CONFIG_ICON_MASK,
188                         "string", ResourceConstants.ANDROID_PACKAGE));
189         builder.setShapePath(iconMaskPath)
190                 .setBottomSheetCornerRadius(
191                         system.getDimensionPixelOffset(
192                                 system.getIdentifier(ResourceConstants.CONFIG_CORNERRADIUS,
193                                         "dimen", ResourceConstants.ANDROID_PACKAGE)));
194     }
195 
addSystemDefaultColor(Builder builder)196     void addSystemDefaultColor(Builder builder) {
197         Resources system = Resources.getSystem();
198         int colorAccentLight = system.getColor(
199                 system.getIdentifier(ResourceConstants.ACCENT_COLOR_LIGHT_NAME, "color",
200                         ResourceConstants.ANDROID_PACKAGE), null);
201         builder.setColorAccentLight(colorAccentLight);
202 
203         int colorAccentDark = system.getColor(
204                 system.getIdentifier(ResourceConstants.ACCENT_COLOR_DARK_NAME, "color",
205                         ResourceConstants.ANDROID_PACKAGE), null);
206         builder.setColorAccentDark(colorAccentDark);
207     }
208 
addSystemDefaultFont(Builder builder)209     void addSystemDefaultFont(Builder builder) {
210         Resources system = Resources.getSystem();
211         String headlineFontFamily = system.getString(system.getIdentifier(
212                 ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY, "string",
213                 ResourceConstants.ANDROID_PACKAGE));
214         String bodyFontFamily = system.getString(system.getIdentifier(
215                 ResourceConstants.CONFIG_BODY_FONT_FAMILY,
216                 "string", ResourceConstants.ANDROID_PACKAGE));
217         builder.setHeadlineFontFamily(Typeface.create(headlineFontFamily, Typeface.NORMAL))
218                 .setBodyFontFamily(Typeface.create(bodyFontFamily, Typeface.NORMAL));
219     }
220 
loadTypeface(String configName, String fontOverlayPackage)221     Typeface loadTypeface(String configName, String fontOverlayPackage)
222             throws NameNotFoundException, NotFoundException {
223 
224         // TODO(santie): check for font being present in system
225 
226         Resources overlayRes = mContext.getPackageManager()
227                 .getResourcesForApplication(fontOverlayPackage);
228 
229         String fontFamily = overlayRes.getString(overlayRes.getIdentifier(configName,
230                 "string", fontOverlayPackage));
231         return Typeface.create(fontFamily, Typeface.NORMAL);
232     }
233 
loadColor(String colorName, String colorPackage)234     int loadColor(String colorName, String colorPackage)
235             throws NameNotFoundException, NotFoundException {
236 
237         Resources overlayRes = mContext.getPackageManager()
238                 .getResourcesForApplication(colorPackage);
239         return overlayRes.getColor(overlayRes.getIdentifier(colorName, "color", colorPackage),
240                 null);
241     }
242 
loadString(String stringName, String packageName)243     String loadString(String stringName, String packageName)
244             throws NameNotFoundException, NotFoundException {
245 
246         Resources overlayRes =
247                 mContext.getPackageManager().getResourcesForApplication(
248                         packageName);
249         return overlayRes.getString(overlayRes.getIdentifier(stringName, "string", packageName));
250     }
251 
252     @Dimension
loadDimen(String dimenName, String packageName)253     int loadDimen(String dimenName, String packageName)
254             throws NameNotFoundException, NotFoundException {
255 
256         Resources overlayRes =
257                 mContext.getPackageManager().getResourcesForApplication(
258                         packageName);
259         return overlayRes.getDimensionPixelOffset(overlayRes.getIdentifier(
260                 dimenName, "dimen", packageName));
261     }
262 
loadBoolean(String booleanName, String packageName)263     boolean loadBoolean(String booleanName, String packageName)
264             throws NameNotFoundException, NotFoundException {
265 
266         Resources overlayRes =
267                 mContext.getPackageManager().getResourcesForApplication(
268                         packageName);
269         return overlayRes.getBoolean(overlayRes.getIdentifier(
270                 booleanName, "boolean", packageName));
271     }
272 
loadIconPreviewDrawable(String drawableName, String packageName, boolean fromSystem)273     Drawable loadIconPreviewDrawable(String drawableName, String packageName,
274             boolean fromSystem) throws NameNotFoundException, NotFoundException {
275 
276         Resources packageRes =
277                 mContext.getPackageManager().getResourcesForApplication(
278                         packageName);
279         Resources res = fromSystem ? Resources.getSystem() : packageRes;
280         return res.getDrawable(
281                 packageRes.getIdentifier(drawableName, "drawable", packageName), null);
282     }
283 
284     @Nullable
getOverlayCategory(String packageName)285     String getOverlayCategory(String packageName) {
286         OverlayInfo info = mOverlayInfos.get(packageName);
287         return info != null ? info.getCategory() : null;
288     }
289 
getShapePreviewIconPackages()290     String[] getShapePreviewIconPackages() {
291         return mShapePreviewIconPackages;
292     }
293 }