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.asset; 17 18 import android.app.Activity; 19 import android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.content.res.AssetFileDescriptor; 22 import android.graphics.Bitmap; 23 import android.graphics.BitmapFactory; 24 import android.graphics.Rect; 25 import android.graphics.drawable.BitmapDrawable; 26 import android.graphics.drawable.ColorDrawable; 27 import android.graphics.drawable.Drawable; 28 import android.net.Uri; 29 import android.os.AsyncTask; 30 import android.util.Log; 31 import android.widget.ImageView; 32 33 import com.bumptech.glide.Glide; 34 import com.bumptech.glide.load.Key; 35 import com.bumptech.glide.load.engine.DiskCacheStrategy; 36 import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; 37 import com.bumptech.glide.request.RequestOptions; 38 39 import java.io.IOException; 40 import java.security.MessageDigest; 41 42 /** 43 * Asset wrapping a drawable for a live wallpaper thumbnail. 44 */ 45 public class LiveWallpaperThumbAsset extends Asset { 46 private static final String TAG = "LiveWallpaperThumbAsset"; 47 48 protected final Context mContext; 49 protected final android.app.WallpaperInfo mInfo; 50 // The content Uri of thumbnail 51 protected Uri mUri; 52 private BitmapDrawable mThumbnailDrawable; 53 LiveWallpaperThumbAsset(Context context, android.app.WallpaperInfo info)54 public LiveWallpaperThumbAsset(Context context, android.app.WallpaperInfo info) { 55 mContext = context.getApplicationContext(); 56 mInfo = info; 57 } 58 LiveWallpaperThumbAsset(Context context, android.app.WallpaperInfo info, Uri uri)59 public LiveWallpaperThumbAsset(Context context, android.app.WallpaperInfo info, Uri uri) { 60 this(context, info); 61 mUri = uri; 62 } 63 64 @Override decodeBitmap(int targetWidth, int targetHeight, BitmapReceiver receiver)65 public void decodeBitmap(int targetWidth, int targetHeight, 66 BitmapReceiver receiver) { 67 // No scaling is needed, as the thumbnail is already a thumbnail. 68 LoadThumbnailTask task = new LoadThumbnailTask(mContext, mInfo, receiver); 69 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 70 } 71 72 @Override decodeBitmapRegion(Rect rect, int targetWidth, int targetHeight, BitmapReceiver receiver)73 public void decodeBitmapRegion(Rect rect, int targetWidth, int targetHeight, 74 BitmapReceiver receiver) { 75 receiver.onBitmapDecoded(null); 76 } 77 78 @Override decodeRawDimensions(Activity unused, DimensionsReceiver receiver)79 public void decodeRawDimensions(Activity unused, DimensionsReceiver receiver) { 80 receiver.onDimensionsDecoded(null); 81 } 82 83 @Override supportsTiling()84 public boolean supportsTiling() { 85 return false; 86 } 87 88 @Override loadDrawable(Context context, ImageView imageView, int placeholderColor)89 public void loadDrawable(Context context, ImageView imageView, 90 int placeholderColor) { 91 RequestOptions reqOptions; 92 if (mUri != null) { 93 reqOptions = RequestOptions.centerCropTransform().apply(RequestOptions 94 .diskCacheStrategyOf(DiskCacheStrategy.NONE) 95 .skipMemoryCache(true)) 96 .placeholder(new ColorDrawable(placeholderColor)); 97 } else { 98 reqOptions = RequestOptions.centerCropTransform() 99 .placeholder(new ColorDrawable(placeholderColor)); 100 } 101 Glide.with(context) 102 .asDrawable() 103 .load(LiveWallpaperThumbAsset.this) 104 .apply(reqOptions) 105 .transition(DrawableTransitionOptions.withCrossFade()) 106 .into(imageView); 107 } 108 109 /** 110 * Returns a Glide cache key. 111 */ getKey()112 Key getKey() { 113 return new LiveWallpaperThumbKey(mInfo); 114 } 115 116 /** 117 * Returns the thumbnail drawable for the live wallpaper synchronously. Should not be called on 118 * the main UI thread. 119 */ getThumbnailDrawable()120 protected Drawable getThumbnailDrawable() { 121 if (mUri != null) { 122 if (mThumbnailDrawable != null) { 123 return mThumbnailDrawable; 124 } 125 try { 126 AssetFileDescriptor assetFileDescriptor = 127 mContext.getContentResolver().openAssetFileDescriptor(mUri, "r"); 128 if (assetFileDescriptor != null) { 129 mThumbnailDrawable = new BitmapDrawable(mContext.getResources(), 130 BitmapFactory.decodeStream(assetFileDescriptor.createInputStream())); 131 return mThumbnailDrawable; 132 } 133 } catch (IOException e) { 134 Log.w(TAG, "Not found thumbnail from URI."); 135 } 136 } 137 return mInfo.loadThumbnail(mContext.getPackageManager()); 138 } 139 140 /** 141 * Glide caching key for resources from any arbitrary package. 142 */ 143 private static final class LiveWallpaperThumbKey implements Key { 144 private android.app.WallpaperInfo mInfo; 145 LiveWallpaperThumbKey(android.app.WallpaperInfo info)146 public LiveWallpaperThumbKey(android.app.WallpaperInfo info) { 147 mInfo = info; 148 } 149 150 @Override toString()151 public String toString() { 152 return getCacheKey(); 153 } 154 155 @Override hashCode()156 public int hashCode() { 157 return getCacheKey().hashCode(); 158 } 159 160 @Override equals(Object object)161 public boolean equals(Object object) { 162 if (!(object instanceof LiveWallpaperThumbKey)) { 163 return false; 164 } 165 166 LiveWallpaperThumbKey otherKey = (LiveWallpaperThumbKey) object; 167 return getCacheKey().equals(otherKey.getCacheKey()); 168 } 169 170 @Override updateDiskCacheKey(MessageDigest messageDigest)171 public void updateDiskCacheKey(MessageDigest messageDigest) { 172 messageDigest.update(getCacheKey().getBytes(CHARSET)); 173 } 174 175 /** 176 * Returns an inexpensively calculated {@link String} suitable for use as a disk cache key, 177 * based on the live wallpaper's package name and service name, which is enough to uniquely 178 * identify a live wallpaper. 179 */ getCacheKey()180 private String getCacheKey() { 181 return "LiveWallpaperThumbKey{" 182 + "packageName=" + mInfo.getPackageName() + "," 183 + "serviceName=" + mInfo.getServiceName() 184 + '}'; 185 } 186 } 187 188 /** 189 * AsyncTask subclass which loads the live wallpaper's thumbnail bitmap off the main UI thread. 190 * Resolves with null if live wallpaper thumbnail is not a bitmap. 191 */ 192 private static class LoadThumbnailTask extends AsyncTask<Void, Void, Bitmap> { 193 private final PackageManager mPackageManager; 194 private android.app.WallpaperInfo mInfo; 195 private BitmapReceiver mReceiver; 196 LoadThumbnailTask(Context context, android.app.WallpaperInfo info, BitmapReceiver receiver)197 public LoadThumbnailTask(Context context, android.app.WallpaperInfo info, 198 BitmapReceiver receiver) { 199 mInfo = info; 200 mReceiver = receiver; 201 mPackageManager = context.getPackageManager(); 202 } 203 204 @Override doInBackground(Void... unused)205 protected Bitmap doInBackground(Void... unused) { 206 Drawable thumb = mInfo.loadThumbnail(mPackageManager); 207 208 // Live wallpaper components may or may not specify a thumbnail drawable. 209 if (thumb != null && thumb instanceof BitmapDrawable) { 210 return ((BitmapDrawable) thumb).getBitmap(); 211 } 212 213 // If no thumbnail was specified, return a null bitmap. 214 return null; 215 } 216 217 @Override onPostExecute(Bitmap bitmap)218 protected void onPostExecute(Bitmap bitmap) { 219 mReceiver.onBitmapDecoded(bitmap); 220 } 221 } 222 } 223