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