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