Add wallpaper support to theme bundles
Show wallpaper preview if available, show a checkbox to keep the
current wallpaper and apply the theme's wallpaper if needed.
Bug: 120559294
Change-Id: I229ab6e3372ace8218356d965e8d38f074e95061
diff --git a/res/layout/fragment_theme_picker.xml b/res/layout/fragment_theme_picker.xml
index 245a22c..e20c5f9 100644
--- a/res/layout/fragment_theme_picker.xml
+++ b/res/layout/fragment_theme_picker.xml
@@ -45,6 +45,13 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <CheckBox
+ android:id="@+id/use_my_wallpaper"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:text="@string/keep_my_wallpaper"/>
<Button
android:id="@+id/apply_button"
style="@style/ActionPrimaryButton"
diff --git a/res/layout/preview_card_wallpaper_content.xml b/res/layout/preview_card_wallpaper_content.xml
new file mode 100644
index 0000000..0b43c2f
--- /dev/null
+++ b/res/layout/preview_card_wallpaper_content.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/preview_static_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ tools:showIn="@layout/theme_preview_card">
+ <TextView
+ style="@style/CardTitleTextAppearance"
+ android:id="@+id/wallpaper_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|bottom"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/theme_preview_card.xml b/res/layout/theme_preview_card.xml
index fcb9e1e..18f2b15 100644
--- a/res/layout/theme_preview_card.xml
+++ b/res/layout/theme_preview_card.xml
@@ -16,27 +16,34 @@
-->
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/PreviewCard"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ app:contentPadding="0dp">
- <LinearLayout
+ <FrameLayout
+ android:id="@+id/theme_preview_card_background"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView
- android:id="@+id/theme_preview_card_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="8dp"
- android:drawablePadding="10dp"
- android:textAppearance="@style/CardTitleTextAppearance"/>
- <FrameLayout
- android:id="@+id/theme_preview_card_body_container"
+ android:layout_height="match_parent">
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginHorizontal="8dp"/>
- </LinearLayout>
-
+ android:padding="@dimen/preview_card_padding"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/theme_preview_card_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ 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"/>
+ </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 e491c39..e451795 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 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>
+
<!-- Label for a button that allows the user to apply the currently selected customization option.
[CHAR LIMIT=20] -->
<string name="apply_btn">Apply</string>
@@ -81,4 +85,8 @@
<!-- 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 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>
</resources>
diff --git a/src/com/android/customization/model/CustomizationManager.java b/src/com/android/customization/model/CustomizationManager.java
index 2a783a8..3f75be5 100644
--- a/src/com/android/customization/model/CustomizationManager.java
+++ b/src/com/android/customization/model/CustomizationManager.java
@@ -15,6 +15,8 @@
*/
package com.android.customization.model;
+import androidx.annotation.Nullable;
+
import java.util.List;
/**
@@ -24,6 +26,22 @@
public interface CustomizationManager<T extends CustomizationOption> {
/**
+ * Callback for applying a customization option.
+ */
+ interface Callback {
+ /**
+ * Called after an option was applied successfully.
+ */
+ void onSuccess();
+
+ /**
+ * Called if there was an error applying the customization
+ * @param throwable Exception thrown if available.
+ */
+ void onError(@Nullable Throwable throwable);
+ }
+
+ /**
* Listener interface for fetching CustomizationOptions
*/
interface OptionsFetchedListener<T extends CustomizationOption> {
@@ -41,7 +59,7 @@
/**
* Applies the given option into the system.
*/
- void apply(T option);
+ void apply(T option, Callback callback);
/**
* Loads the available options for the type of Customization managed by this class, calling the
diff --git a/src/com/android/customization/model/clock/ClockManager.java b/src/com/android/customization/model/clock/ClockManager.java
index 4cfe64a..13c3530 100644
--- a/src/com/android/customization/model/clock/ClockManager.java
+++ b/src/com/android/customization/model/clock/ClockManager.java
@@ -16,7 +16,6 @@
package com.android.customization.model.clock;
import android.content.Context;
-import android.provider.Settings;
import android.provider.Settings.Secure;
import com.android.customization.model.CustomizationManager;
@@ -39,9 +38,14 @@
}
@Override
- public void apply(Clockface option) {
- Settings.Secure.putString(mContext.getContentResolver(),
+ public void apply(Clockface option, Callback callback) {
+ boolean stored = Secure.putString(mContext.getContentResolver(),
CLOCK_FACE_SETTING, option.getId());
+ if (stored) {
+ callback.onSuccess();
+ } else {
+ callback.onError(null);
+ }
}
@Override
diff --git a/src/com/android/customization/model/grid/GridOptionsManager.java b/src/com/android/customization/model/grid/GridOptionsManager.java
index 365a822..b411e10 100644
--- a/src/com/android/customization/model/grid/GridOptionsManager.java
+++ b/src/com/android/customization/model/grid/GridOptionsManager.java
@@ -41,8 +41,13 @@
}
@Override
- public void apply(GridOption option) {
- mProvider.applyGrid(option.name);
+ public void apply(GridOption option, Callback callback) {
+ int updated = mProvider.applyGrid(option.name);
+ if (updated == 1) {
+ callback.onSuccess();
+ } else {
+ callback.onError(null);
+ }
}
@Override
diff --git a/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java b/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java
index 89c45bc..a824b28 100644
--- a/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java
+++ b/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java
@@ -124,7 +124,7 @@
return mOptions;
}
- void applyGrid(String name) {
+ int applyGrid(String name) {
Uri updateDefaultUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(mProviderInfo.authority)
@@ -132,6 +132,6 @@
.build();
ContentValues values = new ContentValues();
values.put("name", name);
- mContext.getContentResolver().update(updateDefaultUri, values, null, null);
+ return mContext.getContentResolver().update(updateDefaultUri, values, null, null);
}
}
diff --git a/src/com/android/customization/model/theme/DefaultThemeProvider.java b/src/com/android/customization/model/theme/DefaultThemeProvider.java
index 911aa7c..a2cb95c 100644
--- a/src/com/android/customization/model/theme/DefaultThemeProvider.java
+++ b/src/com/android/customization/model/theme/DefaultThemeProvider.java
@@ -66,6 +66,10 @@
private static final String ICON_PREVIEW_DRAWABLE_NAME = "ic_wifi_signal_3";
private static final String PREVIEW_COLOR_PREFIX = "theme_preview_color_";
private static final String PREVIEW_SHAPE_PREFIX = "theme_preview_shape_";
+ private static final String WALLPAPER_PREFIX = "theme_wallpaper_";
+ private static final String WALLPAPER_TITLE_PREFIX = "theme_wallpaper_title_";
+ private static final String WALLPAPER_ATTRIBUTION_PREFIX = "theme_wallpaper_attribution_";
+ private static final String WALLPAPER_ACTION_PREFIX = "theme_wallpaper_action_";
private static final String DEFAULT_THEME_NAME= "default";
@@ -194,6 +198,27 @@
iconSettingsOverlayPackage);
}
+ try {
+ String wallpaperResName = WALLPAPER_PREFIX + themeName;
+ int wallpaperResId = mStubApkResources.getIdentifier(wallpaperResName,
+ "drawable", mStubPackageName);
+ if (wallpaperResId > 0) {
+ builder.setWallpaperInfo(mStubPackageName, wallpaperResName,
+ themeName, wallpaperResId,
+ mStubApkResources.getIdentifier(WALLPAPER_TITLE_PREFIX + themeName,
+ "string", mStubPackageName),
+ mStubApkResources.getIdentifier(
+ WALLPAPER_ATTRIBUTION_PREFIX + themeName, "string",
+ mStubPackageName),
+ mStubApkResources.getIdentifier(WALLPAPER_ACTION_PREFIX + themeName,
+ "string", mStubPackageName))
+ .setWallpaperAsset(
+ getDrawableResourceAsset(WALLPAPER_PREFIX, themeName));
+ }
+ } catch (NotFoundException e) {
+ // Nothing to do here, if there's no wallpaper we'll just omit wallpaper
+ }
+
mThemes.add(builder.build());
} catch (NameNotFoundException | NotFoundException e) {
Log.w(TAG, String.format("Couldn't load part of theme %s, will skip it", themeName),
@@ -310,6 +335,30 @@
}
}
+ try {
+ String wallpaperResName = WALLPAPER_PREFIX + DEFAULT_THEME_NAME;
+ int wallpaperResId = mStubApkResources.getIdentifier(wallpaperResName,
+ "drawable", mStubPackageName);
+ if (wallpaperResId > 0) {
+ builder.setWallpaperInfo(mStubPackageName, wallpaperResName, DEFAULT_THEME_NAME,
+ mStubApkResources.getIdentifier(
+ wallpaperResName,
+ "drawable", mStubPackageName),
+ mStubApkResources.getIdentifier(WALLPAPER_TITLE_PREFIX + DEFAULT_THEME_NAME,
+ "string", mStubPackageName),
+ mStubApkResources.getIdentifier(
+ WALLPAPER_ATTRIBUTION_PREFIX + DEFAULT_THEME_NAME, "string",
+ mStubPackageName),
+ mStubApkResources.getIdentifier(
+ WALLPAPER_ACTION_PREFIX + DEFAULT_THEME_NAME,
+ "string", mStubPackageName))
+ .setWallpaperAsset(
+ getDrawableResourceAsset(WALLPAPER_PREFIX, DEFAULT_THEME_NAME));
+ }
+ } catch (NotFoundException e) {
+ // Nothing to do here, if there's no wallpaper we'll just omit wallpaper
+ }
+
mThemes.add(builder.build());
}
diff --git a/src/com/android/customization/model/theme/ThemeBundle.java b/src/com/android/customization/model/theme/ThemeBundle.java
index fd57da9..aa28ad8 100644
--- a/src/com/android/customization/model/theme/ThemeBundle.java
+++ b/src/com/android/customization/model/theme/ThemeBundle.java
@@ -15,6 +15,7 @@
*/
package com.android.customization.model.theme;
+import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Typeface;
@@ -29,12 +30,15 @@
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import androidx.core.graphics.PathParser;
import com.android.customization.model.CustomizationManager;
import com.android.customization.model.CustomizationOption;
import com.android.wallpaper.R;
+import com.android.wallpaper.asset.Asset;
import com.android.wallpaper.asset.ResourceAsset;
+import com.android.wallpaper.model.WallpaperInfo;
import org.json.JSONObject;
@@ -56,13 +60,17 @@
private final PreviewInfo mPreviewInfo;
private final boolean mIsDefault;
private final Map<String, String> mPackagesByCategory;
+ @Nullable private final WallpaperInfo mWallpaperInfo;
+ private WallpaperInfo mOverrideWallpaper;
private ThemeBundle(String title, Map<String, String> overlayPackages,
- boolean isDefault, PreviewInfo previewInfo) {
+ boolean isDefault, @Nullable WallpaperInfo wallpaperInfo,
+ PreviewInfo previewInfo) {
mTitle = title;
mIsDefault = isDefault;
mPreviewInfo = previewInfo;
- mPackagesByCategory = Collections.unmodifiableMap(overlayPackages);
+ mWallpaperInfo = wallpaperInfo;
+ mPackagesByCategory = Collections.unmodifiableMap(overlayPackages);
}
@Override
@@ -114,6 +122,24 @@
return mPreviewInfo;
}
+ public void setOverrideThemeWallpaper(WallpaperInfo homeWallpaper) {
+ mOverrideWallpaper = homeWallpaper;
+ }
+
+ public boolean useThemeWallpaper() {
+ return mOverrideWallpaper == null && mWallpaperInfo != null;
+ }
+
+ public Asset getWallpaperPreviewAsset(Context context) {
+ return mOverrideWallpaper != null ?
+ mOverrideWallpaper.getThumbAsset(context) :
+ getPreviewInfo().wallpaperAsset;
+ }
+
+ public WallpaperInfo getWallpaperInfo() {
+ return mWallpaperInfo;
+ }
+
boolean isDefault() {
return mIsDefault;
}
@@ -122,7 +148,7 @@
return mPackagesByCategory.values();
}
- String getSerializedPackages() {
+ public String getSerializedPackages() {
if (isDefault()) {
return "";
}
@@ -130,6 +156,7 @@
return new JSONObject(mPackagesByCategory).toString();
}
+
public static class PreviewInfo {
public final Typeface bodyFontFamily;
public final Typeface headlineFontFamily;
@@ -137,23 +164,24 @@
@ColorInt public final int colorAccentDark;
public final List<Drawable> icons;
public final Drawable shapeDrawable;
- @DrawableRes public final int wallpaperDrawableRes;
- public final ResourceAsset colorPreviewDrawable;
- public final ResourceAsset shapePreviewDrawable;
+ @Nullable public final ResourceAsset wallpaperAsset;
+ @Nullable public final ResourceAsset colorPreviewAsset;
+ @Nullable public final ResourceAsset shapePreviewAsset;
private PreviewInfo(Typeface bodyFontFamily, Typeface headlineFontFamily,
int colorAccentLight, int colorAccentDark, List<Drawable> icons,
- Drawable shapeDrawable, int wallpaperDrawableRes,
- ResourceAsset colorPreviewDrawable, ResourceAsset shapePreviewDrawable) {
+ Drawable shapeDrawable, @Nullable ResourceAsset wallpaperAsset,
+ @Nullable ResourceAsset colorPreviewAsset,
+ @Nullable ResourceAsset shapePreviewAsset) {
this.bodyFontFamily = bodyFontFamily;
this.headlineFontFamily = headlineFontFamily;
this.colorAccentLight = colorAccentLight;
this.colorAccentDark = colorAccentDark;
this.icons = icons;
this.shapeDrawable = shapeDrawable;
- this.wallpaperDrawableRes = wallpaperDrawableRes;
- this.colorPreviewDrawable = colorPreviewDrawable;
- this.shapePreviewDrawable = shapePreviewDrawable;
+ this.wallpaperAsset = wallpaperAsset;
+ this.colorPreviewAsset = colorPreviewAsset;
+ this.shapePreviewAsset = shapePreviewAsset;
}
}
@@ -167,9 +195,10 @@
private List<Drawable> mIcons = new ArrayList<>();
private String mShapePath;
private boolean mIsDefault;
- @DrawableRes private int mWallpaperDrawableResId;
+ private ResourceAsset mWallpaperAsset;
private ResourceAsset mColorPreview;
private ResourceAsset mShapePreview;
+ private WallpaperInfo mWallpaperInfo;
private Map<String, String> mPackages = new HashMap<>();
public ThemeBundle build() {
@@ -181,9 +210,9 @@
shapeDrawable.setIntrinsicHeight((int) PATH_SIZE);
shapeDrawable.setIntrinsicWidth((int) PATH_SIZE);
}
- return new ThemeBundle(mTitle, mPackages, mIsDefault,
+ return new ThemeBundle(mTitle, mPackages, mIsDefault, mWallpaperInfo,
new PreviewInfo(mBodyFontFamily, mHeadlineFontFamily, mColorAccentLight,
- mColorAccentDark, mIcons, shapeDrawable, mWallpaperDrawableResId,
+ mColorAccentDark, mIcons, shapeDrawable, mWallpaperAsset,
mColorPreview, mShapePreview));
}
@@ -237,6 +266,19 @@
return this;
}
+ public Builder setWallpaperInfo(String wallpaperPackageName, String wallpaperResName,
+ String themeId, @DrawableRes int wallpaperResId, @StringRes int titleResId,
+ @StringRes int attributionResId, @StringRes int actionUrlResId) {
+ mWallpaperInfo = new ThemeBundledWallpaperInfo(wallpaperPackageName, wallpaperResName,
+ themeId, wallpaperResId, titleResId, attributionResId, actionUrlResId);
+ return this;
+ }
+
+ public Builder setWallpaperAsset(ResourceAsset wallpaperAsset) {
+ mWallpaperAsset = wallpaperAsset;
+ return this;
+ }
+
public Builder asDefault() {
mIsDefault = true;
return this;
diff --git a/src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java b/src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java
new file mode 100644
index 0000000..4f6a29f
--- /dev/null
+++ b/src/com/android/customization/model/theme/ThemeBundledWallpaperInfo.java
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+package com.android.customization.model.theme;
+
+import static com.google.android.apps.wallpaper.model.Action.getActionIconForType;
+import static com.google.android.apps.wallpaper.model.Action.getActionLabelForType;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Parcel;
+import android.util.Log;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.StringRes;
+
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.asset.ResourceAsset;
+import com.android.wallpaper.model.InlinePreviewIntentFactory;
+import com.android.wallpaper.model.WallpaperInfo;
+
+import com.google.android.apps.wallpaper.model.Action.ActionType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a wallpaper coming from the resources of the theme bundle container APK.
+ */
+public class ThemeBundledWallpaperInfo extends WallpaperInfo {
+ public static final Creator<ThemeBundledWallpaperInfo> CREATOR =
+ new Creator<ThemeBundledWallpaperInfo>() {
+ @Override
+ public ThemeBundledWallpaperInfo createFromParcel(Parcel in) {
+ return new ThemeBundledWallpaperInfo(in);
+ }
+
+ @Override
+ public ThemeBundledWallpaperInfo[] newArray(int size) {
+ return new ThemeBundledWallpaperInfo[size];
+ }
+ };
+
+ private static final String TAG = "ThemeBundledWallpaperInfo";
+
+ private final String mPackageName;
+ private final String mResName;
+ private final String mCollectionId;
+ @DrawableRes private final int mDrawableResId;
+ @StringRes private final int mTitleResId;
+ @StringRes private final int mAttributionResId;
+ @StringRes private final int mActionUrlResId;
+ private List<String> mAttributions;
+ private String mActionUrl;
+ private Resources mResources;
+ private Asset mAsset;
+
+ /**
+ * Constructs a new theme-bundled static wallpaper model object.
+ *
+ * @param drawableResId Resource ID of the raw wallpaper image.
+ * @param resName The unique name of the wallpaper resource, e.g. "z_wp001".
+ * @param themeName Unique name of the collection this wallpaper belongs in; used for logging.
+ * @param titleResId Resource ID of the string for the title attribution.
+ * @param attributionResId Resource ID of the string for the first subtitle attribution.
+ */
+ public ThemeBundledWallpaperInfo(String packageName, String resName, String themeName,
+ int drawableResId, int titleResId, int attributionResId, int actionUrlResId) {
+ mPackageName = packageName;
+ mResName = resName;
+ mCollectionId = themeName;
+ mDrawableResId = drawableResId;
+ mTitleResId = titleResId;
+ mAttributionResId = attributionResId;
+ mActionUrlResId = actionUrlResId;
+ }
+
+ private ThemeBundledWallpaperInfo(Parcel in) {
+ mPackageName = in.readString();
+ mResName = in.readString();
+ mCollectionId = in.readString();
+ mDrawableResId = in.readInt();
+ mTitleResId = in.readInt();
+ mAttributionResId = in.readInt();
+ mActionUrlResId = in.readInt();
+ }
+
+ @Override
+ public List<String> getAttributions(Context context) {
+ if (mAttributions == null) {
+ Resources res = getPackageResources(context);
+ mAttributions = new ArrayList<>();
+ if (mTitleResId != 0) {
+ mAttributions.add(res.getString(mTitleResId));
+ }
+ if (mAttributionResId != 0) {
+ mAttributions.add(res.getString(mAttributionResId));
+ }
+ }
+
+ return mAttributions;
+ }
+
+ @Override
+ public String getActionUrl(Context context) {
+ if (mActionUrl == null && mActionUrlResId != 0) {
+ mActionUrl = getPackageResources(context).getString(mActionUrlResId);
+ }
+ return mActionUrl;
+ }
+
+ @Override
+ public int getActionLabelRes(Context context) {
+ return getActionLabelForType(getActionType(context));
+ }
+
+ @Override
+ public Asset getAsset(Context context) {
+ if (mAsset == null) {
+ Resources res = getPackageResources(context);
+ mAsset = new ResourceAsset(res, mDrawableResId);
+ }
+
+ return mAsset;
+ }
+
+ @Override
+ public Asset getThumbAsset(Context context) {
+ return getAsset(context);
+ }
+
+ @Override
+ public int getActionIconRes(Context context) {
+ return getActionIconForType(getActionType(context));
+ }
+
+ @Override
+ public void showPreview(Activity srcActivity, InlinePreviewIntentFactory factory,
+ int requestCode) {
+ try {
+ srcActivity.startActivityForResult(factory.newIntent(srcActivity, this), requestCode);
+ } catch (ActivityNotFoundException |SecurityException e) {
+ Log.e(TAG, "App isn't installed or ThemePicker doesn't have permission to launch", e);
+ }
+
+ }
+
+ @Override
+ public String getCollectionId(Context unused) {
+ return mCollectionId;
+ }
+
+ @Override
+ public String getWallpaperId() {
+ return mResName;
+ }
+
+ public String getResName() {
+ return mResName;
+ }
+
+ private int getActionType(Context context) {
+ return ActionType.EXPLORE;
+ }
+
+ /**
+ * Returns the {@link Resources} instance for the theme bundles stub APK.
+ */
+ private Resources getPackageResources(Context context) {
+ if (mResources != null) {
+ return mResources;
+ }
+
+ try {
+ mResources = context.getPackageManager().getResourcesForApplication(mPackageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Could not get app resources for " + mPackageName);
+ }
+ return mResources;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mPackageName);
+ dest.writeString(mResName);
+ dest.writeString(mCollectionId);
+ dest.writeInt(mDrawableResId);
+ dest.writeInt(mTitleResId);
+ dest.writeInt(mAttributionResId);
+ dest.writeInt(mActionUrlResId);
+ }
+}
diff --git a/src/com/android/customization/model/theme/ThemeManager.java b/src/com/android/customization/model/theme/ThemeManager.java
index 2b0bcf8..e56503b 100644
--- a/src/com/android/customization/model/theme/ThemeManager.java
+++ b/src/com/android/customization/model/theme/ThemeManager.java
@@ -19,15 +19,21 @@
import static com.android.customization.model.ResourceConstants.SETTINGS_PACKAGE;
import static com.android.customization.model.ResourceConstants.SYSUI_PACKAGE;
-import android.content.Context;
+import android.app.Activity;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
+import android.graphics.Point;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.annotation.Nullable;
import com.android.customization.model.CustomizationManager;
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.module.WallpaperPersister;
+import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
+import com.android.wallpaper.module.WallpaperSetter;
+import com.android.wallpaper.util.WallpaperCropUtils;
import java.util.HashMap;
import java.util.HashSet;
@@ -64,14 +70,17 @@
private final ThemeBundleProvider mProvider;
private final OverlayManager mOverlayManager;
- private final Context mContext;
- private boolean useThemeWallpaper;
+ private final WallpaperSetter mWallpaperSetter;
+ private final Activity mActivity;
+
private Map<String, String> mCurrentOverlays;
- public ThemeManager(ThemeBundleProvider provider, Context context) {
+ public ThemeManager(ThemeBundleProvider provider, Activity activity,
+ WallpaperSetter wallpaperSetter) {
mProvider = provider;
- mContext = context;
- mOverlayManager = context.getSystemService(OverlayManager.class);
+ mActivity = activity;
+ mOverlayManager = activity.getSystemService(OverlayManager.class);
+ mWallpaperSetter = wallpaperSetter;
}
@Override
@@ -80,26 +89,70 @@
}
@Override
- public void apply(ThemeBundle theme) {
+ public void apply(ThemeBundle theme, Callback callback) {
+ // Set wallpaper
+ if (theme.useThemeWallpaper()) {
+ applyWallpaper(theme, new SetWallpaperCallback() {
+ @Override
+ public void onSuccess() {
+ applyOverlays(theme, callback);
+ }
+
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ callback.onError(throwable);
+ }
+ });
+ } else {
+ applyOverlays(theme, callback);
+ }
+ }
+
+ private void applyWallpaper(ThemeBundle theme, SetWallpaperCallback callback) {
+ Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
+ mActivity.getResources(),
+ mActivity.getWindowManager().getDefaultDisplay());
+ Asset wallpaperAsset = theme.getWallpaperInfo().getAsset(mActivity);
+ wallpaperAsset.decodeRawDimensions(mActivity,
+ dimensions -> {
+ float scale = 1f;
+ // Calculate scale to fit the screen height
+ if (dimensions != null && dimensions.y > 0) {
+ scale = (float) defaultCropSurfaceSize.y / dimensions.y;
+ }
+ mWallpaperSetter.setCurrentWallpaper(mActivity,
+ theme.getWallpaperInfo(),
+ wallpaperAsset,
+ WallpaperPersister.DEST_BOTH,
+ scale, null, callback);
+ });
+ }
+
+ private void applyOverlays(ThemeBundle theme, Callback callback) {
+ boolean allApplied = true;
if (theme.isDefault()) {
- disableCurrentOverlay(ANDROID_PACKAGE, OVERLAY_CATEGORY_COLOR);
- disableCurrentOverlay(ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT);
- disableCurrentOverlay(ANDROID_PACKAGE, OVERLAY_CATEGORY_SHAPE);
- disableCurrentOverlay(ANDROID_PACKAGE, OVERLAY_CATEGORY_ICON_ANDROID);
- disableCurrentOverlay(SYSUI_PACKAGE, OVERLAY_CATEGORY_ICON_SYSUI);
- disableCurrentOverlay(SETTINGS_PACKAGE, OVERLAY_CATEGORY_ICON_SETTINGS);
+ allApplied &= disableCurrentOverlay(ANDROID_PACKAGE, OVERLAY_CATEGORY_COLOR);
+ allApplied &= disableCurrentOverlay(ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT);
+ allApplied &= disableCurrentOverlay(ANDROID_PACKAGE, OVERLAY_CATEGORY_SHAPE);
+ allApplied &= disableCurrentOverlay(ANDROID_PACKAGE, OVERLAY_CATEGORY_ICON_ANDROID);
+ allApplied &= disableCurrentOverlay(SYSUI_PACKAGE, OVERLAY_CATEGORY_ICON_SYSUI);
+ allApplied &= disableCurrentOverlay(SETTINGS_PACKAGE, OVERLAY_CATEGORY_ICON_SETTINGS);
} else {
for (String packageName : theme.getAllPackages()) {
if (packageName != null) {
- mOverlayManager.setEnabledExclusiveInCategory(packageName,
+ allApplied &= mOverlayManager.setEnabledExclusiveInCategory(packageName,
UserHandle.myUserId());
}
}
}
- Settings.Secure.putString(mContext.getContentResolver(), THEME_SETTING,
- theme.getSerializedPackages());
+ allApplied &= Settings.Secure.putString(mActivity.getContentResolver(),
+ THEME_SETTING, theme.getSerializedPackages());
mCurrentOverlays = null;
- // TODO: set wallpaper
+ if (allApplied) {
+ callback.onSuccess();
+ } else {
+ callback.onError(null);
+ }
}
@Override
@@ -107,11 +160,12 @@
mProvider.fetch(callback, false);
}
- private void disableCurrentOverlay(String packageName, String category) {
+ private boolean disableCurrentOverlay(String packageName, String category) {
OverlayInfo current = getEnabledOverlayInfo(packageName, category);
if (current != null) {
- mOverlayManager.setEnabled(current.packageName, false, UserHandle.myUserId());
+ return mOverlayManager.setEnabled(current.packageName, false, UserHandle.myUserId());
}
+ return true;
}
@Nullable
@@ -146,6 +200,6 @@
}
public String getStoredOverlays() {
- return Settings.Secure.getString(mContext.getContentResolver(), THEME_SETTING);
+ return Settings.Secure.getString(mActivity.getContentResolver(), THEME_SETTING);
}
}
diff --git a/src/com/android/customization/picker/CustomizationPickerActivity.java b/src/com/android/customization/picker/CustomizationPickerActivity.java
index d86f5dc..2257946 100644
--- a/src/com/android/customization/picker/CustomizationPickerActivity.java
+++ b/src/com/android/customization/picker/CustomizationPickerActivity.java
@@ -54,6 +54,7 @@
import com.android.wallpaper.module.Injector;
import com.android.wallpaper.module.InjectorProvider;
import com.android.wallpaper.module.UserEventLogger;
+import com.android.wallpaper.module.WallpaperSetter;
import com.android.wallpaper.picker.CategoryFragment;
import com.android.wallpaper.picker.CategoryFragment.CategoryFragmentHost;
import com.android.wallpaper.picker.MyPhotosStarter;
@@ -84,6 +85,7 @@
private static final Map<Integer, CustomizationSection> mSections = new HashMap<>();
private CategoryFragment mWallpaperCategoryFragment;
+ private WallpaperSetter mWallpaperSetter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -145,7 +147,11 @@
return;
}
//Theme
- ThemeManager themeManager = new ThemeManager(new DefaultThemeProvider(this), this);
+ Injector injector = InjectorProvider.getInjector();
+ mWallpaperSetter = new WallpaperSetter(injector.getWallpaperPersister(this),
+ injector.getPreferences(this), mUserEventLogger, false);
+ ThemeManager themeManager = new ThemeManager(new DefaultThemeProvider(this), this,
+ mWallpaperSetter);
if (themeManager.isAvailable()) {
mSections.put(R.id.nav_theme, new ThemeSection(R.id.nav_theme, themeManager));
}
@@ -260,6 +266,14 @@
return section == null ? null : (ThemeManager) section.customizationManager;
}
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mWallpaperSetter != null) {
+ mWallpaperSetter.cleanUp();
+ }
+ }
+
/**
* Represents a section of the Picker (eg "ThemeBundle", "Clock", etc).
* There should be a concrete subclass per available section, providing the corresponding
diff --git a/src/com/android/customization/picker/clock/ClockFragment.java b/src/com/android/customization/picker/clock/ClockFragment.java
index ab38987..5e51dab 100644
--- a/src/com/android/customization/picker/clock/ClockFragment.java
+++ b/src/com/android/customization/picker/clock/ClockFragment.java
@@ -27,6 +27,7 @@
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.customization.model.CustomizationManager.Callback;
import com.android.customization.model.clock.ClockManager;
import com.android.customization.model.clock.Clockface;
import com.android.customization.picker.BasePreviewAdapter;
@@ -78,8 +79,18 @@
mOptionsContainer = view.findViewById(R.id.options_container);
setUpOptions();
view.findViewById(R.id.apply_button).setOnClickListener(v -> {
- mClockManager.apply(mSelectedOption);
- getActivity().finish();
+ mClockManager.apply(mSelectedOption, new Callback() {
+ @Override
+ public void onSuccess() {
+ getActivity().finish();
+ }
+
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ //TODO(santie): handle
+ }
+ });
+
});
return view;
}
diff --git a/src/com/android/customization/picker/grid/GridFragment.java b/src/com/android/customization/picker/grid/GridFragment.java
index 845d008..8d7a5c1 100644
--- a/src/com/android/customization/picker/grid/GridFragment.java
+++ b/src/com/android/customization/picker/grid/GridFragment.java
@@ -33,6 +33,7 @@
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.customization.model.CustomizationManager.Callback;
import com.android.customization.model.grid.GridOption;
import com.android.customization.model.grid.GridOptionsManager;
import com.android.customization.picker.BasePreviewAdapter;
@@ -101,8 +102,18 @@
mScreenAspectRatio = (float) dm.heightPixels / dm.widthPixels;
setUpOptions();
view.findViewById(R.id.apply_button).setOnClickListener(v -> {
- mGridManager.apply(mSelectedOption);
- getActivity().finish();
+ mGridManager.apply(mSelectedOption, new Callback() {
+ @Override
+ public void onSuccess() {
+ getActivity().finish();
+ }
+
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ //TODO(santie): handle
+ }
+ });
+
});
CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector()
.getCurrentWallpaperFactory(getContext().getApplicationContext());
diff --git a/src/com/android/customization/picker/theme/ThemeFragment.java b/src/com/android/customization/picker/theme/ThemeFragment.java
index b95746b..a7d8af2 100644
--- a/src/com/android/customization/picker/theme/ThemeFragment.java
+++ b/src/com/android/customization/picker/theme/ThemeFragment.java
@@ -19,10 +19,15 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@@ -34,8 +39,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.RecyclerView;
-import androidx.viewpager.widget.PagerAdapter;
+import com.android.customization.model.CustomizationManager.Callback;
import com.android.customization.model.theme.ThemeBundle;
import com.android.customization.model.theme.ThemeManager;
import com.android.customization.picker.BasePreviewAdapter;
@@ -43,6 +48,9 @@
import com.android.customization.widget.OptionSelectorController;
import com.android.customization.widget.PreviewPager;
import com.android.wallpaper.R;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
+import com.android.wallpaper.module.InjectorProvider;
import com.android.wallpaper.picker.ToolbarFragment;
/**
@@ -50,13 +58,15 @@
*/
public class ThemeFragment extends ToolbarFragment {
+ private static final String TAG = "ThemeFragment";
+ private static final String KEY_SELECTED_THEME = "ThemeFragment.SelectedThemeBundle";
+
/**
* Interface to be implemented by an Activity hosting a {@link ThemeFragment}
*/
public interface ThemeFragmentHost {
ThemeManager getThemeManager();
}
-
public static ThemeFragment newInstance(CharSequence title) {
ThemeFragment fragment = new ThemeFragment();
fragment.setArguments(ToolbarFragment.createArguments(title));
@@ -67,8 +77,11 @@
private OptionSelectorController mOptionsController;
private ThemeManager mThemeManager;
private ThemeBundle mSelectedTheme;
- private PagerAdapter mAdapter;
+ private ThemePreviewAdapter mAdapter;
private PreviewPager mPreviewPager;
+ private boolean mUseMyWallpaper;
+ private WallpaperInfo mCurrentHomeWallpaper;
+ private CurrentWallpaperInfoFactory mCurrentWallpaperFactory;
@Override
public void onAttach(Context context) {
@@ -83,41 +96,105 @@
View view = inflater.inflate(
R.layout.fragment_theme_picker, container, /* attachToRoot */ false);
setUpToolbar(view);
+
+ mCurrentWallpaperFactory = InjectorProvider.getInjector()
+ .getCurrentWallpaperFactory(getActivity().getApplicationContext());
mPreviewPager = view.findViewById(R.id.theme_preview_pager);
mOptionsContainer = view.findViewById(R.id.options_container);
view.findViewById(R.id.apply_button).setOnClickListener(v -> {
- mThemeManager.apply(mSelectedTheme);
- Toast.makeText(getContext(), R.string.applied_theme_msg, Toast.LENGTH_LONG).show();
- getActivity().finish();
+ mThemeManager.apply(mSelectedTheme, new Callback() {
+ @Override
+ public void onSuccess() {
+ Toast.makeText(getContext(), R.string.applied_theme_msg,
+ Toast.LENGTH_LONG).show();
+ getActivity().finish();
+ }
+
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ Log.w(TAG, "Error applying theme", throwable);
+ Toast.makeText(getContext(), R.string.apply_theme_error_msg,
+ Toast.LENGTH_LONG).show();
+ }
+ });
+
});
- setUpOptions();
+ ((CheckBox)view.findViewById(R.id.use_my_wallpaper)).setOnCheckedChangeListener(
+ this::onUseMyWallpaperCheckChanged);
+
+ setUpOptions(savedInstanceState);
return view;
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ reloadWallpaper();
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (mSelectedTheme != null && !mSelectedTheme.isActive(mThemeManager)) {
+ outState.putString(KEY_SELECTED_THEME, mSelectedTheme.getSerializedPackages());
+ }
+ }
+
+ private void onUseMyWallpaperCheckChanged(CompoundButton checkbox, boolean checked) {
+ mUseMyWallpaper = checked;
+ reloadWallpaper();
+ }
+
+ private void reloadWallpaper() {
+ if (mUseMyWallpaper) {
+ mCurrentWallpaperFactory.createCurrentWallpaperInfos(
+ (homeWallpaper, lockWallpaper, presentationMode) -> {
+ if (mSelectedTheme != null) {
+ mCurrentHomeWallpaper = homeWallpaper;
+ mSelectedTheme.setOverrideThemeWallpaper(homeWallpaper);
+ if (mAdapter != null) {
+ mAdapter.rebindWallpaperIfAvailable();
+ }
+ }
+ }, false);
+ } else {
+ mCurrentHomeWallpaper = null;
+ if (mSelectedTheme != null) {
+ mSelectedTheme.setOverrideThemeWallpaper(null);
+ if (mAdapter != null) {
+ mAdapter.rebindWallpaperIfAvailable();
+ }
+ }
+ }
+ }
+
private void createAdapter() {
mAdapter = new ThemePreviewAdapter(getActivity(), mSelectedTheme);
mPreviewPager.setAdapter(mAdapter);
}
- private void setUpOptions() {
+ private void setUpOptions(@Nullable Bundle savedInstanceState) {
mThemeManager.fetchOptions(options -> {
mOptionsController = new OptionSelectorController(mOptionsContainer, options);
mOptionsController.addListener(selected -> {
mSelectedTheme = (ThemeBundle) selected;
+ mSelectedTheme.setOverrideThemeWallpaper(mCurrentHomeWallpaper);
createAdapter();
});
mOptionsController.initOptions(mThemeManager);
+ String previouslySelected = savedInstanceState != null
+ ? savedInstanceState.getString(KEY_SELECTED_THEME) : null;
+
for (ThemeBundle theme : options) {
- if (theme.isActive(mThemeManager)) {
+ if (previouslySelected != null
+ && previouslySelected.equals(theme.getSerializedPackages())) {
+ mSelectedTheme = theme;
+ } else if (theme.isActive(mThemeManager)) {
mSelectedTheme = theme;
}
}
- // For development only, as there should always be a theme set.
- if (mSelectedTheme == null) {
- mSelectedTheme = options.get(0);
- }
mOptionsController.setSelectedOption(mSelectedTheme);
});
createAdapter();
@@ -130,8 +207,9 @@
@ColorInt final int accentColor;
private final LayoutInflater inflater;
- private ThemePreviewPage(Context context, @StringRes int titleResId, @DrawableRes int iconSrc,
- @LayoutRes int contentLayoutRes, @ColorInt int accentColor) {
+ private ThemePreviewPage(Context context, @StringRes int titleResId,
+ @DrawableRes int iconSrc, @LayoutRes int contentLayoutRes,
+ @ColorInt int accentColor) {
super(null);
this.nameResId = titleResId;
this.iconSrc = iconSrc;
@@ -149,10 +227,14 @@
ViewGroup body = card.findViewById(R.id.theme_preview_card_body_container);
inflater.inflate(contentLayoutRes, body, true);
- bindBody();
+ bindBody(false);
}
- protected abstract void bindBody();
+ protected boolean containsWallpaper() {
+ return false;
+ }
+
+ protected abstract void bindBody(boolean forceRebind);
}
/**
@@ -173,7 +255,7 @@
addPage(new ThemePreviewPage(activity, R.string.preview_name_font, R.drawable.ic_font,
R.layout.preview_card_font_content, theme.getPreviewInfo().colorAccentLight) {
@Override
- protected void bindBody() {
+ protected void bindBody(boolean forceRebind) {
TextView title = card.findViewById(R.id.font_card_title);
title.setTypeface(theme.getPreviewInfo().headlineFontFamily);
TextView body = card.findViewById(R.id.font_card_body);
@@ -185,7 +267,7 @@
R.drawable.ic_wifi_24px, R.layout.preview_card_icon_content,
theme.getPreviewInfo().colorAccentLight) {
@Override
- protected void bindBody() {
+ protected void bindBody(boolean forceRebind) {
for (int i = 0; i < mIconIds.length; i++) {
((ImageView) card.findViewById(mIconIds[i])).setImageDrawable(
theme.getPreviewInfo().icons.get(i));
@@ -193,15 +275,14 @@
}
});
}
- if (theme.getPreviewInfo().colorPreviewDrawable != null) {
+ if (theme.getPreviewInfo().colorPreviewAsset != null) {
addPage(new ThemePreviewPage(activity, R.string.preview_name_color,
R.drawable.ic_colorize_24px, R.layout.preview_card_static_content,
theme.getPreviewInfo().colorAccentLight) {
@Override
- protected void bindBody() {
+ protected void bindBody(boolean forceRebind) {
ImageView staticImage = card.findViewById(R.id.preview_static_image);
-
- theme.getPreviewInfo().colorPreviewDrawable.loadDrawable(activity,
+ theme.getPreviewInfo().colorPreviewAsset.loadDrawable(activity,
staticImage, card.getCardBackgroundColor().getDefaultColor());
staticImage.getLayoutParams().width = res.getDimensionPixelSize(
R.dimen.color_preview_image_width);
@@ -210,14 +291,14 @@
}
});
}
- if (theme.getPreviewInfo().shapePreviewDrawable != null) {
+ if (theme.getPreviewInfo().shapePreviewAsset != null) {
addPage(new ThemePreviewPage(activity, R.string.preview_name_shape,
R.drawable.ic_shapes_24px, R.layout.preview_card_static_content,
theme.getPreviewInfo().colorAccentLight) {
@Override
- protected void bindBody() {
+ protected void bindBody(boolean forceRebind) {
ImageView staticImage = card.findViewById(R.id.preview_static_image);
- theme.getPreviewInfo().shapePreviewDrawable.loadDrawable(activity,
+ theme.getPreviewInfo().shapePreviewAsset.loadDrawable(activity,
staticImage, card.getCardBackgroundColor().getDefaultColor());
staticImage.getLayoutParams().width = res.getDimensionPixelSize(
@@ -227,6 +308,63 @@
}
});
}
+ if (theme.getPreviewInfo().wallpaperAsset != null) {
+ addPage(new ThemePreviewPage(activity, R.string.preview_name_wallpaper,
+ R.drawable.ic_wallpaper_24px, R.layout.preview_card_wallpaper_content,
+ theme.getPreviewInfo().colorAccentLight) {
+
+ private final WallpaperPreviewLayoutListener mListener =
+ new WallpaperPreviewLayoutListener(theme);
+
+ @Override
+ protected boolean containsWallpaper() {
+ return true;
+ }
+
+ @Override
+ protected void bindBody(boolean forceRebind) {
+ if (card == null) {
+ return;
+ }
+ card.addOnLayoutChangeListener(mListener);
+ if (forceRebind) {
+ card.requestLayout();
+ }
+ }
+ });
+ }
+ }
+
+ public void rebindWallpaperIfAvailable() {
+ for (ThemePreviewPage page : mPages) {
+ if (page.containsWallpaper()) {
+ page.bindBody(true);
+ }
+ }
+ }
+
+ private static class WallpaperPreviewLayoutListener implements OnLayoutChangeListener {
+ private final ThemeBundle mTheme;
+
+ public WallpaperPreviewLayoutListener(ThemeBundle theme) {
+ mTheme = theme;
+ }
+
+ @Override
+ public void onLayoutChange(View view, int left, int top, int right,
+ int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ int targetWidth = right - left;
+ int targetHeight = bottom - top;
+ if (targetWidth > 0 && targetHeight > 0) {
+ mTheme.getWallpaperPreviewAsset(view.getContext()).decodeBitmap(
+ targetWidth, targetHeight, bitmap -> {
+ Resources res = view.getContext().getResources();
+ view.findViewById(R.id.theme_preview_card_background)
+ .setBackground(new BitmapDrawable(res, bitmap));
+ });
+ view.removeOnLayoutChangeListener(this);
+ }
+ }
}
}
}