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