Custom Theme 6/n: edit custom theme

Add edit link from custom theme preview card, which takes
the user to CustomThemeAcitvity

Bug: 124796742
Change-Id: I5063ac73fd60b0dd12e993e5b07bdb69d0e9713a
diff --git a/res/drawable/ic_tune_24px.xml b/res/drawable/ic_tune_24px.xml
new file mode 100644
index 0000000..abad67c
--- /dev/null
+++ b/res/drawable/ic_tune_24px.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M3,17v2h6v-2H3zM3,5v2h10V5H3zM13,21v-2h8v-2h-8v-2h-2v6H13zM7,9v2H3v2h4v2h2V9H7zM21,13v-2H11v2H21zM15,9h2V7h4V5h-4V3h-2V9z"/>
+</vector>
diff --git a/res/layout/theme_preview_card.xml b/res/layout/theme_preview_card.xml
index 18f2b15..77155f0 100644
--- a/res/layout/theme_preview_card.xml
+++ b/res/layout/theme_preview_card.xml
@@ -31,6 +31,7 @@
             android:layout_height="match_parent"
             android:padding="@dimen/preview_card_padding"
             android:orientation="vertical">
+
             <TextView
                 android:id="@+id/theme_preview_card_header"
                 android:layout_width="wrap_content"
@@ -39,11 +40,26 @@
                 android:layout_marginBottom="8dp"
                 android:drawablePadding="10dp"
                 android:textAppearance="@style/CardTitleTextAppearance"/>
+
             <FrameLayout
                 android:id="@+id/theme_preview_card_body_container"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:layout_marginHorizontal="8dp"/>
+                android:layout_marginHorizontal="8dp"
+                android:layout_weight="1"/>
+
+            <TextView
+                android:id="@+id/edit_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:drawableStart="@drawable/ic_tune_24px"
+                android:drawablePadding="8dp"
+                android:drawableTint="?android:colorAccent"
+                android:gravity="center"
+                android:text="@string/edit_custom_theme_lbl"
+                android:textAppearance="@style/EditLabelTextAppearance"
+                android:visibility="gone"/>
         </LinearLayout>
     </FrameLayout>
 </androidx.cardview.widget.CardView>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 78a8f1f..d12735f 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -38,6 +38,10 @@
         [CHAR LIMIT=20] -->
     <string name="apply_theme_btn">Apply</string>
 
+    <!-- Label telling the user they can tap the card to edit the currently selected custom Theme.
+        [CHAR LIMIT=50] -->
+    <string name="edit_custom_theme_lbl">Tap to edit</string>
+
     <!-- Label for a checkbox to allow the user to use their currently set wallpaper instead of
         the one bundled with selected Theme [CHAR LIMIT=35]-->
     <string name="keep_my_wallpaper">Keep current wallpaper</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 18482ba..d53ffac 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -86,8 +86,9 @@
         <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault</item>
     </style>
 
-    <style name="ComponentCounterTextAppearance" parent="@android:style/TextAppearance.DeviceDefault.Small">
+    <style name="ComponentCounterTextAppearance" parent="@android:style/TextAppearance.DeviceDefault"/>
 
-
+    <style name="EditLabelTextAppearance" parent="@android:style/TextAppearance.DeviceDefault">
+        <item name="android:textColor">?android:colorAccent</item>
     </style>
 </resources>
diff --git a/src/com/android/customization/model/theme/DefaultThemeProvider.java b/src/com/android/customization/model/theme/DefaultThemeProvider.java
index 02a0047..4b5fb4a 100644
--- a/src/com/android/customization/model/theme/DefaultThemeProvider.java
+++ b/src/com/android/customization/model/theme/DefaultThemeProvider.java
@@ -413,8 +413,22 @@
                     new HashMap<>(), null));
             return;
         }
-        Map<String, String> customPackages = new HashMap<>();
+        ThemeBundle.Builder builder = parseCustomTheme(serializedTheme);
+        if (builder != null) {
+            builder.setTitle(mContext.getString(R.string.custom_theme_title));
+            mThemes.add(builder.build());
+        } 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));
+        }
+    }
+
+    @Override
+    public Builder parseCustomTheme(String serializedTheme) {
         try {
+            Map<String, String> customPackages = new HashMap<>();
+
             JSONObject theme = new JSONObject(serializedTheme);
             Iterator<String> keysIterator = theme.keys();
 
@@ -430,11 +444,10 @@
             addNoPreviewIconOverlay(builder, customPackages.get(OVERLAY_CATEGORY_ICON_SETTINGS));
             addNoPreviewIconOverlay(builder, customPackages.get(OVERLAY_CATEGORY_ICON_LAUNCHER));
 
-            mThemes.add(builder.build());
+            return builder;
         } catch (JSONException | NameNotFoundException | NotFoundException e) {
-            Log.w(TAG, "Couldn't read stored custom theme, resetting", e);
-            mThemes.add(new CustomTheme(mContext.getString(R.string.custom_theme_title),
-                    new HashMap<>(), null));
+            Log.i(TAG, "Couldn't parse serialized custom theme", e);
+            return null;
         }
     }
 
diff --git a/src/com/android/customization/model/theme/ThemeBundleProvider.java b/src/com/android/customization/model/theme/ThemeBundleProvider.java
index ba4ee5d..f2e1915 100644
--- a/src/com/android/customization/model/theme/ThemeBundleProvider.java
+++ b/src/com/android/customization/model/theme/ThemeBundleProvider.java
@@ -15,9 +15,13 @@
  */
 package com.android.customization.model.theme;
 
+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;
 
+
 /**
  * Interface for a class that can retrieve Themes from the system.
  */
@@ -36,4 +40,6 @@
     void fetch(OptionsFetchedListener<ThemeBundle> callback, boolean reload);
 
     void storeCustomTheme(CustomTheme theme);
+
+    @Nullable Builder parseCustomTheme(String serializedTheme);
 }
diff --git a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
index 33dfc2f..352b0e7 100644
--- a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
+++ b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
@@ -145,6 +145,9 @@
         @Override
         public boolean isActive(CustomizationManager<ThemeComponentOption> manager) {
             CustomThemeManager customThemeManager = (CustomThemeManager) manager;
+            if (getOverlayPackages().isEmpty()) {
+                return customThemeManager.getOverlayPackages().isEmpty();
+            }
              for (Map.Entry<String, String> overlayEntry : getOverlayPackages().entrySet()) {
                  if(!Objects.equals(overlayEntry.getValue(),
                          customThemeManager.getOverlayPackages().get(overlayEntry.getKey()))) {
diff --git a/src/com/android/customization/picker/theme/CustomThemeActivity.java b/src/com/android/customization/picker/theme/CustomThemeActivity.java
index 9d0c7bb..6b39a78 100644
--- a/src/com/android/customization/picker/theme/CustomThemeActivity.java
+++ b/src/com/android/customization/picker/theme/CustomThemeActivity.java
@@ -15,6 +15,7 @@
  */
 package com.android.customization.picker.theme;
 
+import android.content.Intent;
 import android.os.Bundle;
 import android.util.Log;
 import android.widget.TextView;
@@ -30,7 +31,10 @@
 import com.android.customization.model.CustomizationManager.Callback;
 import com.android.customization.model.theme.DefaultThemeProvider;
 import com.android.customization.model.theme.OverlayManagerCompat;
+import com.android.customization.model.theme.ThemeBundle.Builder;
+import com.android.customization.model.theme.ThemeBundleProvider;
 import com.android.customization.model.theme.ThemeManager;
+import com.android.customization.model.theme.custom.CustomTheme;
 import com.android.customization.model.theme.custom.CustomThemeManager;
 import com.android.customization.model.theme.custom.FontOptionsProvider;
 import com.android.customization.model.theme.custom.IconOptionsProvider;
@@ -50,6 +54,9 @@
 
 public class CustomThemeActivity extends FragmentActivity implements
         CustomThemeComponentFragmentHost {
+    public static final String EXTRA_THEME_TITLE = "CustomThemeActivity.ThemeTitle";
+    public static final String EXTRA_THEME_PACKAGES = "CustomThemeActivity.ThemePackages";
+
     private static final String TAG = "CustomThemeActivity";
 
     private UserEventLogger mUserEventLogger;
@@ -64,7 +71,21 @@
         super.onCreate(savedInstanceState);
         CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
         mUserEventLogger = injector.getUserEventLogger(this);
-        mCustomThemeManager = new CustomThemeManager(null);
+        Intent intent = getIntent();
+        Builder themeBuilder = null;
+        if (intent != null && intent.hasExtra(EXTRA_THEME_PACKAGES)
+                && intent.hasExtra(EXTRA_THEME_TITLE)) {
+            ThemeBundleProvider themeProvider =
+                    new DefaultThemeProvider(this, injector.getCustomizationPreferences(this));
+            themeBuilder = themeProvider.parseCustomTheme(
+                    intent.getStringExtra(EXTRA_THEME_PACKAGES));
+            if (themeBuilder != null) {
+                themeBuilder.setTitle(intent.getStringExtra(EXTRA_THEME_TITLE));
+            }
+        }
+        mCustomThemeManager = new CustomThemeManager(themeBuilder == null ? null
+                : (CustomTheme) themeBuilder.build());
+
         mThemeManager = new ThemeManager(
                 new DefaultThemeProvider(this, injector.getCustomizationPreferences(this)),
                 this,
diff --git a/src/com/android/customization/picker/theme/ThemeFragment.java b/src/com/android/customization/picker/theme/ThemeFragment.java
index 6df43f0..2590f21 100644
--- a/src/com/android/customization/picker/theme/ThemeFragment.java
+++ b/src/com/android/customization/picker/theme/ThemeFragment.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
 import android.widget.CheckBox;
@@ -47,10 +48,10 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.customization.model.CustomizationManager.Callback;
-import com.android.customization.model.theme.custom.CustomTheme;
 import com.android.customization.model.theme.ThemeBundle;
 import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
 import com.android.customization.model.theme.ThemeManager;
+import com.android.customization.model.theme.custom.CustomTheme;
 import com.android.customization.picker.BasePreviewAdapter;
 import com.android.customization.picker.BasePreviewAdapter.PreviewPage;
 import com.android.customization.widget.OptionSelectorController;
@@ -82,6 +83,7 @@
     }
 
     private RecyclerView mOptionsContainer;
+    private CheckBox mUseMyWallpaperButton;
     private OptionSelectorController mOptionsController;
     private ThemeManager mThemeManager;
     private ThemeBundle mSelectedTheme;
@@ -128,8 +130,8 @@
             });
 
         });
-        ((CheckBox)view.findViewById(R.id.use_my_wallpaper)).setOnCheckedChangeListener(
-                this::onUseMyWallpaperCheckChanged);
+        mUseMyWallpaperButton = view.findViewById(R.id.use_my_wallpaper);
+        mUseMyWallpaperButton.setOnCheckedChangeListener(this::onUseMyWallpaperCheckChanged);
 
         setUpOptions(savedInstanceState);
 
@@ -179,20 +181,33 @@
     }
 
     private void createAdapter() {
-        mAdapter = new ThemePreviewAdapter(getActivity(), mSelectedTheme);
+        mAdapter = new ThemePreviewAdapter(getActivity(), mSelectedTheme,
+                mSelectedTheme instanceof CustomTheme ? this::onEditClicked : null);
         mPreviewPager.setAdapter(mAdapter);
     }
 
+    private void onEditClicked(View view) {
+        if (mSelectedTheme instanceof CustomTheme) {
+            navigateToCustomTheme((CustomTheme) mSelectedTheme);
+        }
+    }
+
+    private void updateButtonsVisibility() {
+        mUseMyWallpaperButton.setVisibility(mSelectedTheme instanceof CustomTheme
+                ? View.GONE : View.VISIBLE);
+    }
+
     private void setUpOptions(@Nullable Bundle savedInstanceState) {
         mThemeManager.fetchOptions(options -> {
             mOptionsController = new OptionSelectorController(mOptionsContainer, options);
             mOptionsController.addListener(selected -> {
                 if (selected instanceof CustomTheme && !((CustomTheme) selected).isDefined()) {
-                    navigateToCustomTheme();
+                    navigateToCustomTheme(null);
                 } else {
                     mSelectedTheme = (ThemeBundle) selected;
                     mSelectedTheme.setOverrideThemeWallpaper(mCurrentHomeWallpaper);
                     createAdapter();
+                    updateButtonsVisibility();
                 }
             });
             mOptionsController.initOptions(mThemeManager);
@@ -215,10 +230,16 @@
             mOptionsController.setSelectedOption(mSelectedTheme);
         });
         createAdapter();
+        updateButtonsVisibility();
     }
 
-    private void navigateToCustomTheme() {
+    private void navigateToCustomTheme(@Nullable 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());
+        }
         startActivity(intent);
     }
 
@@ -227,17 +248,19 @@
         @DrawableRes final int iconSrc;
         @LayoutRes final int contentLayoutRes;
         @ColorInt final int accentColor;
+        private final OnClickListener editClickListener;
         private final LayoutInflater inflater;
 
         private ThemePreviewPage(Context context, @StringRes int titleResId,
                 @DrawableRes int iconSrc, @LayoutRes int contentLayoutRes,
-                @ColorInt int accentColor) {
+                @ColorInt int accentColor, OnClickListener editClickListener) {
             super(null);
             this.nameResId = titleResId;
             this.iconSrc = iconSrc;
             this.contentLayoutRes = contentLayoutRes;
             this.accentColor = accentColor;
             this.inflater = LayoutInflater.from(context);
+            this.editClickListener = editClickListener;
         }
 
         @Override
@@ -250,6 +273,9 @@
             ViewGroup body = card.findViewById(R.id.theme_preview_card_body_container);
             inflater.inflate(contentLayoutRes, body, true);
             bindBody(false);
+            card.setOnClickListener(editClickListener);
+            card.findViewById(R.id.edit_label).setVisibility(editClickListener != null
+                    ? View.VISIBLE : View.GONE);
         }
 
         protected boolean containsWallpaper() {
@@ -280,13 +306,14 @@
             R.id.preview_color_qs_0_icon, R.id.preview_color_qs_1_icon, R.id.preview_color_qs_2_icon
         };
 
-        ThemePreviewAdapter(Activity activity, ThemeBundle theme) {
+        ThemePreviewAdapter(Activity activity, ThemeBundle theme,
+                @Nullable OnClickListener editClickListener) {
             super(activity, R.layout.theme_preview_card);
             final Resources res = activity.getResources();
             final PreviewInfo previewInfo = theme.getPreviewInfo();
             addPage(new ThemePreviewPage(activity, R.string.preview_name_font, R.drawable.ic_font,
                     R.layout.preview_card_font_content,
-                    previewInfo.resolveAccentColor(res)) {
+                    previewInfo.resolveAccentColor(res), editClickListener) {
                 @Override
                 protected void bindBody(boolean forceRebind) {
                     TextView title = card.findViewById(R.id.font_card_title);
@@ -298,7 +325,7 @@
             if (previewInfo.icons.size() >= mIconIds.length) {
                 addPage(new ThemePreviewPage(activity, R.string.preview_name_icon,
                         R.drawable.ic_wifi_24px, R.layout.preview_card_icon_content,
-                        previewInfo.resolveAccentColor(res)) {
+                        previewInfo.resolveAccentColor(res), editClickListener) {
                     @Override
                     protected void bindBody(boolean forceRebind) {
                         for (int i = 0; i < mIconIds.length; i++) {
@@ -311,7 +338,7 @@
             if (previewInfo.colorPreviewAsset != null) {
                 addPage(new ThemePreviewPage(activity, R.string.preview_name_color,
                         R.drawable.ic_colorize_24px, R.layout.preview_card_color_content,
-                        previewInfo.resolveAccentColor(res)) {
+                        previewInfo.resolveAccentColor(res), editClickListener) {
                     @Override
                     protected void bindBody(boolean forceRebind) {
                         ColorStateList tintList = new ColorStateList(
@@ -360,7 +387,7 @@
             if (previewInfo.shapePreviewAsset != null) {
                 addPage(new ThemePreviewPage(activity, R.string.preview_name_shape,
                         R.drawable.ic_shapes_24px, R.layout.preview_card_static_content,
-                        previewInfo.resolveAccentColor(res)) {
+                        previewInfo.resolveAccentColor(res), editClickListener) {
                     @Override
                     protected void bindBody(boolean forceRebind) {
                         ImageView staticImage = card.findViewById(R.id.preview_static_image);
@@ -377,7 +404,7 @@
             if (previewInfo.wallpaperAsset != null) {
                 addPage(new ThemePreviewPage(activity, R.string.preview_name_wallpaper,
                         R.drawable.ic_wallpaper_24px, R.layout.preview_card_wallpaper_content,
-                        previewInfo.resolveAccentColor(res)) {
+                        previewInfo.resolveAccentColor(res), null) {
 
                     private final WallpaperPreviewLayoutListener  mListener =
                             new WallpaperPreviewLayoutListener(theme, previewInfo);