1 /* 2 * Copyright (C) 2023 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 19 import static com.android.wallpaper.model.WallpaperInfoContract.WALLPAPER_EFFECTS_CLEAR_URI; 20 import static com.android.wallpaper.model.WallpaperInfoContract.WALLPAPER_EFFECTS_CURRENT_ID; 21 import static com.android.wallpaper.model.WallpaperInfoContract.WALLPAPER_EFFECTS_SECTION_SUBTITLE; 22 import static com.android.wallpaper.model.WallpaperInfoContract.WALLPAPER_EFFECTS_SECTION_TITLE; 23 24 import android.annotation.Nullable; 25 import android.content.ContentProviderClient; 26 import android.content.Context; 27 import android.database.Cursor; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.text.TextUtils; 31 import android.util.Log; 32 33 import com.android.wallpaper.asset.CreativeWallpaperThumbAsset; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 38 /** The {@link WallpaperCategory} implements category for user created wallpapers. */ 39 public class CreativeCategory extends WallpaperCategory { 40 41 private static final String TAG = "CreativeCategory"; 42 public static final String KEY_WALLPAPER_CREATIVE_CATEGORY = 43 "android.service.wallpaper.category"; 44 45 public static final String KEY_WALLPAPER_CREATIVE_WALLPAPERS = 46 "android.service.wallpaper.categorizedwallpapers"; 47 public static final String KEY_WALLPAPER_CREATIVE_WALLPAPER_EFFECTS = 48 "android.service.wallpaper.effects"; 49 50 public final android.app.WallpaperInfo mWallpaperInfo; 51 52 public static final String KEY_WALLPAPER_SAVE_CREATIVE_CATEGORY_WALLPAPER = 53 "android.service.wallpaper.savewallpaper"; 54 public static final String KEY_WALLPAPER_SAVE_CREATIVE_WALLPAPER_CURRENT = 55 "android.service.wallpaper.currentwallpapers"; 56 57 /** Return true for CreativeCategories since we support user generated wallpapers here. */ 58 @Override supportsUserCreatedWallpapers()59 public boolean supportsUserCreatedWallpapers() { 60 return true; 61 } 62 63 /** 64 * This function aims at saving a creative category wallpaper by calling the relevant URI 65 * of the creative category wallpaper provider. 66 * 67 * @param context Context of calling activity 68 * @param wallpaper instance of the WallpaperInfo that we want to save 69 * @param saveWallpaperUri the URI to be used for saving the wallpaper 70 * @param destination depicts the destination of the wallpaper being saved 71 */ 72 @Nullable saveCreativeCategoryWallpaper(Context context, LiveWallpaperInfo wallpaper, Uri saveWallpaperUri, int destination)73 public static CreativeWallpaperInfo saveCreativeCategoryWallpaper(Context context, 74 LiveWallpaperInfo wallpaper, Uri saveWallpaperUri, int destination) { 75 76 // Adding destination field which depicts whether it is home-screen or lock-screen. 77 Uri updatedSaveWallpaperUri = saveWallpaperUri.buildUpon() 78 .appendQueryParameter("destination", String.valueOf(destination)) 79 .build(); 80 try (ContentProviderClient client = 81 context.getContentResolver().acquireContentProviderClient( 82 updatedSaveWallpaperUri.getAuthority())) { 83 if (client == null) { 84 Log.w(TAG, "Couldn't resolve content provider for " + updatedSaveWallpaperUri); 85 return null; 86 } 87 try (Cursor cursor = client.query(updatedSaveWallpaperUri, /* projection= */ null, 88 /* selection= */ null, /* selectionArgs= */ null, /* sortOrder= */ null)) { 89 if (cursor == null || !cursor.moveToFirst()) { 90 return null; 91 } 92 return CreativeWallpaperInfo.buildFromCursor(wallpaper.getWallpaperComponent(), 93 cursor); 94 } catch (Throwable e) { 95 Log.e(TAG, "Couldn't read creative category.", e); 96 } 97 } 98 return null; 99 } 100 CreativeCategory(Context context, String title, String collectionId, Uri thumbUri, List<WallpaperInfo> wallpaperInfos, int priority, android.app.WallpaperInfo wallpaperInfo)101 public CreativeCategory(Context context, String title, String collectionId, Uri thumbUri, 102 List<WallpaperInfo> wallpaperInfos, int priority, 103 android.app.WallpaperInfo wallpaperInfo) { 104 super(title, 105 collectionId, 106 wallpaperInfos.isEmpty() ? null : new CreativeWallpaperThumbAsset(context, 107 wallpaperInfos.get(0).getWallpaperComponent(), thumbUri), 108 wallpaperInfos, 109 priority); 110 mWallpaperInfo = wallpaperInfo; 111 } 112 113 @Override supportsWallpaperSetUpdates()114 public boolean supportsWallpaperSetUpdates() { 115 return true; 116 } 117 118 @Override fetchWallpapers(Context context, WallpaperReceiver receiver, boolean forceReload)119 public void fetchWallpapers(Context context, WallpaperReceiver receiver, boolean forceReload) { 120 if (!forceReload) { 121 super.fetchWallpapers(context, receiver, forceReload); 122 return; 123 } 124 List<WallpaperInfo> wallpapers = readCreativeWallpapers( 125 context, getCollectionId(), mWallpaperInfo); 126 synchronized (this) { 127 getMutableWallpapers().clear(); 128 getMutableWallpapers().addAll(wallpapers); 129 } 130 if (receiver != null) { 131 receiver.onWallpapersReceived(wallpapers); 132 } 133 } 134 135 /** 136 * Returns a list of [CreativeWallpaperInfo] objects by creating them using the relevant 137 * info. obtained from creative-category APK on device. 138 * 139 * @param context context of the hosting activity 140 * @param collectionId ID of the collection to which these wallpapers belong to 141 * @param wallpaperInfo contains relevant metadata information about creative-category wallpaper 142 * @return list of CreativeWallpaperInfo objects 143 */ readCreativeWallpapers(Context context, String collectionId, android.app.WallpaperInfo wallpaperInfo)144 public static List<WallpaperInfo> readCreativeWallpapers(Context context, 145 String collectionId, android.app.WallpaperInfo wallpaperInfo) { 146 List<WallpaperInfo> wallpapers = new ArrayList<>(); 147 Bundle metaData = wallpaperInfo.getServiceInfo().metaData; 148 Uri wallpapersUri = Uri.parse((String) metaData.get(KEY_WALLPAPER_CREATIVE_WALLPAPERS)); 149 try (ContentProviderClient client = 150 context.getContentResolver().acquireContentProviderClient( 151 wallpapersUri.getAuthority())) { 152 try (Cursor cursor = client.query(wallpapersUri, /* projection= */ null, 153 /* selection= */ null, /* selectionArgs= */ null, /* sortOrder= */ null)) { 154 if (cursor == null || !cursor.moveToFirst()) { 155 return wallpapers; 156 } 157 do { 158 String categoryId = cursor.getString( 159 cursor.getColumnIndex(WallpaperInfoContract.CATEGORY_ID)); 160 if (!TextUtils.equals(categoryId, collectionId)) { 161 continue; 162 } 163 CreativeWallpaperInfo creativeWallpaperInfo = 164 CreativeWallpaperInfo.buildFromCursor(wallpaperInfo, cursor); 165 // If the meta data for wallpaper actions exists, only then can we query the 166 // action fields and action table. 167 if (metaData.get(KEY_WALLPAPER_CREATIVE_WALLPAPER_EFFECTS) != null) { 168 String effectsBottomSheetTitle = cursor.getString( 169 cursor.getColumnIndex(WALLPAPER_EFFECTS_SECTION_TITLE)); 170 String effectsBottomSheetSubtitle = cursor.getString( 171 cursor.getColumnIndex(WALLPAPER_EFFECTS_SECTION_SUBTITLE)); 172 String currentEffectId = cursor.getString( 173 cursor.getColumnIndex(WALLPAPER_EFFECTS_CURRENT_ID)); 174 Uri clearActionUri = Uri.parse(cursor.getString( 175 cursor.getColumnIndex(WALLPAPER_EFFECTS_CLEAR_URI))); 176 177 creativeWallpaperInfo.setEffectsBottomSheetTitle( 178 effectsBottomSheetTitle); 179 creativeWallpaperInfo.setEffectsBottomSheetSubtitle( 180 effectsBottomSheetSubtitle); 181 creativeWallpaperInfo.setClearActionsUri(clearActionUri); 182 creativeWallpaperInfo.setCurrentlyAppliedEffectId(currentEffectId); 183 184 Uri effectsUri = Uri.parse((String) metaData.get( 185 KEY_WALLPAPER_CREATIVE_WALLPAPER_EFFECTS)); 186 creativeWallpaperInfo.setEffectsUri(effectsUri); 187 } 188 wallpapers.add(creativeWallpaperInfo); 189 } while (cursor.moveToNext()); 190 } 191 } catch (Throwable e) { 192 Log.e(TAG, "Exception reading creative wallpapers", e); 193 } 194 return wallpapers; 195 } 196 197 @Override supportsThirdParty()198 public boolean supportsThirdParty() { 199 return true; 200 } 201 202 @Override isSingleWallpaperCategory()203 public boolean isSingleWallpaperCategory() { 204 // if the category is empty then we go directly to the creation screen 205 // if there is at least one wallpaper then we show the collection of wallpapers 206 // NOTE: don't count the "add walpapper button" wallpaper. This is why <= 1 is being used 207 // as opposed to <= 0 208 return getMutableWallpapers() == null || getMutableWallpapers().size() <= 1; 209 } 210 } 211