1 /* 2 * Copyright (C) 2017 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.module; 17 18 import android.annotation.SuppressLint; 19 import android.app.WallpaperManager; 20 import android.content.Context; 21 import android.graphics.Bitmap; 22 import android.graphics.BitmapFactory; 23 import android.graphics.drawable.BitmapDrawable; 24 import android.os.AsyncTask; 25 import android.os.ParcelFileDescriptor; 26 import android.util.Log; 27 28 import com.android.wallpaper.R; 29 import com.android.wallpaper.asset.BitmapUtils; 30 import com.android.wallpaper.compat.BuildCompat; 31 import com.android.wallpaper.compat.WallpaperManagerCompat; 32 import com.android.wallpaper.model.WallpaperMetadata; 33 34 import java.io.FileInputStream; 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.List; 40 41 /** 42 * Default implementation of {@link WallpaperRefresher} which refreshes wallpaper metadata 43 * asynchronously. 44 */ 45 @SuppressLint("ServiceCast") 46 public class DefaultWallpaperRefresher implements WallpaperRefresher { 47 private static final String TAG = "DefaultWPRefresher"; 48 49 private final Context mAppContext; 50 private final WallpaperPreferences mWallpaperPreferences; 51 private final WallpaperManager mWallpaperManager; 52 private final WallpaperStatusChecker mWallpaperStatusChecker; 53 54 /** 55 * @param context The application's context. 56 */ DefaultWallpaperRefresher(Context context)57 public DefaultWallpaperRefresher(Context context) { 58 mAppContext = context.getApplicationContext(); 59 60 Injector injector = InjectorProvider.getInjector(); 61 mWallpaperPreferences = injector.getPreferences(mAppContext); 62 mWallpaperStatusChecker = injector.getWallpaperStatusChecker(); 63 64 // Retrieve WallpaperManager using Context#getSystemService instead of 65 // WallpaperManager#getInstance so it can be mocked out in test. 66 mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE); 67 } 68 69 @Override refresh(RefreshListener listener)70 public void refresh(RefreshListener listener) { 71 GetWallpaperMetadataAsyncTask task = new GetWallpaperMetadataAsyncTask(listener); 72 task.execute(); 73 } 74 75 /** 76 * Retrieves the current wallpaper's thumbnail and metadata off the UI thread. 77 */ 78 private class GetWallpaperMetadataAsyncTask extends 79 AsyncTask<Void, Void, List<WallpaperMetadata>> { 80 private final RefreshListener mListener; 81 private final WallpaperManagerCompat mWallpaperManagerCompat; 82 83 private long mCurrentHomeWallpaperHashCode; 84 private long mCurrentLockWallpaperHashCode; 85 private String mSystemWallpaperPackageName; 86 87 @SuppressLint("ServiceCast") GetWallpaperMetadataAsyncTask(RefreshListener listener)88 public GetWallpaperMetadataAsyncTask(RefreshListener listener) { 89 mListener = listener; 90 mWallpaperManagerCompat = 91 InjectorProvider.getInjector().getWallpaperManagerCompat(mAppContext); 92 } 93 94 @Override doInBackground(Void... unused)95 protected List<WallpaperMetadata> doInBackground(Void... unused) { 96 List<WallpaperMetadata> wallpaperMetadatas = new ArrayList<>(); 97 98 if (!isHomeScreenMetadataCurrent() || isHomeScreenAttributionsEmpty()) { 99 mWallpaperPreferences.clearHomeWallpaperMetadata(); 100 setFallbackHomeScreenWallpaperMetadata(); 101 } 102 103 boolean isLockScreenWallpaperCurrentlySet = mWallpaperStatusChecker.isLockWallpaperSet( 104 mAppContext); 105 106 if (!BuildCompat.isAtLeastN() || !isLockScreenWallpaperCurrentlySet) { 107 // Return only home metadata if pre-N device or lock screen wallpaper is not explicitly set. 108 wallpaperMetadatas.add(new WallpaperMetadata( 109 mWallpaperPreferences.getHomeWallpaperAttributions(), 110 mWallpaperPreferences.getHomeWallpaperActionUrl(), 111 mWallpaperPreferences.getHomeWallpaperActionLabelRes(), 112 mWallpaperPreferences.getHomeWallpaperActionIconRes(), 113 mWallpaperPreferences.getHomeWallpaperCollectionId(), 114 mWallpaperPreferences.getHomeWallpaperBackingFileName(), 115 mWallpaperManager.getWallpaperInfo())); 116 return wallpaperMetadatas; 117 } 118 119 if (!isLockScreenMetadataCurrent() || isLockScreenAttributionsEmpty()) { 120 mWallpaperPreferences.clearLockWallpaperMetadata(); 121 setFallbackLockScreenWallpaperMetadata(); 122 } 123 124 wallpaperMetadatas.add(new WallpaperMetadata( 125 mWallpaperPreferences.getHomeWallpaperAttributions(), 126 mWallpaperPreferences.getHomeWallpaperActionUrl(), 127 mWallpaperPreferences.getHomeWallpaperActionLabelRes(), 128 mWallpaperPreferences.getHomeWallpaperActionIconRes(), 129 mWallpaperPreferences.getHomeWallpaperCollectionId(), 130 mWallpaperPreferences.getHomeWallpaperBackingFileName(), 131 mWallpaperManager.getWallpaperInfo())); 132 133 wallpaperMetadatas.add(new WallpaperMetadata( 134 mWallpaperPreferences.getLockWallpaperAttributions(), 135 mWallpaperPreferences.getLockWallpaperActionUrl(), 136 mWallpaperPreferences.getLockWallpaperActionLabelRes(), 137 mWallpaperPreferences.getLockWallpaperActionIconRes(), 138 mWallpaperPreferences.getLockWallpaperCollectionId(), 139 mWallpaperPreferences.getLockWallpaperBackingFileName(), 140 null /* wallpaperComponent */)); 141 142 return wallpaperMetadatas; 143 } 144 145 @Override onPostExecute(List<WallpaperMetadata> metadatas)146 protected void onPostExecute(List<WallpaperMetadata> metadatas) { 147 if (metadatas.size() > 2) { 148 Log.e(TAG, "Got more than 2 WallpaperMetadata objects - only home and (optionally) lock " 149 + "are permitted."); 150 return; 151 } 152 153 mListener.onRefreshed(metadatas.get(0), metadatas.size() > 1 ? metadatas.get(1) : null, 154 mWallpaperPreferences.getWallpaperPresentationMode()); 155 } 156 157 /** 158 * Sets fallback wallpaper attributions to WallpaperPreferences when the saved metadata did not 159 * match the system wallpaper. For live wallpapers, loads the label (title) but for image 160 * wallpapers loads a generic title string. 161 */ setFallbackHomeScreenWallpaperMetadata()162 private void setFallbackHomeScreenWallpaperMetadata() { 163 android.app.WallpaperInfo wallpaperComponent = mWallpaperManager.getWallpaperInfo(); 164 if (wallpaperComponent == null) { // Image wallpaper 165 mWallpaperPreferences.setHomeWallpaperAttributions( 166 Arrays.asList(mAppContext.getResources().getString(R.string.fallback_wallpaper_title))); 167 168 // Set wallpaper ID if at least N or set a hash code if an earlier version of Android. 169 if (BuildCompat.isAtLeastN()) { 170 mWallpaperPreferences.setHomeWallpaperManagerId(mWallpaperManagerCompat.getWallpaperId( 171 WallpaperManagerCompat.FLAG_SYSTEM)); 172 } else { 173 mWallpaperPreferences.setHomeWallpaperHashCode(getCurrentHomeWallpaperHashCode()); 174 } 175 } else { // Live wallpaper 176 mWallpaperPreferences.setHomeWallpaperAttributions(Arrays.asList( 177 wallpaperComponent.loadLabel(mAppContext.getPackageManager()).toString())); 178 mWallpaperPreferences.setHomeWallpaperPackageName(mSystemWallpaperPackageName); 179 } 180 mWallpaperPreferences.setWallpaperPresentationMode( 181 WallpaperPreferences.PRESENTATION_MODE_STATIC); 182 } 183 184 /** 185 * Sets fallback lock screen wallpaper attributions to WallpaperPreferences. This should be 186 * called when the saved lock screen wallpaper metadata does not match the currently set lock 187 * screen wallpaper. 188 */ setFallbackLockScreenWallpaperMetadata()189 private void setFallbackLockScreenWallpaperMetadata() { 190 mWallpaperPreferences.setLockWallpaperAttributions( 191 Arrays.asList(mAppContext.getResources().getString(R.string.fallback_wallpaper_title))); 192 mWallpaperPreferences.setLockWallpaperId(mWallpaperManagerCompat.getWallpaperId( 193 WallpaperManagerCompat.FLAG_LOCK)); 194 } 195 196 /** 197 * Returns whether the home screen metadata saved in WallpaperPreferences corresponds to the 198 * current system wallpaper. 199 */ isHomeScreenMetadataCurrent()200 private boolean isHomeScreenMetadataCurrent() { 201 return (mWallpaperManager.getWallpaperInfo() == null) 202 ? isHomeScreenImageWallpaperCurrent() 203 : isHomeScreenLiveWallpaperCurrent(); 204 } 205 206 /** 207 * Returns whether the home screen attributions saved in WallpaperPreferences is empty. 208 */ isHomeScreenAttributionsEmpty()209 private boolean isHomeScreenAttributionsEmpty() { 210 List<String> homeScreenAttributions = mWallpaperPreferences.getHomeWallpaperAttributions(); 211 return homeScreenAttributions.get(0) == null 212 && homeScreenAttributions.get(1) == null 213 && homeScreenAttributions.get(2) == null; 214 } 215 getCurrentHomeWallpaperHashCode()216 private long getCurrentHomeWallpaperHashCode() { 217 if (mCurrentHomeWallpaperHashCode == 0) { 218 BitmapDrawable wallpaperDrawable = (BitmapDrawable) mWallpaperManagerCompat.getDrawable(); 219 Bitmap wallpaperBitmap = wallpaperDrawable.getBitmap(); 220 mCurrentHomeWallpaperHashCode = BitmapUtils.generateHashCode(wallpaperBitmap); 221 222 // Manually request that WallpaperManager loses its reference to the current wallpaper 223 // bitmap, which can occupy a large memory allocation for the lifetime of the app. 224 mWallpaperManager.forgetLoadedWallpaper(); 225 } 226 return mCurrentHomeWallpaperHashCode; 227 } 228 getCurrentLockWallpaperHashCode()229 private long getCurrentLockWallpaperHashCode() { 230 if (mCurrentLockWallpaperHashCode == 0 231 && mWallpaperStatusChecker.isLockWallpaperSet(mAppContext)) { 232 Bitmap wallpaperBitmap = getLockWallpaperBitmap(); 233 mCurrentLockWallpaperHashCode = BitmapUtils.generateHashCode(wallpaperBitmap); 234 } 235 return mCurrentLockWallpaperHashCode; 236 } 237 238 /** 239 * Returns the lock screen wallpaper currently set on the device as a Bitmap, or null if no 240 * lock screen wallpaper is set. 241 */ getLockWallpaperBitmap()242 private Bitmap getLockWallpaperBitmap() { 243 Bitmap lockBitmap = null; 244 245 ParcelFileDescriptor pfd = mWallpaperManagerCompat.getWallpaperFile( 246 WallpaperManagerCompat.FLAG_LOCK); 247 // getWallpaperFile returns null if the lock screen isn't explicitly set, so need this 248 // check. 249 if (pfd != null) { 250 InputStream fileStream = null; 251 try { 252 fileStream = new FileInputStream(pfd.getFileDescriptor()); 253 lockBitmap = BitmapFactory.decodeStream(fileStream); 254 pfd.close(); 255 return lockBitmap; 256 } catch (IOException e) { 257 Log.e(TAG, "IO exception when closing the file descriptor."); 258 } finally { 259 if (fileStream != null) { 260 try { 261 fileStream.close(); 262 } catch (IOException e) { 263 Log.e(TAG, "IO exception when closing input stream for lock screen WP."); 264 } 265 } 266 } 267 } 268 269 return lockBitmap; 270 } 271 272 /** 273 * Returns whether the image wallpaper set to the system matches the metadata in 274 * WallpaperPreferences. 275 */ isHomeScreenImageWallpaperCurrent()276 private boolean isHomeScreenImageWallpaperCurrent() { 277 long savedBitmapHash = mWallpaperPreferences.getHomeWallpaperHashCode(); 278 279 // Use WallpaperManager IDs to check same-ness of image wallpaper on N+ versions of Android 280 // only when there is no saved bitmap hash code (which could be leftover from a previous build 281 // of the app that did not use wallpaper IDs). 282 if (BuildCompat.isAtLeastN() && savedBitmapHash == 0) { 283 return mWallpaperPreferences.getHomeWallpaperManagerId() 284 == mWallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_SYSTEM); 285 } 286 287 return savedBitmapHash == getCurrentHomeWallpaperHashCode(); 288 } 289 290 /** 291 * Returns whether the live wallpaper set to the system's home screen matches the metadata in 292 * WallpaperPreferences. 293 */ isHomeScreenLiveWallpaperCurrent()294 private boolean isHomeScreenLiveWallpaperCurrent() { 295 mSystemWallpaperPackageName = mWallpaperManager.getWallpaperInfo().getPackageName(); 296 String homeWallpaperPackageName = mWallpaperPreferences.getHomeWallpaperPackageName(); 297 return mSystemWallpaperPackageName.equals(homeWallpaperPackageName); 298 } 299 300 /** 301 * Returns whether the lock screen metadata saved in WallpaperPreferences corresponds to the 302 * current lock screen wallpaper. 303 */ isLockScreenMetadataCurrent()304 private boolean isLockScreenMetadataCurrent() { 305 // Check for lock wallpaper image same-ness only when there is no stored lock wallpaper hash 306 // code. Otherwise if there is a lock wallpaper hash code stored in 307 // {@link WallpaperPreferences}, then check hash codes. 308 long savedLockWallpaperHash = mWallpaperPreferences.getLockWallpaperHashCode(); 309 310 return (savedLockWallpaperHash == 0) 311 ? mWallpaperPreferences.getLockWallpaperId() 312 == mWallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_LOCK) 313 : savedLockWallpaperHash == getCurrentLockWallpaperHashCode(); 314 } 315 316 /** 317 * Returns whether the lock screen attributions saved in WallpaperPreferences are empty. 318 */ isLockScreenAttributionsEmpty()319 private boolean isLockScreenAttributionsEmpty() { 320 List<String> attributions = mWallpaperPreferences.getLockWallpaperAttributions(); 321 return attributions.get(0) == null 322 && attributions.get(1) == null 323 && attributions.get(2) == null; 324 } 325 } 326 } 327