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