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