| package com.android.customization.model.theme; |
| |
| import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE; |
| import static com.android.customization.model.ResourceConstants.ICONS_FOR_PREVIEW; |
| import static com.android.customization.model.ResourceConstants.SETTINGS_PACKAGE; |
| import static com.android.customization.model.ResourceConstants.SYSUI_PACKAGE; |
| |
| import android.content.Context; |
| import android.content.om.OverlayInfo; |
| import android.content.om.OverlayManager; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.content.res.Resources; |
| import android.content.res.Resources.NotFoundException; |
| import android.graphics.Typeface; |
| import android.graphics.drawable.Drawable; |
| import android.os.UserHandle; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import androidx.annotation.Dimension; |
| import androidx.annotation.Nullable; |
| |
| import com.android.customization.model.ResourceConstants; |
| import com.android.customization.model.theme.ThemeBundle.Builder; |
| import com.android.customization.model.theme.ThemeBundle.PreviewInfo.ShapeAppIcon; |
| import com.android.wallpaper.R; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.function.Consumer; |
| |
| class OverlayThemeExtractor { |
| |
| private static final String TAG = "OverlayThemeExtractor"; |
| |
| private final Context mContext; |
| private final Map<String, OverlayInfo> mOverlayInfos = new HashMap<>(); |
| // List of packages |
| private final String[] mShapePreviewIconPackages; |
| |
| OverlayThemeExtractor(Context context) { |
| mContext = context; |
| OverlayManager om = context.getSystemService(OverlayManager.class); |
| if (om != null) { |
| Consumer<OverlayInfo> addToMap = overlayInfo -> mOverlayInfos.put( |
| overlayInfo.getPackageName(), overlayInfo); |
| |
| UserHandle user = UserHandle.of(UserHandle.myUserId()); |
| om.getOverlayInfosForTarget(ANDROID_PACKAGE, user).forEach(addToMap); |
| om.getOverlayInfosForTarget(SYSUI_PACKAGE, user).forEach(addToMap); |
| om.getOverlayInfosForTarget(SETTINGS_PACKAGE, user).forEach(addToMap); |
| om.getOverlayInfosForTarget(ResourceConstants.getLauncherPackage(context), user) |
| .forEach(addToMap); |
| om.getOverlayInfosForTarget(context.getPackageName(), user).forEach(addToMap); |
| } |
| mShapePreviewIconPackages = context.getResources().getStringArray( |
| R.array.icon_shape_preview_packages); |
| } |
| |
| boolean isAvailable() { |
| return !mOverlayInfos.isEmpty(); |
| } |
| |
| void addColorOverlay(Builder builder, String colorOverlayPackage) |
| throws NameNotFoundException { |
| if (!TextUtils.isEmpty(colorOverlayPackage)) { |
| builder.addOverlayPackage(getOverlayCategory(colorOverlayPackage), |
| colorOverlayPackage) |
| .setColorAccentLight(loadColor(ResourceConstants.ACCENT_COLOR_LIGHT_NAME, |
| colorOverlayPackage)) |
| .setColorAccentDark(loadColor(ResourceConstants.ACCENT_COLOR_DARK_NAME, |
| colorOverlayPackage)); |
| } else { |
| addSystemDefaultColor(builder); |
| } |
| } |
| |
| void addShapeOverlay(Builder builder, String shapeOverlayPackage) |
| throws NameNotFoundException { |
| addShapeOverlay(builder, shapeOverlayPackage, true); |
| } |
| |
| void addShapeOverlay(Builder builder, String shapeOverlayPackage, boolean addPreview) |
| throws NameNotFoundException { |
| if (!TextUtils.isEmpty(shapeOverlayPackage)) { |
| builder.addOverlayPackage(getOverlayCategory(shapeOverlayPackage), |
| shapeOverlayPackage) |
| .setShapePath( |
| loadString(ResourceConstants.CONFIG_ICON_MASK, shapeOverlayPackage)) |
| .setBottomSheetCornerRadius( |
| loadDimen(ResourceConstants.CONFIG_CORNERRADIUS, shapeOverlayPackage)); |
| } else { |
| addSystemDefaultShape(builder); |
| } |
| if (addPreview) { |
| addShapePreviewIcons(builder); |
| } |
| } |
| |
| private void addShapePreviewIcons(Builder builder) { |
| List<ShapeAppIcon> icons = new ArrayList<>(); |
| for (String packageName : mShapePreviewIconPackages) { |
| Drawable icon = null; |
| CharSequence name = null; |
| try { |
| icon = mContext.getPackageManager().getApplicationIcon(packageName); |
| // Add the shape icon app name. |
| ApplicationInfo appInfo = mContext.getPackageManager() |
| .getApplicationInfo(packageName, /* flag= */ 0); |
| name = mContext.getPackageManager().getApplicationLabel(appInfo); |
| } catch (NameNotFoundException e) { |
| Log.d(TAG, "Couldn't find app " + packageName |
| + ", won't use it for icon shape preview"); |
| } finally { |
| if (icon != null && !TextUtils.isEmpty(name)) { |
| icons.add(new ShapeAppIcon(icon, name)); |
| } |
| } |
| } |
| builder.setShapePreviewIcons(icons); |
| } |
| |
| void addNoPreviewIconOverlay(Builder builder, String overlayPackage) { |
| if (!TextUtils.isEmpty(overlayPackage)) { |
| builder.addOverlayPackage(getOverlayCategory(overlayPackage), |
| overlayPackage); |
| } |
| } |
| |
| void addSysUiIconOverlay(Builder builder, String iconSysUiOverlayPackage) |
| throws NameNotFoundException { |
| if (!TextUtils.isEmpty(iconSysUiOverlayPackage)) { |
| addIconOverlay(builder, iconSysUiOverlayPackage); |
| } |
| } |
| |
| void addAndroidIconOverlay(Builder builder, String iconAndroidOverlayPackage) |
| throws NameNotFoundException { |
| if (!TextUtils.isEmpty(iconAndroidOverlayPackage)) { |
| addIconOverlay(builder, iconAndroidOverlayPackage, ICONS_FOR_PREVIEW); |
| } else { |
| addSystemDefaultIcons(builder, ANDROID_PACKAGE, ICONS_FOR_PREVIEW); |
| } |
| } |
| |
| void addIconOverlay(Builder builder, String packageName, String... previewIcons) |
| throws NameNotFoundException { |
| builder.addOverlayPackage(getOverlayCategory(packageName), packageName); |
| for (String iconName : previewIcons) { |
| builder.addIcon(loadIconPreviewDrawable(iconName, packageName, false)); |
| } |
| } |
| |
| void addFontOverlay(Builder builder, String fontOverlayPackage) |
| throws NameNotFoundException { |
| if (!TextUtils.isEmpty(fontOverlayPackage)) { |
| builder.addOverlayPackage(getOverlayCategory(fontOverlayPackage), |
| fontOverlayPackage) |
| .setBodyFontFamily(loadTypeface( |
| ResourceConstants.CONFIG_BODY_FONT_FAMILY, |
| fontOverlayPackage)) |
| .setHeadlineFontFamily(loadTypeface( |
| ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY, |
| fontOverlayPackage)); |
| } else { |
| addSystemDefaultFont(builder); |
| } |
| } |
| |
| void addSystemDefaultIcons(Builder builder, String packageName, |
| String... previewIcons) { |
| try { |
| for (String iconName : previewIcons) { |
| builder.addIcon(loadIconPreviewDrawable(iconName, packageName, true)); |
| } |
| } catch (NameNotFoundException | NotFoundException e) { |
| Log.w(TAG, "Didn't find android package icons, will skip preview", e); |
| } |
| } |
| |
| void addSystemDefaultShape(Builder builder) { |
| Resources system = Resources.getSystem(); |
| String iconMaskPath = system.getString( |
| system.getIdentifier(ResourceConstants.CONFIG_ICON_MASK, |
| "string", ResourceConstants.ANDROID_PACKAGE)); |
| builder.setShapePath(iconMaskPath) |
| .setBottomSheetCornerRadius( |
| system.getDimensionPixelOffset( |
| system.getIdentifier(ResourceConstants.CONFIG_CORNERRADIUS, |
| "dimen", ResourceConstants.ANDROID_PACKAGE))); |
| } |
| |
| void addSystemDefaultColor(Builder builder) { |
| Resources system = Resources.getSystem(); |
| int colorAccentLight = system.getColor( |
| system.getIdentifier(ResourceConstants.ACCENT_COLOR_LIGHT_NAME, "color", |
| ResourceConstants.ANDROID_PACKAGE), null); |
| builder.setColorAccentLight(colorAccentLight); |
| |
| int colorAccentDark = system.getColor( |
| system.getIdentifier(ResourceConstants.ACCENT_COLOR_DARK_NAME, "color", |
| ResourceConstants.ANDROID_PACKAGE), null); |
| builder.setColorAccentDark(colorAccentDark); |
| } |
| |
| void addSystemDefaultFont(Builder builder) { |
| Resources system = Resources.getSystem(); |
| String headlineFontFamily = system.getString(system.getIdentifier( |
| ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY, "string", |
| ResourceConstants.ANDROID_PACKAGE)); |
| String bodyFontFamily = system.getString(system.getIdentifier( |
| ResourceConstants.CONFIG_BODY_FONT_FAMILY, |
| "string", ResourceConstants.ANDROID_PACKAGE)); |
| builder.setHeadlineFontFamily(Typeface.create(headlineFontFamily, Typeface.NORMAL)) |
| .setBodyFontFamily(Typeface.create(bodyFontFamily, Typeface.NORMAL)); |
| } |
| |
| Typeface loadTypeface(String configName, String fontOverlayPackage) |
| throws NameNotFoundException, NotFoundException { |
| |
| // TODO(santie): check for font being present in system |
| |
| Resources overlayRes = mContext.getPackageManager() |
| .getResourcesForApplication(fontOverlayPackage); |
| |
| String fontFamily = overlayRes.getString(overlayRes.getIdentifier(configName, |
| "string", fontOverlayPackage)); |
| return Typeface.create(fontFamily, Typeface.NORMAL); |
| } |
| |
| int loadColor(String colorName, String colorPackage) |
| throws NameNotFoundException, NotFoundException { |
| |
| Resources overlayRes = mContext.getPackageManager() |
| .getResourcesForApplication(colorPackage); |
| return overlayRes.getColor(overlayRes.getIdentifier(colorName, "color", colorPackage), |
| null); |
| } |
| |
| String loadString(String stringName, String packageName) |
| throws NameNotFoundException, NotFoundException { |
| |
| Resources overlayRes = |
| mContext.getPackageManager().getResourcesForApplication( |
| packageName); |
| return overlayRes.getString(overlayRes.getIdentifier(stringName, "string", packageName)); |
| } |
| |
| @Dimension |
| int loadDimen(String dimenName, String packageName) |
| throws NameNotFoundException, NotFoundException { |
| |
| Resources overlayRes = |
| mContext.getPackageManager().getResourcesForApplication( |
| packageName); |
| return overlayRes.getDimensionPixelOffset(overlayRes.getIdentifier( |
| dimenName, "dimen", packageName)); |
| } |
| |
| boolean loadBoolean(String booleanName, String packageName) |
| throws NameNotFoundException, NotFoundException { |
| |
| Resources overlayRes = |
| mContext.getPackageManager().getResourcesForApplication( |
| packageName); |
| return overlayRes.getBoolean(overlayRes.getIdentifier( |
| booleanName, "boolean", packageName)); |
| } |
| |
| Drawable loadIconPreviewDrawable(String drawableName, String packageName, |
| boolean fromSystem) throws NameNotFoundException, NotFoundException { |
| |
| Resources packageRes = |
| mContext.getPackageManager().getResourcesForApplication( |
| packageName); |
| Resources res = fromSystem ? Resources.getSystem() : packageRes; |
| return res.getDrawable( |
| packageRes.getIdentifier(drawableName, "drawable", packageName), null); |
| } |
| |
| @Nullable |
| String getOverlayCategory(String packageName) { |
| OverlayInfo info = mOverlayInfos.get(packageName); |
| return info != null ? info.getCategory() : null; |
| } |
| |
| String[] getShapePreviewIconPackages() { |
| return mShapePreviewIconPackages; |
| } |
| } |