• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.wallpaper.model;
17 
18 import static com.android.wallpaper.model.CreativeCategory.KEY_WALLPAPER_SAVE_CREATIVE_CATEGORY_WALLPAPER;
19 
20 import android.annotation.Nullable;
21 import android.app.WallpaperInfo;
22 import android.content.ClipData;
23 import android.content.ContentProviderClient;
24 import android.content.ContentValues;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.PackageManager;
28 import android.database.Cursor;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.os.Parcel;
32 import android.text.TextUtils;
33 import android.util.Log;
34 
35 import androidx.activity.result.contract.ActivityResultContract;
36 import androidx.annotation.NonNull;
37 
38 import com.android.wallpaper.asset.Asset;
39 import com.android.wallpaper.asset.CreativeWallpaperThumbAsset;
40 
41 import java.util.ArrayList;
42 import java.util.List;
43 
44 /**
45  * Represents a creative live wallpaper component.
46  */
47 public class CreativeWallpaperInfo extends LiveWallpaperInfo {
48 
49     public static final Creator<CreativeWallpaperInfo> CREATOR =
50             new Creator<CreativeWallpaperInfo>() {
51                 @Override
52                 public CreativeWallpaperInfo createFromParcel(Parcel in) {
53                     return new CreativeWallpaperInfo(in);
54                 }
55 
56                 @Override
57                 public CreativeWallpaperInfo[] newArray(int size) {
58                     return new CreativeWallpaperInfo[size];
59                 }
60             };
61 
62     private Uri mConfigPreviewUri;
63     private Uri mCleanPreviewUri;
64     private Uri mDeleteUri;
65     private Uri mThumbnailUri;
66     private Uri mShareUri;
67     private String mTitle;
68     private String mAuthor;
69     private String mDescription;
70     private String mContentDescription;
71     private boolean mIsCurrent;
72     private String mGroupName;
73 
74     private static final String TAG = "CreativeWallpaperInfo";
75 
76     private ArrayList<WallpaperAction> mEffectsToggles = new ArrayList<>();
77     private String mEffectsBottomSheetTitle;
78     private String mEffectsBottomSheetSubtitle;
79     private Uri mClearActionsUri;
80     private Uri mEffectsUri = null;
81     private String mCurrentlyAppliedEffectId = null;
82 
CreativeWallpaperInfo(WallpaperInfo info, String title, @Nullable String author, @Nullable String description, String contentDescription, Uri configPreviewUri, Uri cleanPreviewUri, Uri deleteUri, Uri thumbnailUri, Uri shareUri, String groupName, boolean isCurrent)83     public CreativeWallpaperInfo(WallpaperInfo info, String title, @Nullable String author,
84             @Nullable String description, String contentDescription, Uri configPreviewUri,
85             Uri cleanPreviewUri, Uri deleteUri, Uri thumbnailUri, Uri shareUri, String groupName,
86             boolean isCurrent) {
87         this(info, /* visibleTitle= */ false, /* collectionId= */ null);
88         mTitle = title;
89         mAuthor = author;
90         mDescription = description;
91         mContentDescription = contentDescription;
92         mConfigPreviewUri = configPreviewUri;
93         mCleanPreviewUri = cleanPreviewUri;
94         mDeleteUri = deleteUri;
95         mThumbnailUri = thumbnailUri;
96         mShareUri = shareUri;
97         mIsCurrent = isCurrent;
98         mGroupName = groupName;
99     }
100 
CreativeWallpaperInfo(WallpaperInfo info, boolean isCurrent)101     public CreativeWallpaperInfo(WallpaperInfo info, boolean isCurrent) {
102         this(info, false, null);
103         mIsCurrent = isCurrent;
104     }
105 
CreativeWallpaperInfo(WallpaperInfo info, boolean visibleTitle, @Nullable String collectionId)106     public CreativeWallpaperInfo(WallpaperInfo info, boolean visibleTitle,
107             @Nullable String collectionId) {
108         super(info, visibleTitle, collectionId);
109     }
110 
CreativeWallpaperInfo(Parcel in)111     protected CreativeWallpaperInfo(Parcel in) {
112         super(in);
113         mTitle = in.readString();
114         mAuthor = in.readString();
115         mDescription = in.readString();
116         mContentDescription = in.readString();
117         mConfigPreviewUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
118         mCleanPreviewUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
119         mDeleteUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
120         mThumbnailUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
121         mShareUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
122         mIsCurrent = in.readBoolean();
123         mGroupName = in.readString();
124         mCurrentlyAppliedEffectId = in.readString();
125         mEffectsUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
126         mClearActionsUri = in.readParcelable(Uri.class.getClassLoader(), Uri.class);
127         mEffectsToggles = in.readArrayList(WallpaperAction.class.getClassLoader(),
128                 WallpaperAction.class);
129         mEffectsBottomSheetTitle = in.readString();
130         mEffectsBottomSheetSubtitle = in.readString();
131     }
132 
133     @Override
writeToParcel(Parcel parcel, int flags)134     public void writeToParcel(Parcel parcel, int flags) {
135         super.writeToParcel(parcel, flags);
136         parcel.writeString(mTitle);
137         parcel.writeString(mAuthor);
138         parcel.writeString(mDescription);
139         parcel.writeString(mContentDescription);
140         parcel.writeParcelable(mConfigPreviewUri, flags);
141         parcel.writeParcelable(mCleanPreviewUri, flags);
142         parcel.writeParcelable(mDeleteUri, flags);
143         parcel.writeParcelable(mThumbnailUri, flags);
144         parcel.writeParcelable(mShareUri, flags);
145         parcel.writeBoolean(mIsCurrent);
146         parcel.writeString(mGroupName);
147         parcel.writeString(mCurrentlyAppliedEffectId);
148         parcel.writeParcelable(mEffectsUri, flags);
149         parcel.writeParcelable(mClearActionsUri, flags);
150         parcel.writeList(mEffectsToggles);
151         parcel.writeString(mEffectsBottomSheetTitle);
152         parcel.writeString(mEffectsBottomSheetSubtitle);
153     }
154 
155     /**
156      * Creates a new {@link ActivityResultContract} used to request the settings Activity overlay
157      * for this wallpaper.
158      *
159      * @param intent settings intent
160      */
getContract(Intent intent)161     public static ActivityResultContract<Void, Integer> getContract(Intent intent) {
162         return new ActivityResultContract<Void, Integer>() {
163             @NonNull
164             @Override
165             public Intent createIntent(@NonNull Context context, Void unused) {
166                 return intent;
167             }
168 
169             @Override
170             public Integer parseResult(int i, @Nullable Intent intent) {
171                 return i;
172             }
173         };
174     }
175 
176     /**
177      * Loads the current wallpaper's effects.
178      *
179      * @param context context of the current android component
180      * @return an array list of WallpaperAction data objects
181      * for the currently previewing wallpaper
182      */
183     @Nullable
184     public ArrayList<WallpaperAction> getWallpaperEffects(Context context) {
185         if (mEffectsUri == null) {
186             return null;
187         }
188         mEffectsToggles.clear();
189         // TODO (269350033): Move content provider query off the main thread.
190         try (ContentProviderClient effectsClient =
191                      context.getContentResolver().acquireContentProviderClient(
192                              mEffectsUri.getAuthority())) {
193             try (Cursor effectsCursor = effectsClient.query(mEffectsUri, /* projection= */ null,
194                     /* selection= */ null, /* selectionArgs= */ null, /* sortOrder= */ null)) {
195                 if (effectsCursor == null) {
196                     return null;
197                 }
198                 while (effectsCursor.moveToNext()) {
199                     Uri effectsToggleUri = Uri.parse(
200                             effectsCursor.getString(effectsCursor.getColumnIndex(
201                                     WallpaperInfoContract.WALLPAPER_EFFECTS_TOGGLE_URI)));
202                     String effectsButtonLabel = effectsCursor.getString(
203                             effectsCursor.getColumnIndex(
204                                     WallpaperInfoContract.WALLPAPER_EFFECTS_BUTTON_LABEL));
205                     String effectsId = effectsCursor.getString(
206                             effectsCursor.getColumnIndex(
207                                     WallpaperInfoContract.WALLPAPER_EFFECTS_TOGGLE_ID));
208                     mEffectsToggles.add(new WallpaperAction(effectsButtonLabel,
209                             effectsToggleUri, effectsId, /* toggled= */ false));
210                 }
211                 return mEffectsToggles;
212             }
213         } catch (Exception e) {
214             Log.e(TAG, "Read wallpaper effects with exception.", e);
215         }
216         return null;
217     }
218 
219     @Override
220     public String getTitle(Context context) {
221         if (mVisibleTitle) {
222             return mTitle;
223         }
224         return null;
225     }
226 
227     @Override
228     public List<String> getAttributions(Context context) {
229         PackageManager packageManager = context.getPackageManager();
230         CharSequence labelCharSeq = mInfo.loadLabel(packageManager);
231         List<String> attributions = new ArrayList<>();
232         attributions.add(labelCharSeq == null ? null : labelCharSeq.toString());
233         attributions.add(mAuthor == null ? "" : mAuthor);
234         attributions.add(mDescription == null ? "" : mDescription);
235 
236         return attributions;
237     }
238 
239     @Override
240     public String getContentDescription(Context context) {
241         return mContentDescription;
242     }
243 
244     @Override
245     public Asset getThumbAsset(Context context) {
246         if (mThumbAsset == null) {
247             mThumbAsset = new CreativeWallpaperThumbAsset(context, mInfo, mThumbnailUri);
248         }
249         return mThumbAsset;
250     }
251 
252     @Override
253     public String getGroupName(Context context) {
254         return mGroupName;
255     }
256 
257     /**
258      * Calls the config URI to initialize the preview for this wallpaper.
259      */
260     public void initializeWallpaperPreview(Context context) {
261         if (mConfigPreviewUri != null) {
262             context.getContentResolver().update(mConfigPreviewUri, new ContentValues(), null);
263         }
264     }
265 
266     /**
267      * Calls the clean URI to de-initialize the preview for this wallpaper.
268      */
269     public void cleanUpWallpaperPreview(Context context) {
270         if (mCleanPreviewUri != null) {
271             context.getContentResolver().update(mCleanPreviewUri, new ContentValues(), null);
272         }
273     }
274 
275     /**
276      * Returns true if this wallpaper can be deleted.
277      */
278     public boolean canBeDeleted() {
279         return mDeleteUri != null && !TextUtils.isEmpty(mDeleteUri.toString());
280     }
281 
282     @Override
283     public boolean isApplied(WallpaperInfo currentWallpaper) {
284         return super.isApplied(currentWallpaper) && mIsCurrent;
285     }
286 
287     /**
288      * Requests the content provider to delete this wallpaper.
289      */
290     public void requestDelete(Context context) {
291         context.getContentResolver().delete(mDeleteUri, null, null);
292     }
293 
294     public void setEffectsToggles(ArrayList<WallpaperAction> effectsToggles) {
295         mEffectsToggles = effectsToggles;
296     }
297 
298     public ArrayList<WallpaperAction> getEffectsToggles() {
299         return mEffectsToggles;
300     }
301 
302     public String getEffectsBottomSheetTitle() {
303         return mEffectsBottomSheetTitle;
304     }
305 
306     public void setEffectsBottomSheetTitle(String effectsBottomSheetTitle) {
307         mEffectsBottomSheetTitle = effectsBottomSheetTitle;
308     }
309 
310     /**
311      * Returns the URI that can be used to save a creative category wallpaper.
312      * @return the save wallpaper URI
313      */
314     public Uri getSaveWallpaperUriForCreativeWallpaper() {
315         Bundle metaData = this.getWallpaperComponent().getServiceInfo().metaData;
316         if (metaData == null || !metaData.containsKey(
317                 KEY_WALLPAPER_SAVE_CREATIVE_CATEGORY_WALLPAPER)) {
318             return null;
319         }
320         String keyForCreativeCategoryWallpaper = (String) metaData.get(
321                 KEY_WALLPAPER_SAVE_CREATIVE_CATEGORY_WALLPAPER);
322         return Uri.parse(keyForCreativeCategoryWallpaper);
323     }
324 
325     public String getEffectsBottomSheetSubtitle() {
326         return mEffectsBottomSheetSubtitle;
327     }
328 
329     public void setEffectsBottomSheetSubtitle(String effectsBottomSheetSubtitle) {
330         mEffectsBottomSheetSubtitle = effectsBottomSheetSubtitle;
331     }
332 
333     public Uri getClearActionsUri() {
334         return mClearActionsUri;
335     }
336 
337     public void setClearActionsUri(Uri clearActionsUri) {
338         mClearActionsUri = clearActionsUri;
339     }
340 
341     public Uri getEffectsUri() {
342         return mEffectsUri;
343     }
344 
345     public void setEffectsUri(Uri effectsUri) {
346         mEffectsUri = effectsUri;
347     }
348 
349     public String getCurrentlyAppliedEffectId() {
350         return mCurrentlyAppliedEffectId;
351     }
352 
353     public void setCurrentlyAppliedEffectId(String currentlyAppliedEffectId) {
354         mCurrentlyAppliedEffectId = currentlyAppliedEffectId;
355     }
356     /**
357      * Returns true if this wallpaper can be shared.
358      */
359     public boolean canBeShared() {
360         return mShareUri != null && !TextUtils.isEmpty(mShareUri.toString());
361     }
362 
363     /**
364      * Gets the share wallpaper image intent.
365      */
366     public Intent getShareIntent() {
367         Intent shareIntent = new Intent(Intent.ACTION_SEND);
368         shareIntent.putExtra(Intent.EXTRA_STREAM, mShareUri);
369         shareIntent.setType("image/*");
370         shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
371         shareIntent.setClipData(ClipData.newRawUri(null, mShareUri));
372         return Intent.createChooser(shareIntent, null);
373     }
374 
375     /**
376      * This method returns whether wallpaper effects are supported for this wallpaper.
377      * @return boolean
378      */
379     public boolean doesSupportWallpaperEffects() {
380         return (mClearActionsUri != null && !TextUtils.isEmpty(mEffectsBottomSheetTitle));
381     }
382 
383     /**
384      * Triggers the content provider call to clear all effects upon the current.
385      * wallpaper.
386      */
387     public void clearEffects(Context context) {
388         if (doesSupportWallpaperEffects() && mClearActionsUri != null) {
389             context.getContentResolver().update(mClearActionsUri, new ContentValues(), null);
390         }
391     }
392 
393     /**
394      * Triggers the content provider call to apply the selected effect upon the current
395      * wallpaper.
396      */
397     public void applyEffect(Context context, Uri applyEffectUri) {
398         if (doesSupportWallpaperEffects()) {
399             context.getContentResolver().update(applyEffectUri, new ContentValues(), null);
400         }
401     }
402 
403     /**
404      * Creates an object of CreativeWallpaperInfo from the given cursor object.
405      *
406      * @param wallpaperInfo contains relevant metadata information about creative-category wallpaper
407      * @param cursor contains relevant info to create an object of CreativeWallpaperInfo
408      * @return an object of type CreativeWallpaperInfo
409      */
410     @NonNull
411     public static CreativeWallpaperInfo buildFromCursor(
412             android.app.WallpaperInfo wallpaperInfo, Cursor cursor) {
413         String wallpaperTitle = cursor.getString(
414                 cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_TITLE));
415         String wallpaperAuthor = null;
416         if (cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_AUTHOR) >= 0) {
417             wallpaperAuthor = cursor.getString(
418                     cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_AUTHOR));
419         }
420         String wallpaperDescription = null;
421         if (cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_DESCRIPTION) >= 0) {
422             wallpaperDescription = cursor.getString(
423                     cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_DESCRIPTION));
424         }
425         String wallpaperContentDescription = null;
426         int wallpaperContentDescriptionIndex = cursor.getColumnIndex(
427                 WallpaperInfoContract.WALLPAPER_CONTENT_DESCRIPTION);
428         if (wallpaperContentDescriptionIndex >= 0) {
429             wallpaperContentDescription = cursor.getString(
430                     wallpaperContentDescriptionIndex);
431         }
432         Uri thumbnailUri = Uri.parse(cursor.getString(cursor.getColumnIndex(
433                 WallpaperInfoContract.WALLPAPER_THUMBNAIL)));
434         Uri configPreviewUri = Uri.parse(cursor.getString(cursor.getColumnIndex(
435                 WallpaperInfoContract.WALLPAPER_CONFIG_PREVIEW_URI)));
436         Uri cleanPreviewUri = Uri.parse(cursor.getString(cursor.getColumnIndex(
437                 WallpaperInfoContract.WALLPAPER_CLEAN_PREVIEW_URI)));
438         Uri deleteUri = Uri.parse(cursor.getString(
439                 cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_DELETE_URI)));
440         Uri shareUri = Uri.parse(cursor.getString(cursor.getColumnIndex(
441                 WallpaperInfoContract.WALLPAPER_SHARE_URI)));
442         String groupName = cursor.getString(
443                 cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_GROUP_NAME));
444         int isCurrentApplied = cursor.getInt(
445                 cursor.getColumnIndex(WallpaperInfoContract.WALLPAPER_IS_APPLIED));
446 
447         return new CreativeWallpaperInfo(wallpaperInfo, wallpaperTitle, wallpaperAuthor,
448                 wallpaperDescription, wallpaperContentDescription, configPreviewUri,
449                 cleanPreviewUri, deleteUri, thumbnailUri, shareUri, groupName, /* isCurrent= */
450                 (isCurrentApplied == 1));
451     }
452 
453     /**
454      * Saves a wallpaper of type of CreativeWallpaperInfo for a particular destination.
455      * @param context context of the calling activity
456      * @param destination depicts the destination of the wallpaper being saved
457      * @return CreativeWallpaperInfo object that has been saved
458      */
459     @Override
460     public CreativeWallpaperInfo saveWallpaper(Context context, int destination) {
461         if (context == null) {
462             Log.w(TAG, "Context is null!!");
463             return null;
464         }
465 
466         Uri saveWallpaperUri = getSaveWallpaperUriForCreativeWallpaper();
467         if (saveWallpaperUri == null) {
468             Log.w(TAG, "Missing save wallpaper uri in  " + this.getWallpaperComponent()
469                     .getServiceName());
470             return null;
471         }
472         return CreativeCategory.saveCreativeCategoryWallpaper(
473                 context, this, saveWallpaperUri, destination);
474     }
475 }
476