• 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.custom;
17 
18 import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
19 import static com.android.customization.model.ResourceConstants.CONFIG_CORNERRADIUS;
20 import static com.android.customization.model.ResourceConstants.CONFIG_ICON_MASK;
21 import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
22 import static com.android.customization.model.ResourceConstants.PATH_SIZE;
23 
24 import android.content.Context;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.res.Resources;
29 import android.content.res.Resources.NotFoundException;
30 import android.graphics.Path;
31 import android.graphics.drawable.AdaptiveIconDrawable;
32 import android.graphics.drawable.Drawable;
33 import android.graphics.drawable.ShapeDrawable;
34 import android.graphics.drawable.shapes.PathShape;
35 import android.text.TextUtils;
36 import android.util.Log;
37 
38 import androidx.annotation.Dimension;
39 import androidx.core.graphics.PathParser;
40 
41 import com.android.customization.model.ResourceConstants;
42 import com.android.customization.model.theme.OverlayManagerCompat;
43 import com.android.customization.model.theme.ThemeBundle.PreviewInfo.ShapeAppIcon;
44 import com.android.customization.model.theme.custom.ThemeComponentOption.ShapeOption;
45 import com.android.customization.widget.DynamicAdaptiveIconDrawable;
46 import com.android.wallpaper.R;
47 
48 import java.util.ArrayList;
49 import java.util.List;
50 
51 /**
52  * Implementation of {@link ThemeComponentOptionProvider} that reads {@link ShapeOption}s from
53  * icon overlays.
54  */
55 public class ShapeOptionsProvider extends ThemeComponentOptionProvider<ShapeOption> {
56 
57     private static final String TAG = "ShapeOptionsProvider";
58     private final String[] mShapePreviewIconPackages;
59     private int mThumbSize;
60 
ShapeOptionsProvider(Context context, OverlayManagerCompat manager)61     public ShapeOptionsProvider(Context context, OverlayManagerCompat manager) {
62         super(context, manager, OVERLAY_CATEGORY_SHAPE);
63         mShapePreviewIconPackages = context.getResources().getStringArray(
64                 R.array.icon_shape_preview_packages);
65         mThumbSize = mContext.getResources().getDimensionPixelSize(
66                 R.dimen.component_shape_thumb_size);
67     }
68 
69     @Override
loadOptions()70     protected void loadOptions() {
71         addDefault();
72         for (String overlayPackage : mOverlayPackages) {
73             try {
74                 Path path = loadPath(mContext.getPackageManager()
75                         .getResourcesForApplication(overlayPackage), overlayPackage);
76                 PackageManager pm = mContext.getPackageManager();
77                 String label = pm.getApplicationInfo(overlayPackage, 0).loadLabel(pm).toString();
78                 mOptions.add(new ShapeOption(overlayPackage, label, path,
79                         loadCornerRadius(overlayPackage), createShapeDrawable(path),
80                         getShapedAppIcons(path)));
81             } catch (NameNotFoundException | NotFoundException e) {
82                 Log.w(TAG, String.format("Couldn't load shape overlay %s, will skip it",
83                         overlayPackage), e);
84             }
85         }
86     }
87 
addDefault()88     private void addDefault() {
89         Resources system = Resources.getSystem();
90         Path path = loadPath(system, ANDROID_PACKAGE);
91         mOptions.add(new ShapeOption(null, mContext.getString(R.string.default_theme_title), path,
92                 system.getDimensionPixelOffset(
93                         system.getIdentifier(ResourceConstants.CONFIG_CORNERRADIUS,
94                                 "dimen", ResourceConstants.ANDROID_PACKAGE)),
95                 createShapeDrawable(path), getShapedAppIcons(path)));
96     }
97 
createShapeDrawable(Path path)98     private ShapeDrawable createShapeDrawable(Path path) {
99         PathShape shape = new PathShape(path, PATH_SIZE, PATH_SIZE);
100         ShapeDrawable shapeDrawable = new ShapeDrawable(shape);
101         shapeDrawable.setIntrinsicHeight(mThumbSize);
102         shapeDrawable.setIntrinsicWidth(mThumbSize);
103         return shapeDrawable;
104     }
105 
getShapedAppIcons(Path path)106     private List<ShapeAppIcon> getShapedAppIcons(Path path) {
107         List<ShapeAppIcon> shapedAppIcons = new ArrayList<>();
108         for (String packageName : mShapePreviewIconPackages) {
109             Drawable icon = null;
110             CharSequence name = null;
111             try {
112                 Drawable appIcon = mContext.getPackageManager().getApplicationIcon(packageName);
113                 if (appIcon instanceof AdaptiveIconDrawable) {
114                     AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) appIcon;
115                     icon = new DynamicAdaptiveIconDrawable(adaptiveIcon.getBackground(),
116                             adaptiveIcon.getForeground(), path);
117 
118                     ApplicationInfo appInfo = mContext.getPackageManager()
119                             .getApplicationInfo(packageName, /* flag= */ 0);
120                     name = mContext.getPackageManager().getApplicationLabel(appInfo);
121                 }
122             } catch (NameNotFoundException e) {
123                 Log.d(TAG, "Couldn't find app " + packageName
124                         + ", won't use it for icon shape preview");
125             } finally {
126                 if (icon != null && !TextUtils.isEmpty(name)) {
127                     shapedAppIcons.add(new ShapeAppIcon(icon, name));
128                 }
129             }
130         }
131         return shapedAppIcons;
132     }
133 
loadPath(Resources overlayRes, String packageName)134     private Path loadPath(Resources overlayRes, String packageName) {
135         String shape = overlayRes.getString(overlayRes.getIdentifier(CONFIG_ICON_MASK, "string",
136                 packageName));
137 
138         if (!TextUtils.isEmpty(shape)) {
139             return PathParser.createPathFromPathData(shape);
140         }
141         return null;
142     }
143 
144     @Dimension
loadCornerRadius(String packageName)145     private int loadCornerRadius(String packageName)
146             throws NameNotFoundException, NotFoundException {
147 
148         Resources overlayRes =
149                 mContext.getPackageManager().getResourcesForApplication(
150                         packageName);
151         return overlayRes.getDimensionPixelOffset(overlayRes.getIdentifier(
152                 CONFIG_CORNERRADIUS, "dimen", packageName));
153     }
154 }
155