Merge "Import translations. DO NOT MERGE" into ub-launcher3-qt-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 319aa0a..217b0ef 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -67,7 +67,7 @@
<string name="option_previewed_description"><xliff:g name="style_name">%1$s</xliff:g>, currently previewed</string>
<!-- Sample text used to show a preview of a selected font [CHAR LIMIT=3] -->
- <string name="theme_font_example">ABC</string>
+ <string name="theme_font_example" translatable="false">ABC</string>
<!-- Content description for previewing a style, describing each of their components. [CHAR_LIMIT=NONE] -->
<string name="theme_description">Font: <xliff:g name="font_name">%1$s</xliff:g>, icons: <xliff:g name="icon_name">%2$s</xliff:g>, shape: <xliff:g name="shape_name">%3$s</xliff:g>, color: <xliff:g name="color_name">%4$s</xliff:g> </string>
@@ -105,6 +105,9 @@
<!-- Message shown when a theme has been applied successfully in the system [CHAR LIMIT=NONE] -->
<string name="applied_theme_msg">Style applied</string>
+ <!-- Message shown when a clock has been applied successfully in the system [CHAR LIMIT=NONE] -->
+ <string name="applied_clock_msg">Clock applied</string>
+
<!-- Message shown when a theme couldn't be applied in the system because of an error
[CHAR LIMIT=NONE] -->
<string name="apply_theme_error_msg">There was a problem applying the style</string>
@@ -117,9 +120,10 @@
[CHAR LIMIT=20] -->
<string name="custom_theme_previous">Previous</string>
- <!-- Label for a system Style/Theme (combination of fonts/colors/icons) that is defined and
- customized by the user [CHAR LIMIT=15] -->
- <string name="custom_theme_title">Custom</string>
+ <!-- Generic label for one system Style/Theme (combination of fonts/colors/icons) that is
+ defined and customized by the user (note there could be more than one so the label includes
+ a number, eg "Custom 1, Custom 2, etc") [CHAR LIMIT=15] -->
+ <string name="custom_theme_title">Custom <xliff:g name="custom_num" example="1">%1$d</xliff:g></string>
<!-- Title for a screen that lets the user define their custom system Style/Theme
[CHAR LIMIT=30] -->
diff --git a/src/com/android/customization/model/theme/DefaultThemeProvider.java b/src/com/android/customization/model/theme/DefaultThemeProvider.java
index ac32312..c1c6bbe 100644
--- a/src/com/android/customization/model/theme/DefaultThemeProvider.java
+++ b/src/com/android/customization/model/theme/DefaultThemeProvider.java
@@ -15,11 +15,7 @@
*/
package com.android.customization.model.theme;
-import static com.android.customization.model.ResourceConstants.ACCENT_COLOR_DARK_NAME;
-import static com.android.customization.model.ResourceConstants.ACCENT_COLOR_LIGHT_NAME;
import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
-import static com.android.customization.model.ResourceConstants.CONFIG_CORNERRADIUS;
-import static com.android.customization.model.ResourceConstants.CONFIG_ICON_MASK;
import static com.android.customization.model.ResourceConstants.ICONS_FOR_PREVIEW;
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_COLOR;
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
@@ -29,31 +25,22 @@
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_SYSUI;
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_THEMEPICKER;
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_SHAPE;
-import static com.android.customization.model.ResourceConstants.SETTINGS_PACKAGE;
import static com.android.customization.model.ResourceConstants.SYSUI_PACKAGE;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.om.OverlayInfo;
-import android.content.om.OverlayManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
-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.service.wallpaper.WallpaperService;
import android.text.TextUtils;
import android.util.Log;
-import androidx.annotation.Dimension;
import androidx.annotation.Nullable;
import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
-import com.android.customization.model.ResourceConstants;
import com.android.customization.model.ResourcesApkProvider;
import com.android.customization.model.theme.ThemeBundle.Builder;
import com.android.customization.model.theme.custom.CustomTheme;
@@ -64,6 +51,7 @@
import com.bumptech.glide.request.RequestOptions;
+import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParserException;
@@ -74,7 +62,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.function.Consumer;
/**
* Default implementation of {@link ThemeBundleProvider} that reads Themes' overlays from a stub APK.
@@ -99,30 +86,17 @@
private static final String WALLPAPER_ACTION_PREFIX = "theme_wallpaper_action_";
private static final String DEFAULT_THEME_NAME= "default";
+ private static final String THEME_TITLE_FIELD = "_theme_title";
+ private static final String THEME_ID_FIELD = "_theme_id";
- // List of packages
- private final String[] mShapePreviewIconPackages;
+ private final OverlayThemeExtractor mOverlayProvider;
private List<ThemeBundle> mThemes;
- private Map<String, OverlayInfo> mOverlayInfos;
private final CustomizationPreferences mCustomizationPreferences;
public DefaultThemeProvider(Context context, CustomizationPreferences customizationPrefs) {
super(context, context.getString(R.string.themes_stub_package));
- OverlayManager om = context.getSystemService(OverlayManager.class);
+ mOverlayProvider = new OverlayThemeExtractor(context);
mCustomizationPreferences = customizationPrefs;
- mOverlayInfos = new HashMap<>();
-
- 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);
}
@Override
@@ -154,35 +128,36 @@
"string", mStubPackageName)));
String shapeOverlayPackage = getOverlayPackage(SHAPE_PREFIX, themeName);
- addShapeOverlay(builder, shapeOverlayPackage);
+ mOverlayProvider.addShapeOverlay(builder, shapeOverlayPackage);
String fontOverlayPackage = getOverlayPackage(FONT_PREFIX, themeName);
- addFontOverlay(builder, fontOverlayPackage);
+ mOverlayProvider.addFontOverlay(builder, fontOverlayPackage);
String colorOverlayPackage = getOverlayPackage(COLOR_PREFIX, themeName);
- addColorOverlay(builder, colorOverlayPackage);
+ mOverlayProvider.addColorOverlay(builder, colorOverlayPackage);
String iconAndroidOverlayPackage = getOverlayPackage(ICON_ANDROID_PREFIX,
themeName);
- addAndroidIconOverlay(builder, iconAndroidOverlayPackage);
+ mOverlayProvider.addAndroidIconOverlay(builder, iconAndroidOverlayPackage);
String iconSysUiOverlayPackage = getOverlayPackage(ICON_SYSUI_PREFIX, themeName);
- addSysUiIconOverlay(builder, iconSysUiOverlayPackage);
+ mOverlayProvider.addSysUiIconOverlay(builder, iconSysUiOverlayPackage);
String iconLauncherOverlayPackage = getOverlayPackage(ICON_LAUNCHER_PREFIX,
themeName);
- addNoPreviewIconOverlay(builder, iconLauncherOverlayPackage);
+ mOverlayProvider.addNoPreviewIconOverlay(builder, iconLauncherOverlayPackage);
String iconThemePickerOverlayPackage = getOverlayPackage(ICON_THEMEPICKER_PREFIX,
themeName);
- addNoPreviewIconOverlay(builder, iconThemePickerOverlayPackage);
+ mOverlayProvider.addNoPreviewIconOverlay(builder,
+ iconThemePickerOverlayPackage);
String iconSettingsOverlayPackage = getOverlayPackage(ICON_SETTINGS_PREFIX,
themeName);
- addNoPreviewIconOverlay(builder, iconSettingsOverlayPackage);
+ mOverlayProvider.addNoPreviewIconOverlay(builder, iconSettingsOverlayPackage);
addWallpaper(themeName, builder);
@@ -193,7 +168,7 @@
}
}
- addCustomTheme();
+ addCustomThemes();
}
private void addWallpaper(String themeName, Builder builder) {
@@ -235,9 +210,7 @@
LiveWallpaperInfo liveInfo = new LiveWallpaperInfo(wallpaperInfo);
builder.setLiveWallpaperInfo(liveInfo)
.setWallpaperAsset(liveInfo.getThumbAsset(mContext));
- } catch (XmlPullParserException e) {
- Log.w(TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
- } catch (IOException e) {
+ } catch (XmlPullParserException | IOException e) {
Log.w(TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
}
}
@@ -248,97 +221,6 @@
}
}
- private void addColorOverlay(Builder builder, String colorOverlayPackage)
- throws NameNotFoundException {
- if (!TextUtils.isEmpty(colorOverlayPackage)) {
- builder.addOverlayPackage(getOverlayCategory(colorOverlayPackage),
- colorOverlayPackage)
- .setColorAccentLight(loadColor(ACCENT_COLOR_LIGHT_NAME,
- colorOverlayPackage))
- .setColorAccentDark(loadColor(ACCENT_COLOR_DARK_NAME,
- colorOverlayPackage));
- } else {
- addSystemDefaultColor(builder);
- }
- }
-
- private void addShapeOverlay(Builder builder, String shapeOverlayPackage)
- throws NameNotFoundException {
- if (!TextUtils.isEmpty(shapeOverlayPackage)) {
- builder.addOverlayPackage(getOverlayCategory(shapeOverlayPackage),
- shapeOverlayPackage)
- .setShapePath(loadString(CONFIG_ICON_MASK, shapeOverlayPackage))
- .setBottomSheetCornerRadius(loadDimen(CONFIG_CORNERRADIUS, shapeOverlayPackage))
- /*.setUseRoundIcon(loadBoolean(CONFIG_USE_ROUNDICON, shapeOverlayPackage))*/;
- } else {
- builder.setShapePath(mContext.getResources().getString(
- Resources.getSystem().getIdentifier(CONFIG_ICON_MASK, "string",
- ANDROID_PACKAGE)))
- .setBottomSheetCornerRadius(
- mContext.getResources().getDimensionPixelOffset(
- Resources.getSystem().getIdentifier(CONFIG_CORNERRADIUS,
- "dimen", ANDROID_PACKAGE)))
- /*.setUseRoundIcon(mContext.getResources().getBoolean(
- Resources.getSystem().getIdentifier(CONFIG_USE_ROUNDICON,
- "boolean", ANDROID_PACKAGE)))*/;
- }
- for (String packageName : mShapePreviewIconPackages) {
- try {
- builder.addShapePreviewIcon(
- mContext.getPackageManager().getApplicationIcon(packageName));
- } catch (NameNotFoundException e) {
- Log.d(TAG, "Couldn't find app " + packageName
- + ", won't use it for icon shape preview");
- }
- }
- }
-
- private void addNoPreviewIconOverlay(Builder builder, String overlayPackage) {
- if (!TextUtils.isEmpty(overlayPackage)) {
- builder.addOverlayPackage(getOverlayCategory(overlayPackage),
- overlayPackage);
- }
- }
-
- private void addSysUiIconOverlay(Builder builder, String iconSysUiOverlayPackage)
- throws NameNotFoundException {
- if (!TextUtils.isEmpty(iconSysUiOverlayPackage)) {
- addIconOverlay(builder, iconSysUiOverlayPackage);
- }
- }
-
- private 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);
- }
- }
- private 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));
- }
- }
-
- private 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);
- }
- }
-
/**
* Default theme requires different treatment: if there are overlay packages specified in the
* stub apk, we'll use those, otherwise we'll get the System default values. But we cannot skip
@@ -358,37 +240,29 @@
String colorOverlayPackage = getOverlayPackage(COLOR_PREFIX, DEFAULT_THEME_NAME);
try {
- builder.addOverlayPackage(getOverlayCategory(colorOverlayPackage), colorOverlayPackage)
- .setColorAccentLight(loadColor(ACCENT_COLOR_LIGHT_NAME, colorOverlayPackage))
- .setColorAccentDark(loadColor(ACCENT_COLOR_DARK_NAME, colorOverlayPackage));
+ mOverlayProvider.addColorOverlay(builder, colorOverlayPackage);
} catch (NameNotFoundException | NotFoundException e) {
- Log.d(TAG, "Didn't find color overlay for default theme, will use system default", e);
- addSystemDefaultColor(builder);
+ Log.d(TAG, "Didn't find color overlay for default theme, will use system default");
+ mOverlayProvider.addSystemDefaultColor(builder);
}
String fontOverlayPackage = getOverlayPackage(FONT_PREFIX, DEFAULT_THEME_NAME);
try {
- builder.addOverlayPackage(getOverlayCategory(fontOverlayPackage), fontOverlayPackage)
- .setBodyFontFamily(loadTypeface(ResourceConstants.CONFIG_BODY_FONT_FAMILY,
- fontOverlayPackage))
- .setHeadlineFontFamily(loadTypeface(
- ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY,
- fontOverlayPackage));
+ mOverlayProvider.addFontOverlay(builder, fontOverlayPackage);
} catch (NameNotFoundException | NotFoundException e) {
- Log.d(TAG, "Didn't find font overlay for default theme, will use system default", e);
- addSystemDefaultFont(builder);
+ Log.d(TAG, "Didn't find font overlay for default theme, will use system default");
+ mOverlayProvider.addSystemDefaultFont(builder);
}
try {
String shapeOverlayPackage = getOverlayPackage(SHAPE_PREFIX, DEFAULT_THEME_NAME);
- builder.addOverlayPackage(getOverlayCategory(shapeOverlayPackage), shapeOverlayPackage)
- .setShapePath(loadString(CONFIG_ICON_MASK, shapeOverlayPackage));
+ mOverlayProvider.addShapeOverlay(builder ,shapeOverlayPackage, false);
} catch (NameNotFoundException | NotFoundException e) {
- Log.d(TAG, "Didn't find shape overlay for default theme, will use system default", e);
- addSystemDefaultShape(builder);
+ Log.d(TAG, "Didn't find shape overlay for default theme, will use system default");
+ mOverlayProvider.addSystemDefaultShape(builder);
}
- for (String packageName : mShapePreviewIconPackages) {
+ for (String packageName : mOverlayProvider.getShapePreviewIconPackages()) {
try {
builder.addShapePreviewIcon(
mContext.getPackageManager().getApplicationIcon(packageName));
@@ -401,24 +275,20 @@
try {
String iconAndroidOverlayPackage = getOverlayPackage(ICON_ANDROID_PREFIX,
DEFAULT_THEME_NAME);
- builder.addOverlayPackage(getOverlayCategory(iconAndroidOverlayPackage),
- iconAndroidOverlayPackage)
- .addIcon(loadIconPreviewDrawable(ICON_ANDROID_PREFIX,
- iconAndroidOverlayPackage, false));
+ mOverlayProvider.addAndroidIconOverlay(builder, iconAndroidOverlayPackage);
} catch (NameNotFoundException | NotFoundException e) {
- Log.d(TAG, "Didn't find Android icons overlay for default theme, using system default",
- e);
- addSystemDefaultIcons(builder, ANDROID_PACKAGE, ICONS_FOR_PREVIEW);
+ Log.d(TAG, "Didn't find Android icons overlay for default theme, using system default");
+ mOverlayProvider.addSystemDefaultIcons(builder, ANDROID_PACKAGE, ICONS_FOR_PREVIEW);
}
try {
String iconSysUiOverlayPackage = getOverlayPackage(ICON_SYSUI_PREFIX,
DEFAULT_THEME_NAME);
- addSysUiIconOverlay(builder, iconSysUiOverlayPackage);
+ mOverlayProvider.addSysUiIconOverlay(builder, iconSysUiOverlayPackage);
} catch (NameNotFoundException | NotFoundException e) {
- Log.d(TAG, "Didn't find SystemUi icons overlay for default theme, using system default",
- e);
- addSystemDefaultIcons(builder, SYSUI_PACKAGE, ICONS_FOR_PREVIEW);
+ Log.d(TAG,
+ "Didn't find SystemUi icons overlay for default theme, using system default");
+ mOverlayProvider.addSystemDefaultIcons(builder, SYSUI_PACKAGE, ICONS_FOR_PREVIEW);
}
addWallpaper(DEFAULT_THEME_NAME, builder);
@@ -426,84 +296,104 @@
mThemes.add(builder.build(mContext));
}
- private 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);
+ @Override
+ public void storeCustomTheme(CustomTheme theme) {
+ if (mThemes == null) {
+ fetch(options -> {
+ addCustomThemeAndStore(theme);
+ }, false);
+ } else {
+ addCustomThemeAndStore(theme);
}
}
- private void addSystemDefaultShape(Builder builder) {
- Resources system = Resources.getSystem();
- String iconMaskPath = system.getString(system.getIdentifier(CONFIG_ICON_MASK,
- "string", ANDROID_PACKAGE));
- builder.setShapePath(iconMaskPath)
- .setBottomSheetCornerRadius(
- system.getDimensionPixelOffset(
- system.getIdentifier(CONFIG_CORNERRADIUS,
- "dimen", ANDROID_PACKAGE)));
+ private void addCustomThemeAndStore(CustomTheme theme) {
+ if (!mThemes.contains(theme)) {
+ mThemes.add(theme);
+ } else {
+ mThemes.replaceAll(t -> theme.equals(t) ? theme : t);
+ }
+ JSONArray themesArray = new JSONArray();
+ mThemes.stream()
+ .filter(themeBundle -> themeBundle instanceof CustomTheme
+ && !themeBundle.getPackagesByCategory().isEmpty())
+ .forEachOrdered(themeBundle -> addThemeBundleToArray(themesArray, themeBundle));
+ mCustomizationPreferences.storeCustomThemes(themesArray.toString());
}
- private void addSystemDefaultColor(Builder builder) {
- Resources system = Resources.getSystem();
- int colorAccentLight = system.getColor(
- system.getIdentifier(ACCENT_COLOR_LIGHT_NAME, "color", ANDROID_PACKAGE), null);
- builder.setColorAccentLight(colorAccentLight);
-
- int colorAccentDark = system.getColor(
- system.getIdentifier(ACCENT_COLOR_DARK_NAME, "color", ANDROID_PACKAGE), null);
- builder.setColorAccentDark(colorAccentDark);
- }
-
- private void addSystemDefaultFont(Builder builder) {
- Resources system = Resources.getSystem();
- String headlineFontFamily = system.getString(system.getIdentifier(
- ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY,"string", ANDROID_PACKAGE));
- String bodyFontFamily = system.getString(system.getIdentifier(
- ResourceConstants.CONFIG_BODY_FONT_FAMILY,
- "string", ANDROID_PACKAGE));
- builder.setHeadlineFontFamily(Typeface.create(headlineFontFamily, Typeface.NORMAL))
- .setBodyFontFamily(Typeface.create(bodyFontFamily, Typeface.NORMAL));
- }
-
- @Override
- public void storeCustomTheme(CustomTheme theme) {
- mCustomizationPreferences.storeCustomTheme(theme.getSerializedPackages());
+ private void addThemeBundleToArray(JSONArray themesArray, ThemeBundle themeBundle) {
+ JSONObject jsonPackages = themeBundle.getJsonPackages();
+ try {
+ jsonPackages.put(THEME_TITLE_FIELD, themeBundle.getTitle());
+ if (themeBundle instanceof CustomTheme) {
+ jsonPackages.put(THEME_ID_FIELD, ((CustomTheme)themeBundle).getId());
+ }
+ } catch (JSONException e) {
+ Log.w("Exception saving theme's title", e);
+ }
+ themesArray.put(jsonPackages);
}
@Override
public void removeCustomTheme(CustomTheme theme) {
- //TODO: add support for multiple custom themes.
- mCustomizationPreferences.storeCustomTheme("");
+ JSONArray themesArray = new JSONArray();
+ mThemes.stream()
+ .filter(themeBundle -> themeBundle instanceof CustomTheme
+ && ((CustomTheme) themeBundle).isDefined())
+ .forEachOrdered(customTheme -> {
+ if (!customTheme.equals(theme)) {
+ addThemeBundleToArray(themesArray, customTheme);
+ }
+ });
+ mCustomizationPreferences.storeCustomThemes(themesArray.toString());
}
- private void addCustomTheme() {
- String serializedTheme = mCustomizationPreferences.getSerializedCustomTheme();
- if (TextUtils.isEmpty(serializedTheme)) {
- mThemes.add(new CustomTheme(mContext.getString(R.string.custom_theme_title),
- new HashMap<>(), null));
- return;
+ private void addCustomThemes() {
+ String serializedThemes = mCustomizationPreferences.getSerializedCustomThemes();
+ int customThemesCount = 0;
+ if (!TextUtils.isEmpty(serializedThemes)) {
+ try {
+ JSONArray customThemes = new JSONArray(serializedThemes);
+ for (int i = 0; i < customThemes.length(); i++) {
+ JSONObject jsonTheme = customThemes.getJSONObject(i);
+ ThemeBundle.Builder builder = convertJsonToBuilder(jsonTheme);
+ if (builder != null) {
+ if (TextUtils.isEmpty(builder.getTitle())) {
+ builder.setTitle(mContext.getString(R.string.custom_theme_title,
+ customThemesCount + 1));
+ }
+ mThemes.add(builder.build(mContext));
+ } else {
+ Log.w(TAG, "Couldn't read stored custom theme, resetting");
+ mThemes.add(new CustomTheme(CustomTheme.newId(),
+ mContext.getString(R.string.custom_theme_title,
+ customThemesCount + 1), new HashMap<>(), null));
+ }
+ customThemesCount++;
+ }
+ } catch (JSONException e) {
+ Log.w(TAG, "Couldn't read stored custom theme, resetting", e);
+ mThemes.add(new CustomTheme(CustomTheme.newId(), mContext.getString(R.string.custom_theme_title,
+ customThemesCount + 1), new HashMap<>(), null));
+ }
}
- ThemeBundle.Builder builder = parseCustomTheme(serializedTheme);
- if (builder != null) {
- builder.setTitle(mContext.getString(R.string.custom_theme_title));
- mThemes.add(builder.build(mContext));
- } else {
- Log.w(TAG, "Couldn't read stored custom theme, resetting");
- mThemes.add(new CustomTheme(mContext.getString(R.string.custom_theme_title),
- new HashMap<>(), null));
- }
+
+ // Add an empty one at the end.
+ mThemes.add(new CustomTheme(CustomTheme.newId(), mContext.getString(R.string.custom_theme_title,
+ customThemesCount + 1), new HashMap<>(), null));
+
}
@Override
- public Builder parseCustomTheme(String serializedTheme) {
+ public CustomTheme.Builder parseCustomTheme(String serializedTheme) throws JSONException {
+ JSONObject theme = new JSONObject(serializedTheme);
+ return convertJsonToBuilder(theme);
+ }
+
+ @Nullable
+ private CustomTheme.Builder convertJsonToBuilder(JSONObject theme) throws JSONException {
try {
Map<String, String> customPackages = new HashMap<>();
-
- JSONObject theme = new JSONObject(serializedTheme);
Iterator<String> keysIterator = theme.keys();
while (keysIterator.hasNext()) {
@@ -511,94 +401,35 @@
customPackages.put(category, theme.getString(category));
}
CustomTheme.Builder builder = new CustomTheme.Builder();
- builder.setTitle(mContext.getString(R.string.custom_theme_title));
- addShapeOverlay(builder, customPackages.get(OVERLAY_CATEGORY_SHAPE));
- addFontOverlay(builder, customPackages.get(OVERLAY_CATEGORY_FONT));
- addColorOverlay(builder, customPackages.get(OVERLAY_CATEGORY_COLOR));
- addAndroidIconOverlay(builder, customPackages.get(OVERLAY_CATEGORY_ICON_ANDROID));
- addSysUiIconOverlay(builder, customPackages.get(OVERLAY_CATEGORY_ICON_SYSUI));
- addNoPreviewIconOverlay(builder, customPackages.get(OVERLAY_CATEGORY_ICON_SETTINGS));
- addNoPreviewIconOverlay(builder, customPackages.get(OVERLAY_CATEGORY_ICON_LAUNCHER));
- addNoPreviewIconOverlay(builder, customPackages.get(OVERLAY_CATEGORY_ICON_THEMEPICKER));
-
+ mOverlayProvider.addShapeOverlay(builder,
+ customPackages.get(OVERLAY_CATEGORY_SHAPE));
+ mOverlayProvider.addFontOverlay(builder,
+ customPackages.get(OVERLAY_CATEGORY_FONT));
+ mOverlayProvider.addColorOverlay(builder,
+ customPackages.get(OVERLAY_CATEGORY_COLOR));
+ mOverlayProvider.addAndroidIconOverlay(builder,
+ customPackages.get(OVERLAY_CATEGORY_ICON_ANDROID));
+ mOverlayProvider.addSysUiIconOverlay(builder,
+ customPackages.get(OVERLAY_CATEGORY_ICON_SYSUI));
+ mOverlayProvider.addNoPreviewIconOverlay(builder,
+ customPackages.get(OVERLAY_CATEGORY_ICON_SETTINGS));
+ mOverlayProvider.addNoPreviewIconOverlay(builder,
+ customPackages.get(OVERLAY_CATEGORY_ICON_LAUNCHER));
+ mOverlayProvider.addNoPreviewIconOverlay(builder,
+ customPackages.get(OVERLAY_CATEGORY_ICON_THEMEPICKER));
+ if (theme.has(THEME_TITLE_FIELD)) {
+ builder.setTitle(theme.getString(THEME_TITLE_FIELD));
+ }
+ if (theme.has(THEME_ID_FIELD)) {
+ builder.setId(theme.getString(THEME_ID_FIELD));
+ }
return builder;
- } catch (JSONException | NameNotFoundException | NotFoundException e) {
+ } catch (NameNotFoundException | NotFoundException e) {
Log.i(TAG, "Couldn't parse serialized custom theme", e);
return null;
}
}
- private String getOverlayPackage(String prefix, String themeName) {
- return getItemStringFromStub(prefix, themeName);
- }
-
- private ResourceAsset getDrawableResourceAsset(String prefix, String themeName) {
- int drawableResId = mStubApkResources.getIdentifier(prefix + themeName,
- "drawable", mStubPackageName);
- return drawableResId == 0 ? null : new ResourceAsset(mStubApkResources, drawableResId,
- RequestOptions.fitCenterTransform());
- }
-
- private 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);
- }
-
- private int loadColor(String colorName, String colorPackage)
- throws NameNotFoundException, NotFoundException {
-
- Resources overlayRes = mContext.getPackageManager()
- .getResourcesForApplication(colorPackage);
- return overlayRes.getColor(overlayRes.getIdentifier(colorName, "color", colorPackage),
- null);
- }
-
- private String loadString(String stringName, String packageName)
- throws NameNotFoundException, NotFoundException {
-
- Resources overlayRes = mContext.getPackageManager().getResourcesForApplication(packageName);
- return overlayRes.getString(overlayRes.getIdentifier(stringName, "string", packageName));
- }
-
- @Dimension
- private int loadDimen(String dimenName, String packageName)
- throws NameNotFoundException, NotFoundException {
-
- Resources overlayRes = mContext.getPackageManager().getResourcesForApplication(packageName);
- return overlayRes.getDimensionPixelOffset(overlayRes.getIdentifier(
- dimenName, "dimen", packageName));
- }
-
- private boolean loadBoolean(String booleanName, String packageName)
- throws NameNotFoundException, NotFoundException {
-
- Resources overlayRes = mContext.getPackageManager().getResourcesForApplication(packageName);
- return overlayRes.getBoolean(overlayRes.getIdentifier(
- booleanName, "boolean", packageName));
- }
-
- private 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
- private String getOverlayCategory(String packageName) {
- OverlayInfo info = mOverlayInfos.get(packageName);
- return info != null ? info.getCategory() : null;
- }
@Override
public ThemeBundle findEquivalent(ThemeBundle other) {
@@ -612,4 +443,15 @@
}
return null;
}
+
+ private String getOverlayPackage(String prefix, String themeName) {
+ return getItemStringFromStub(prefix, themeName);
+ }
+
+ private ResourceAsset getDrawableResourceAsset(String prefix, String themeName) {
+ int drawableResId = mStubApkResources.getIdentifier(prefix + themeName,
+ "drawable", mStubPackageName);
+ return drawableResId == 0 ? null : new ResourceAsset(mStubApkResources, drawableResId,
+ RequestOptions.fitCenterTransform());
+ }
}
diff --git a/src/com/android/customization/model/theme/OverlayThemeExtractor.java b/src/com/android/customization/model/theme/OverlayThemeExtractor.java
new file mode 100644
index 0000000..3c261e7
--- /dev/null
+++ b/src/com/android/customization/model/theme/OverlayThemeExtractor.java
@@ -0,0 +1,274 @@
+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.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.wallpaper.R;
+
+import java.util.HashMap;
+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);
+ 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);
+ }
+
+ 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) {
+ for (String packageName : mShapePreviewIconPackages) {
+ try {
+ builder.addShapePreviewIcon(
+ mContext.getPackageManager().getApplicationIcon(
+ packageName));
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, "Couldn't find app " + packageName
+ + ", won't use it for icon shape preview");
+ }
+ }
+ }
+
+ 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;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/customization/model/theme/ThemeBundle.java b/src/com/android/customization/model/theme/ThemeBundle.java
index 414719e..9d2f397 100644
--- a/src/com/android/customization/model/theme/ThemeBundle.java
+++ b/src/com/android/customization/model/theme/ThemeBundle.java
@@ -192,10 +192,17 @@
if (isDefault()) {
return "";
}
+ return getJsonPackages().toString();
+ }
+
+ JSONObject getJsonPackages() {
+ if (isDefault()) {
+ return new JSONObject();
+ }
JSONObject json = new JSONObject(mPackagesByCategory);
// Remove items with null values to avoid deserialization issues.
removeNullValues(json);
- return json.toString();
+ return json;
}
private void removeNullValues(JSONObject json) {
@@ -325,6 +332,10 @@
mWallpaperAsset, shapeIcons);
}
+ public String getTitle() {
+ return mTitle;
+ }
+
public Builder setTitle(String title) {
mTitle = title;
return this;
diff --git a/src/com/android/customization/model/theme/ThemeBundleProvider.java b/src/com/android/customization/model/theme/ThemeBundleProvider.java
index cd33a81..fac11cd 100644
--- a/src/com/android/customization/model/theme/ThemeBundleProvider.java
+++ b/src/com/android/customization/model/theme/ThemeBundleProvider.java
@@ -18,9 +18,9 @@
import androidx.annotation.Nullable;
import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
-import com.android.customization.model.theme.ThemeBundle.Builder;
import com.android.customization.model.theme.custom.CustomTheme;
+import org.json.JSONException;
/**
* Interface for a class that can retrieve Themes from the system.
@@ -43,7 +43,7 @@
void removeCustomTheme(CustomTheme theme);
- @Nullable Builder parseCustomTheme(String serializedTheme);
+ @Nullable CustomTheme.Builder parseCustomTheme(String serializedTheme) throws JSONException;
ThemeBundle findEquivalent(ThemeBundle other);
}
diff --git a/src/com/android/customization/model/theme/ThemeManager.java b/src/com/android/customization/model/theme/ThemeManager.java
index d873972..de6a98b 100644
--- a/src/com/android/customization/model/theme/ThemeManager.java
+++ b/src/com/android/customization/model/theme/ThemeManager.java
@@ -164,7 +164,7 @@
private void applyOverlays(ThemeBundle theme, Callback callback) {
boolean allApplied = Settings.Secure.putString(mActivity.getContentResolver(),
ResourceConstants.THEME_SETTING, theme.getSerializedPackages());
- if (theme instanceof CustomTheme) {
+ if (theme instanceof CustomTheme && !((CustomTheme) theme).isDefined()) {
storeCustomTheme((CustomTheme) theme);
}
mCurrentOverlays = null;
diff --git a/src/com/android/customization/model/theme/custom/CustomTheme.java b/src/com/android/customization/model/theme/custom/CustomTheme.java
index 5e809a0..15e4eb8 100644
--- a/src/com/android/customization/model/theme/custom/CustomTheme.java
+++ b/src/com/android/customization/model/theme/custom/CustomTheme.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.view.View;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.customization.model.CustomizationManager;
@@ -25,12 +26,38 @@
import com.android.wallpaper.R;
import java.util.Map;
+import java.util.UUID;
public class CustomTheme extends ThemeBundle {
- public CustomTheme(String title, Map<String, String> overlayPackages,
+ public static String newId() {
+ return UUID.randomUUID().toString();
+ }
+
+ private final String mId;
+
+ public CustomTheme(@NonNull String id, String title, Map<String, String> overlayPackages,
@Nullable PreviewInfo previewInfo) {
super(title, overlayPackages, false, null, previewInfo);
+ mId = id;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CustomTheme)) {
+ return false;
+ }
+ CustomTheme other = (CustomTheme) obj;
+ return mId.equals(other.mId);
+ }
+
+ @Override
+ public int hashCode() {
+ return mId.hashCode();
}
@Override
@@ -60,9 +87,16 @@
}
public static class Builder extends ThemeBundle.Builder {
+ private String mId;
+
@Override
public CustomTheme build(Context context) {
- return new CustomTheme(mTitle, mPackages, createPreviewInfo(context));
+ return new CustomTheme(mId, mTitle, mPackages, createPreviewInfo(context));
+ }
+
+ public Builder setId(String id) {
+ mId = id;
+ return this;
}
}
}
diff --git a/src/com/android/customization/model/theme/custom/CustomThemeManager.java b/src/com/android/customization/model/theme/custom/CustomThemeManager.java
index f8e5fbb..0cc0161 100644
--- a/src/com/android/customization/model/theme/custom/CustomThemeManager.java
+++ b/src/com/android/customization/model/theme/custom/CustomThemeManager.java
@@ -16,6 +16,7 @@
package com.android.customization.model.theme.custom;
import android.content.Context;
+import android.text.TextUtils;
import androidx.annotation.Nullable;
@@ -31,7 +32,8 @@
private final Map<String, String> mOverlayPackages = new HashMap<>();
private final CustomTheme mOriginalTheme;
- private CustomThemeManager(Map<String, String> overlayPackages, @Nullable CustomTheme originalTheme) {
+ private CustomThemeManager(Map<String, String> overlayPackages,
+ @Nullable CustomTheme originalTheme) {
mOverlayPackages.putAll(overlayPackages);
mOriginalTheme = originalTheme;
}
@@ -43,7 +45,11 @@
@Override
public void apply(ThemeComponentOption option, @Nullable Callback callback) {
- mOverlayPackages.putAll(option.getOverlayPackages());
+ option.getOverlayPackages().forEach((category, packageName) -> {
+ if (!TextUtils.isEmpty(packageName)) {
+ mOverlayPackages.put(category, packageName);
+ }
+ });
if (callback != null) {
callback.onSuccess();
}
@@ -53,10 +59,8 @@
return mOverlayPackages;
}
- public CustomTheme buildPartialCustomTheme(Context context) {
- return new CustomTheme(mOriginalTheme != null
- ? mOriginalTheme.getTitle() : context.getString(R.string.custom_theme_title),
- mOverlayPackages, null);
+ public CustomTheme buildPartialCustomTheme(String id, String title) {
+ return new CustomTheme(id, title, mOverlayPackages, null);
}
@Override
@@ -74,6 +78,6 @@
return new CustomThemeManager(customTheme.getPackagesByCategory(), customTheme);
}
// Seed the first custom theme with the currently applied theme.
- return new CustomThemeManager(themeManager.getCurrentOverlays(), null);
+ return new CustomThemeManager(themeManager.getCurrentOverlays(), customTheme);
}
}
diff --git a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
index fa45931..2dc163e 100644
--- a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
+++ b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
@@ -150,7 +150,8 @@
@Override
public void bindThumbnailTile(View view) {
Resources res = view.getContext().getResources();
- Drawable icon = mIcons.get(THUMBNAIL_ICON_POSITION).mutate();
+ Drawable icon = mIcons.get(THUMBNAIL_ICON_POSITION)
+ .getConstantState().newDrawable().mutate();
icon.setTint(res.getColor(R.color.icon_thumbnail_color, null));
((ImageView) view.findViewById(R.id.option_icon)).setImageDrawable(
icon);
diff --git a/src/com/android/customization/module/CustomizationPreferences.java b/src/com/android/customization/module/CustomizationPreferences.java
index dc7662c..d32143a 100644
--- a/src/com/android/customization/module/CustomizationPreferences.java
+++ b/src/com/android/customization/module/CustomizationPreferences.java
@@ -22,9 +22,9 @@
String KEY_CUSTOM_THEME= "themepicker_custom_theme";
String KEY_VISITED_PREFIX = "themepicker_visited_";
- String getSerializedCustomTheme();
+ String getSerializedCustomThemes();
- void storeCustomTheme(String serializedCustomTheme);
+ void storeCustomThemes(String serializedCustomThemes);
boolean getTabVisited(String id);
diff --git a/src/com/android/customization/module/DefaultCustomizationPreferences.java b/src/com/android/customization/module/DefaultCustomizationPreferences.java
index 202ff49..e6b4d05 100644
--- a/src/com/android/customization/module/DefaultCustomizationPreferences.java
+++ b/src/com/android/customization/module/DefaultCustomizationPreferences.java
@@ -28,13 +28,13 @@
@Override
- public String getSerializedCustomTheme() {
+ public String getSerializedCustomThemes() {
return mSharedPrefs.getString(KEY_CUSTOM_THEME, null);
}
@Override
- public void storeCustomTheme(String serializedCustomTheme) {
- mSharedPrefs.edit().putString(KEY_CUSTOM_THEME, serializedCustomTheme).apply();
+ public void storeCustomThemes(String serializedCustomThemes) {
+ mSharedPrefs.edit().putString(KEY_CUSTOM_THEME, serializedCustomThemes).apply();
}
@Override
diff --git a/src/com/android/customization/picker/CustomizationPickerActivity.java b/src/com/android/customization/picker/CustomizationPickerActivity.java
index c20b73f..281ff78 100644
--- a/src/com/android/customization/picker/CustomizationPickerActivity.java
+++ b/src/com/android/customization/picker/CustomizationPickerActivity.java
@@ -141,6 +141,18 @@
mUserEventLogger.logResumed();
// refresh the sections as the preview may have changed
initSections();
+ if (mBottomNav == null) {
+ return;
+ }
+ CustomizationSection section = mSections.get(mBottomNav.getSelectedItemId());
+ if (section == null) {
+ return;
+ }
+ // Keep CategoryFragment's design to load category within its fragment
+ if (section instanceof WallpaperSection) {
+ switchFragment(section);
+ section.onVisible();
+ }
}
@Override
@@ -266,6 +278,17 @@
}
}
+ @Override
+ public void onBackPressed() {
+ if (getSupportFragmentManager().popBackStackImmediate()) {
+ return;
+ }
+ if (moveTaskToBack(false)) {
+ return;
+ }
+ super.onBackPressed();
+ }
+
private void navigateToSection(@IdRes int id) {
mBottomNav.setSelectedItemId(id);
}
diff --git a/src/com/android/customization/picker/clock/ClockFragment.java b/src/com/android/customization/picker/clock/ClockFragment.java
index 73a8af4..c3e1c97 100644
--- a/src/com/android/customization/picker/clock/ClockFragment.java
+++ b/src/com/android/customization/picker/clock/ClockFragment.java
@@ -22,6 +22,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -87,7 +88,9 @@
mClockManager.apply(mSelectedOption, new Callback() {
@Override
public void onSuccess() {
- getActivity().finish();
+ mOptionsController.setAppliedOption(mSelectedOption);
+ Toast.makeText(getContext(), R.string.applied_clock_msg,
+ Toast.LENGTH_SHORT).show();
}
@Override
diff --git a/src/com/android/customization/picker/theme/CustomThemeActivity.java b/src/com/android/customization/picker/theme/CustomThemeActivity.java
index 40d1fe8..0ea9617 100644
--- a/src/com/android/customization/picker/theme/CustomThemeActivity.java
+++ b/src/com/android/customization/picker/theme/CustomThemeActivity.java
@@ -19,6 +19,7 @@
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
@@ -55,11 +56,14 @@
import com.android.wallpaper.module.InjectorProvider;
import com.android.wallpaper.module.WallpaperSetter;
+import org.json.JSONException;
+
import java.util.ArrayList;
import java.util.List;
public class CustomThemeActivity extends FragmentActivity implements
CustomThemeComponentFragmentHost {
+ public static final String EXTRA_THEME_ID = "CustomThemeActivity.ThemeId";
public static final String EXTRA_THEME_TITLE = "CustomThemeActivity.ThemeTitle";
public static final String EXTRA_THEME_PACKAGES = "CustomThemeActivity.ThemePackages";
public static final int REQUEST_CODE_CUSTOM_THEME = 1;
@@ -84,15 +88,20 @@
Intent intent = getIntent();
CustomTheme customTheme = null;
if (intent != null && intent.hasExtra(EXTRA_THEME_PACKAGES)
- && intent.hasExtra(EXTRA_THEME_TITLE)) {
+ && intent.hasExtra(EXTRA_THEME_TITLE) && intent.hasExtra(EXTRA_THEME_ID)) {
ThemeBundleProvider themeProvider =
new DefaultThemeProvider(this, injector.getCustomizationPreferences(this));
- Builder themeBuilder = themeProvider.parseCustomTheme(
- intent.getStringExtra(EXTRA_THEME_PACKAGES));
- if (themeBuilder != null) {
- themeBuilder.setTitle(intent.getStringExtra(EXTRA_THEME_TITLE));
+ try {
+ CustomTheme.Builder themeBuilder = themeProvider.parseCustomTheme(
+ intent.getStringExtra(EXTRA_THEME_PACKAGES));
+ if (themeBuilder != null) {
+ themeBuilder.setId(intent.getStringExtra(EXTRA_THEME_ID));
+ themeBuilder.setTitle(intent.getStringExtra(EXTRA_THEME_TITLE));
+ customTheme = themeBuilder.build(this);
+ }
+ } catch (JSONException e) {
+ Log.w(TAG, "Couldn't parse provided custom theme, will override it");
}
- customTheme = (CustomTheme) themeBuilder.build(this);
}
mThemeManager = new ThemeManager(
@@ -135,7 +144,7 @@
private void navigateToStep(int i) {
FragmentManager fragmentManager = getSupportFragmentManager();
ComponentStep step = mSteps.get(i);
- Fragment fragment = step.getFragment();
+ Fragment fragment = step.getFragment(mCustomThemeManager.getOriginalTheme().getTitle());
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragment);
@@ -145,7 +154,7 @@
}
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
- updateApplyButtonLabel();
+ updateNavigationButtonLabels();
}
private void initSteps(int currentStep) {
@@ -166,15 +175,16 @@
if (mCurrentStep < mSteps.size() - 1) {
navigateToStep(mCurrentStep + 1);
} else {
+ CustomTheme originalTheme = mCustomThemeManager.getOriginalTheme();
+
// We're on the last step, apply theme and leave
CustomTheme themeToApply = mCustomThemeManager.buildPartialCustomTheme(
- CustomThemeActivity.this);
+ originalTheme.getId(), originalTheme.getTitle());
// If the current theme is equal to the original theme being edited, then
// don't search for an equivalent, let the user apply the same one by keeping
// it null.
- ThemeBundle equivalent = (mCustomThemeManager.getOriginalTheme() != null
- && mCustomThemeManager.getOriginalTheme().isEquivalent(themeToApply))
+ ThemeBundle equivalent = (originalTheme.isEquivalent(themeToApply))
? null : mThemeManager.findThemeByPackages(themeToApply);
if (equivalent != null) {
@@ -233,10 +243,11 @@
@Override
public void setCurrentStep(int i) {
mCurrentStep = i;
- updateApplyButtonLabel();
+ updateNavigationButtonLabels();
}
- private void updateApplyButtonLabel() {
+ private void updateNavigationButtonLabels() {
+ mPreviousButton.setVisibility(mCurrentStep == 0 ? View.INVISIBLE : View.VISIBLE);
mNextButton.setText((mCurrentStep < mSteps.size() -1) ? R.string.custom_theme_next
: R.string.apply_btn);
}
@@ -282,9 +293,9 @@
this.position = position;
}
- CustomThemeComponentFragment getFragment() {
+ CustomThemeComponentFragment getFragment(String title) {
if (mFragment == null) {
- mFragment = createFragment();
+ mFragment = createFragment(title);
}
return mFragment;
}
@@ -292,7 +303,7 @@
/**
* @return a newly created fragment that will handle this step's UI.
*/
- abstract CustomThemeComponentFragment createFragment();
+ abstract CustomThemeComponentFragment createFragment(String title);
}
private class FontStep extends ComponentStep<FontOption> {
@@ -303,9 +314,9 @@
}
@Override
- CustomThemeComponentFragment createFragment() {
+ CustomThemeComponentFragment createFragment(String title) {
return CustomThemeComponentFragment.newInstance(
- CustomThemeActivity.this.getString(R.string.custom_theme_fragment_title),
+ title,
position,
titleResId);
}
@@ -319,9 +330,9 @@
}
@Override
- CustomThemeComponentFragment createFragment() {
+ CustomThemeComponentFragment createFragment(String title) {
return CustomThemeComponentFragment.newInstance(
- CustomThemeActivity.this.getString(R.string.custom_theme_fragment_title),
+ title,
position,
titleResId);
}
@@ -335,9 +346,9 @@
}
@Override
- CustomThemeComponentFragment createFragment() {
+ CustomThemeComponentFragment createFragment(String title) {
return CustomThemeComponentFragment.newInstance(
- CustomThemeActivity.this.getString(R.string.custom_theme_fragment_title),
+ title,
position,
titleResId,
true);
@@ -352,9 +363,9 @@
}
@Override
- CustomThemeComponentFragment createFragment() {
+ CustomThemeComponentFragment createFragment(String title) {
return CustomThemeComponentFragment.newInstance(
- CustomThemeActivity.this.getString(R.string.custom_theme_fragment_title),
+ title,
position,
titleResId);
}
diff --git a/src/com/android/customization/picker/theme/ThemeFragment.java b/src/com/android/customization/picker/theme/ThemeFragment.java
index e3695e4..5b590f0 100644
--- a/src/com/android/customization/picker/theme/ThemeFragment.java
+++ b/src/com/android/customization/picker/theme/ThemeFragment.java
@@ -234,7 +234,7 @@
mOptionsController = new OptionSelectorController<>(mOptionsContainer, options);
mOptionsController.addListener(selected -> {
if (selected instanceof CustomTheme && !((CustomTheme) selected).isDefined()) {
- navigateToCustomTheme(null);
+ navigateToCustomTheme((CustomTheme) selected);
} else {
mSelectedTheme = (ThemeBundle) selected;
if (mUseMyWallpaper || mSelectedTheme instanceof CustomTheme) {
@@ -292,13 +292,12 @@
}, true);
}
- private void navigateToCustomTheme(@Nullable CustomTheme themeToEdit) {
+ private void navigateToCustomTheme(CustomTheme themeToEdit) {
Intent intent = new Intent(getActivity(), CustomThemeActivity.class);
- if (themeToEdit != null) {
- intent.putExtra(CustomThemeActivity.EXTRA_THEME_TITLE, themeToEdit.getTitle());
- intent.putExtra(CustomThemeActivity.EXTRA_THEME_PACKAGES,
- themeToEdit.getSerializedPackages());
- }
+ intent.putExtra(CustomThemeActivity.EXTRA_THEME_TITLE, themeToEdit.getTitle());
+ intent.putExtra(CustomThemeActivity.EXTRA_THEME_ID, themeToEdit.getId());
+ intent.putExtra(CustomThemeActivity.EXTRA_THEME_PACKAGES,
+ themeToEdit.getSerializedPackages());
startActivityForResult(intent, CustomThemeActivity.REQUEST_CODE_CUSTOM_THEME);
}
diff --git a/src/com/android/customization/widget/OptionSelectorController.java b/src/com/android/customization/widget/OptionSelectorController.java
index 9725995..de2f09c 100644
--- a/src/com/android/customization/widget/OptionSelectorController.java
+++ b/src/com/android/customization/widget/OptionSelectorController.java
@@ -111,7 +111,12 @@
if (!mOptions.contains(option)) {
throw new IllegalArgumentException("Invalid option");
}
+ CustomizationOption lastAppliedOption = mAppliedOption;
mAppliedOption = option;
+ mAdapter.notifyItemChanged(mOptions.indexOf(option));
+ if (lastAppliedOption != null) {
+ mAdapter.notifyItemChanged(mOptions.indexOf(lastAppliedOption));
+ }
}
private void updateActivatedStatus(CustomizationOption option, boolean isActivated) {
@@ -125,23 +130,26 @@
if (holder instanceof TileViewHolder) {
TileViewHolder tileHolder = (TileViewHolder) holder;
- if (isActivated) {
- if (option == mAppliedOption) {
+ if (tileHolder.labelView != null) {
+ if (isActivated) {
+ if (option == mAppliedOption) {
+ CharSequence cd = mContainer.getContext().getString(
+ R.string.option_applied_previewed_description,
+ option.getTitle());
+ tileHolder.labelView.setContentDescription(cd);
+ } else {
+ CharSequence cd = mContainer.getContext().getString(
+ R.string.option_previewed_description, option.getTitle());
+ tileHolder.labelView.setContentDescription(cd);
+ }
+ } else if (option == mAppliedOption) {
CharSequence cd = mContainer.getContext().getString(
- R.string.option_applied_previewed_description, option.getTitle());
+ R.string.option_applied_description, option.getTitle());
tileHolder.labelView.setContentDescription(cd);
} else {
- CharSequence cd = mContainer.getContext().getString(
- R.string.option_previewed_description, option.getTitle());
- tileHolder.labelView.setContentDescription(cd);
+ // Remove content description
+ tileHolder.labelView.setContentDescription(null);
}
- } else if (option == mAppliedOption) {
- CharSequence cd = mContainer.getContext().getString(
- R.string.option_applied_description, option.getTitle());
- tileHolder.labelView.setContentDescription(cd);
- } else {
- // Remove content description
- tileHolder.labelView.setContentDescription(null);
}
}
}