Custom Theme 10/n: check for existing theme
If the currently defined custom theme is equivalent to an
existing one, suggest using that one instead of creating a
new custom one.
Bug: 124796742
Change-Id: Ib8436d129f359a53f284f907a566dd16293df6ef
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 01f57d6..b8a3e28 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -148,4 +148,22 @@
<!-- Label for a dialog which asks the user the destination (home screen, lock screen, both)
where to set the theme's bundled image as wallpaper. [CHAR LIMIT=50] -->
<string name="set_theme_wallpaper_dialog_message">Set style wallpaper</string>
+
+ <!-- Title for a dialog box asking the user to use an existing, equivalent theme (style) instead
+ of their customly defined one. [CHAR_LIMIT=30] -->
+ <string name="use_style_instead_title">Use <xliff:g name="style_name">%1$s</xliff:g> instead?</string>
+
+ <!-- Body for a dialog box asking the user to use an existing, equivalent theme (style) instead
+ of their customly defined one. [CHAR_LIMIT=NONE] -->
+ <string name="use_style_instead_body">The components you chose match the <xliff:g name="style_name">%1$s</xliff:g> style. Do you want to use <xliff:g name="style_name">%1$s</xliff:g> instead?</string>
+
+ <!-- Label for a button in the dialog box that asks the user to use an existing, theme (style)
+ instead of their customly defined one. The button let's the user use the suggested theme
+ [CHAR_LIMIT=15] -->
+ <string name="use_style_button">Use <xliff:g name="style_name">%1$s</xliff:g></string>
+
+ <!-- Label for a button in the dialog box that asks the user to use an existing, theme (style)
+ instead of their customly defined one. The button dismisses the dialog and goes back to the
+ previous screen. [CHAR_LIMIT=15] -->
+ <string name="no_thanks">No, thanks</string>
</resources>
diff --git a/src/com/android/customization/model/theme/DefaultThemeProvider.java b/src/com/android/customization/model/theme/DefaultThemeProvider.java
index 6bb4926..bcecc45 100644
--- a/src/com/android/customization/model/theme/DefaultThemeProvider.java
+++ b/src/com/android/customization/model/theme/DefaultThemeProvider.java
@@ -544,4 +544,17 @@
OverlayInfo info = mOverlayInfos.get(packageName);
return info != null ? info.category : null;
}
+
+ @Override
+ public ThemeBundle findEquivalent(ThemeBundle other) {
+ if (mThemes == null) {
+ return null;
+ }
+ for (ThemeBundle theme : mThemes) {
+ if (theme.isEquivalent(other)) {
+ return theme;
+ }
+ }
+ return null;
+ }
}
diff --git a/src/com/android/customization/model/theme/ThemeBundle.java b/src/com/android/customization/model/theme/ThemeBundle.java
index 50b70cb..2b217a0 100644
--- a/src/com/android/customization/model/theme/ThemeBundle.java
+++ b/src/com/android/customization/model/theme/ThemeBundle.java
@@ -124,6 +124,21 @@
return R.layout.theme_option;
}
+ /**
+ * This is similar to #equals() but it only compares this theme's packages with the other, that
+ * is, it will return true if applying this theme has the same effect of applying the given one.
+ */
+ public boolean isEquivalent(ThemeBundle other) {
+ if (other == null) {
+ return false;
+ }
+ if (mIsDefault) {
+ return other.isDefault() || TextUtils.isEmpty(other.getSerializedPackages());
+ }
+ // Map#equals ensures keys and values are compared.
+ return mPackagesByCategory.equals(other.mPackagesByCategory);
+ }
+
public PreviewInfo getPreviewInfo() {
return mPreviewInfo;
}
diff --git a/src/com/android/customization/model/theme/ThemeBundleProvider.java b/src/com/android/customization/model/theme/ThemeBundleProvider.java
index 7a7ba28..cd33a81 100644
--- a/src/com/android/customization/model/theme/ThemeBundleProvider.java
+++ b/src/com/android/customization/model/theme/ThemeBundleProvider.java
@@ -44,4 +44,6 @@
void removeCustomTheme(CustomTheme theme);
@Nullable Builder parseCustomTheme(String serializedTheme);
+
+ 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 313798f..28e7bb9 100644
--- a/src/com/android/customization/model/theme/ThemeManager.java
+++ b/src/com/android/customization/model/theme/ThemeManager.java
@@ -232,4 +232,13 @@
public void removeCustomTheme(CustomTheme theme) {
mProvider.removeCustomTheme(theme);
}
+
+ /**
+ * @return an existing ThemeBundle that matches the same packages as the given one, if one
+ * exists, or {@code null} otherwise.
+ */
+ @Nullable
+ public ThemeBundle findThemeByPackages(ThemeBundle other) {
+ return mProvider.findEquivalent(other);
+ }
}
diff --git a/src/com/android/customization/picker/theme/CustomThemeActivity.java b/src/com/android/customization/picker/theme/CustomThemeActivity.java
index 9797e8e..e7df1e6 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.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -31,6 +32,7 @@
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;
import com.android.customization.model.theme.ThemeBundle.Builder;
import com.android.customization.model.theme.ThemeBundleProvider;
import com.android.customization.model.theme.ThemeManager;
@@ -99,6 +101,7 @@
new WallpaperSetter(injector.getWallpaperPersister(this),
injector.getPreferences(this), mUserEventLogger, false),
new OverlayManagerCompat(this));
+ mThemeManager.fetchOptions(null, false);
setContentView(R.layout.activity_custom_theme);
mApplyButton = findViewById(R.id.next_button);
mApplyButton.setOnClickListener(view -> onNextOrApply());
@@ -147,26 +150,32 @@
navigateToStep(mCurrentStep + 1);
} else {
// We're on the last step, apply theme and leave
- // TODO: Verify that custom theme doesn't collide with existing one
- // (compare overlay packages)
- mThemeManager.apply(mCustomThemeManager.buildPartialCustomTheme(
- CustomThemeActivity.this), new Callback() {
- @Override
- public void onSuccess() {
- Toast.makeText(CustomThemeActivity.this, R.string.applied_theme_msg,
- Toast.LENGTH_LONG).show();
- setResult(RESULT_THEME_APPLIED);
- finish();
- }
+ CustomTheme themeToApply = mCustomThemeManager.buildPartialCustomTheme(
+ CustomThemeActivity.this);
- @Override
- public void onError(@Nullable Throwable throwable) {
- Log.w(TAG, "Error applying custom theme", throwable);
- Toast.makeText(CustomThemeActivity.this,
- R.string.apply_theme_error_msg,
- Toast.LENGTH_LONG).show();
- }
- });
+ // 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))
+ ? null : mThemeManager.findThemeByPackages(themeToApply);
+
+ if (equivalent != null) {
+ AlertDialog.Builder builder =
+ new AlertDialog.Builder(CustomThemeActivity.this);
+ builder.setTitle(getString(R.string.use_style_instead_title,
+ equivalent.getTitle()))
+ .setMessage(getString(R.string.use_style_instead_body,
+ equivalent.getTitle()))
+ .setPositiveButton(getString(R.string.use_style_button,
+ equivalent.getTitle()),
+ (dialogInterface, i) -> applyTheme(equivalent))
+ .setNegativeButton(R.string.no_thanks, null)
+ .create()
+ .show();
+ } else {
+ applyTheme(themeToApply);
+ }
}
}
@@ -179,6 +188,26 @@
});
}
+ private void applyTheme(ThemeBundle themeToApply) {
+ mThemeManager.apply(themeToApply, new Callback() {
+ @Override
+ public void onSuccess() {
+ Toast.makeText(CustomThemeActivity.this, R.string.applied_theme_msg,
+ Toast.LENGTH_LONG).show();
+ setResult(RESULT_THEME_APPLIED);
+ finish();
+ }
+
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ Log.w(TAG, "Error applying custom theme", throwable);
+ Toast.makeText(CustomThemeActivity.this,
+ R.string.apply_theme_error_msg,
+ Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+
private CustomThemeComponentFragment getCurrentStepFragment() {
return (CustomThemeComponentFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);