Multiple custom themes 1/n

Refactor DefaultThemeProvider for better reuse, and add
support for storing and retrieving more than one custom
theme (custom theme names not supported yet).

Bug: 132257073
Change-Id: I9ed8b0e13fb5ab063373adedf40fc98896d0a20e
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 319aa0a..a97e26d 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -117,9 +117,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..a4f9a31 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.
@@ -100,29 +87,14 @@
 
     private static final String DEFAULT_THEME_NAME= "default";
 
-    // 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 +126,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 +166,7 @@
             }
         }
 
-        addCustomTheme();
+        addCustomThemes();
     }
 
     private void addWallpaper(String themeName, Builder builder) {
@@ -235,9 +208,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 +219,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 +238,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 +273,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 +294,83 @@
         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 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 addCustomThemeAndStore(CustomTheme theme) {
+        mThemes.add(theme);
+        JSONArray themesArray = new JSONArray();
+        mThemes.stream()
+                .filter(themeBundle -> themeBundle instanceof CustomTheme
+                        && !themeBundle.getPackagesByCategory().isEmpty())
+                .forEachOrdered(themeBundle -> themesArray.put(themeBundle.getJsonPackages()));
+        mCustomizationPreferences.storeCustomThemes(themesArray.toString());
     }
 
     @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)
+                .forEachOrdered(themeBundle -> {
+                    if (!themeBundle.equals(theme)) {
+                        themesArray.put(themeBundle.getJsonPackages());
+                    }
+                });
+        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++) {
+                    ThemeBundle.Builder builder = convertJsonToBuilder(
+                            customThemes.getJSONObject(i));
+                    if (builder != null) {
+                        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(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(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(mContext.getString(R.string.custom_theme_title,
+                customThemesCount + 1), new HashMap<>(), null));
+
     }
 
     @Override
-    public Builder parseCustomTheme(String serializedTheme) {
+    public Builder parseCustomTheme(String serializedTheme) throws JSONException {
+        JSONObject theme = new JSONObject(serializedTheme);
+        return convertJsonToBuilder(theme);
+    }
+
+    @Nullable
+    private 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 +378,30 @@
                 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));
 
             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 +415,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..ad21405 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) {
diff --git a/src/com/android/customization/model/theme/ThemeBundleProvider.java b/src/com/android/customization/model/theme/ThemeBundleProvider.java
index cd33a81..578b96c 100644
--- a/src/com/android/customization/model/theme/ThemeBundleProvider.java
+++ b/src/com/android/customization/model/theme/ThemeBundleProvider.java
@@ -21,6 +21,7 @@
 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 +44,7 @@
 
     void removeCustomTheme(CustomTheme theme);
 
-    @Nullable Builder parseCustomTheme(String serializedTheme);
+    @Nullable 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/CustomThemeManager.java b/src/com/android/customization/model/theme/custom/CustomThemeManager.java
index f8e5fbb..5463eb0 100644
--- a/src/com/android/customization/model/theme/custom/CustomThemeManager.java
+++ b/src/com/android/customization/model/theme/custom/CustomThemeManager.java
@@ -31,7 +31,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;
     }
@@ -53,10 +54,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 title) {
+        return new CustomTheme(title, mOverlayPackages, null);
     }
 
     @Override
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/theme/CustomThemeActivity.java b/src/com/android/customization/picker/theme/CustomThemeActivity.java
index 40d1fe8..1e8af0a 100644
--- a/src/com/android/customization/picker/theme/CustomThemeActivity.java
+++ b/src/com/android/customization/picker/theme/CustomThemeActivity.java
@@ -55,6 +55,8 @@
 import com.android.wallpaper.module.InjectorProvider;
 import com.android.wallpaper.module.WallpaperSetter;
 
+import org.json.JSONException;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -87,12 +89,17 @@
                 && intent.hasExtra(EXTRA_THEME_TITLE)) {
             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));
+            Builder themeBuilder = null;
+            try {
+                themeBuilder = themeProvider.parseCustomTheme(
+                        intent.getStringExtra(EXTRA_THEME_PACKAGES));
+                if (themeBuilder != null) {
+                    themeBuilder.setTitle(intent.getStringExtra(EXTRA_THEME_TITLE));
+                    customTheme = (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(
@@ -166,15 +173,20 @@
                 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 != null
+                                    ? originalTheme.getTitle()
+                                    : getString(R.string.custom_theme_title,
+                                            0));
 
                     // 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 != null
+                            && originalTheme.isEquivalent(themeToApply))
                                 ? null : mThemeManager.findThemeByPackages(themeToApply);
 
                     if (equivalent != null) {