• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 
17 package com.example.android.leanback;
18 
19 import android.app.Activity;
20 import android.graphics.Bitmap;
21 import android.graphics.drawable.BitmapDrawable;
22 import android.graphics.drawable.Drawable;
23 import android.os.AsyncTask;
24 import android.os.Handler;
25 import android.support.v17.leanback.app.BackgroundManager;
26 import android.support.v4.content.ContextCompat;
27 import android.util.Log;
28 import android.view.View;
29 
30 /**
31  * App uses BackgroundHelper for each Activity, it wraps BackgroundManager and provides:
32  * 1. AsyncTask to load bitmap in background thread.
33  * 2. Using a BitmapCache to cache loaded bitmaps.
34  */
35 public class BackgroundHelper {
36 
37     private static final String TAG = "BackgroundHelper";
38     private static final boolean DEBUG = false;
39     private static final boolean ENABLED = true;
40 
41     // Background delay serves to avoid kicking off expensive bitmap loading
42     // in case multiple backgrounds are set in quick succession.
43     private static final int SET_BACKGROUND_DELAY_MS = 100;
44 
45     /**
46      * An very simple example of BitmapCache.
47      */
48     public static class BitmapCache {
49         Bitmap mLastBitmap;
50         Object mLastToken;
51 
52         // Singleton BitmapCache shared by multiple activities/backgroundHelper.
53         static BitmapCache sInstance = new BitmapCache();
54 
BitmapCache()55         private BitmapCache() {
56         }
57 
58         /**
59          * Get cached bitmap by token, returns null if missing cache.
60          */
getCache(Object token)61         public Bitmap getCache(Object token) {
62             if (token == null ? mLastToken == null : token.equals(mLastToken)) {
63                 if (DEBUG) Log.v(TAG, "hitCache token:" + token + " " + mLastBitmap);
64                 return mLastBitmap;
65             }
66             return null;
67         }
68 
69         /**
70          * Add cached bitmap.
71          */
putCache(Object token, Bitmap bitmap)72         public void putCache(Object token, Bitmap bitmap) {
73             if (DEBUG) Log.v(TAG, "putCache token:" + token + " " + bitmap);
74             mLastToken = token;
75             mLastBitmap = bitmap;
76         }
77 
78         /**
79          * Add singleton of BitmapCache shared across activities.
80          */
getInstance()81         public static BitmapCache getInstance() {
82             return sInstance;
83         }
84     }
85 
86     /**
87      * Callback class to perform task after bitmap is loaded.
88      */
89     public abstract static class BitmapLoadCallback {
90         /**
91          * Called when Bitmap is loaded.
92          */
onBitmapLoaded(Bitmap bitmap)93         public abstract void onBitmapLoaded(Bitmap bitmap);
94     }
95 
96     static class Request {
97         Object mImageToken;
98         Bitmap mResult;
99 
Request(Object imageToken)100         Request(Object imageToken) {
101             mImageToken = imageToken;
102         }
103     }
104 
BackgroundHelper(Activity activity)105     public BackgroundHelper(Activity activity) {
106         if (DEBUG && !ENABLED) Log.v(TAG, "BackgroundHelper: disabled");
107         mActivity = activity;
108     }
109 
110     class LoadBackgroundRunnable implements Runnable {
111         Request mRequest;
112 
LoadBackgroundRunnable(Object imageToken)113         LoadBackgroundRunnable(Object imageToken) {
114             mRequest = new Request(imageToken);
115         }
116 
117         @Override
run()118         public void run() {
119             if (DEBUG) Log.v(TAG, "Executing task");
120             new LoadBitmapIntoBackgroundManagerTask().execute(mRequest);
121             mRunnable = null;
122         }
123     }
124 
125     class LoadBitmapTaskBase extends AsyncTask<Request, Object, Request> {
126         @Override
doInBackground(Request... params)127         protected Request doInBackground(Request... params) {
128             boolean cancelled = isCancelled();
129             if (DEBUG) Log.v(TAG, "doInBackground cancelled " + cancelled);
130             Request request = params[0];
131             if (!cancelled) {
132                 request.mResult = loadBitmap(request.mImageToken);
133             }
134             return request;
135         }
136 
137         @Override
onPostExecute(Request request)138         protected void onPostExecute(Request request) {
139             if (DEBUG) Log.v(TAG, "onPostExecute");
140             BitmapCache.getInstance().putCache(request.mImageToken, request.mResult);
141         }
142 
143         @Override
onCancelled(Request request)144         protected void onCancelled(Request request) {
145             if (DEBUG) Log.v(TAG, "onCancelled");
146         }
147 
loadBitmap(Object imageToken)148         private Bitmap loadBitmap(Object imageToken) {
149             if (imageToken instanceof Integer) {
150                 final int resourceId = (Integer) imageToken;
151                 if (DEBUG) Log.v(TAG, "load resourceId " + resourceId);
152                 Drawable drawable = ContextCompat.getDrawable(mActivity, resourceId);
153                 if (drawable instanceof BitmapDrawable) {
154                     return ((BitmapDrawable) drawable).getBitmap();
155                 }
156             }
157             return null;
158         }
159     }
160 
161     class LoadBitmapIntoBackgroundManagerTask extends LoadBitmapTaskBase {
162         @Override
onPostExecute(Request request)163         protected void onPostExecute(Request request) {
164             super.onPostExecute(request);
165             mBackgroundManager.setBitmap(request.mResult);
166         }
167     }
168 
169     class LoadBitmapCallbackTask extends LoadBitmapTaskBase {
170         BitmapLoadCallback mCallback;
171 
LoadBitmapCallbackTask(BitmapLoadCallback callback)172         LoadBitmapCallbackTask(BitmapLoadCallback callback) {
173             mCallback = callback;
174         }
175 
176         @Override
onPostExecute(Request request)177         protected void onPostExecute(Request request) {
178             super.onPostExecute(request);
179             if (mCallback != null) {
180                 mCallback.onBitmapLoaded(request.mResult);
181             }
182         }
183     }
184 
185     final Activity mActivity;
186     BackgroundManager mBackgroundManager;
187     LoadBackgroundRunnable mRunnable;
188 
189     // Allocate a dedicated handler because there may be no view available
190     // when setBackground is invoked.
191     static Handler sHandler = new Handler();
192 
createBackgroundManagerIfNeeded()193     void createBackgroundManagerIfNeeded() {
194         if (mBackgroundManager == null) {
195             mBackgroundManager = BackgroundManager.getInstance(mActivity);
196         }
197     }
198 
199     /**
200      * Attach BackgroundManager to activity window.
201      */
attachToWindow()202     public void attachToWindow() {
203         if (!ENABLED) {
204             return;
205         }
206         if (DEBUG) Log.v(TAG, "attachToWindow " + mActivity);
207         createBackgroundManagerIfNeeded();
208         mBackgroundManager.attach(mActivity.getWindow());
209     }
210 
211     /**
212      * Attach BackgroundManager to a view inside activity.
213      */
attachToView(View backgroundView)214     public void attachToView(View backgroundView) {
215         if (!ENABLED) {
216             return;
217         }
218         if (DEBUG) Log.v(TAG, "attachToView " + mActivity + " " + backgroundView);
219         createBackgroundManagerIfNeeded();
220         mBackgroundManager.attachToView(backgroundView);
221     }
222 
223     /**
224      * Sets a background bitmap. It will look up the cache first if missing, an AsyncTask will
225      * will be launched to load the bitmap.
226      */
setBackground(Object imageToken)227     public void setBackground(Object imageToken) {
228         if (!ENABLED) {
229             return;
230         }
231         if (DEBUG) Log.v(TAG, "set imageToken " + imageToken + " to " + mActivity);
232         createBackgroundManagerIfNeeded();
233         if (imageToken == null) {
234             mBackgroundManager.setDrawable(null);
235             return;
236         }
237         Bitmap cachedBitmap = BitmapCache.getInstance().getCache(imageToken);
238         if (cachedBitmap != null) {
239             mBackgroundManager.setBitmap(cachedBitmap);
240             return;
241         }
242         if (mRunnable != null) {
243             sHandler.removeCallbacks(mRunnable);
244         }
245         mRunnable = new LoadBackgroundRunnable(imageToken);
246         sHandler.postDelayed(mRunnable, SET_BACKGROUND_DELAY_MS);
247     }
248 
249     /**
250      * Clear Drawable.
251      */
clearDrawable()252     public void clearDrawable() {
253         if (!ENABLED) {
254             return;
255         }
256         if (DEBUG) Log.v(TAG, "clearDrawable to " + mActivity);
257         createBackgroundManagerIfNeeded();
258         mBackgroundManager.clearDrawable();
259     }
260 
261     /**
262      * Directly sets a Drawable as background.
263      */
setDrawable(Drawable drawable)264     public void setDrawable(Drawable drawable) {
265         if (!ENABLED) {
266             return;
267         }
268         if (DEBUG) Log.v(TAG, "setDrawable " + drawable + " to " + mActivity);
269         createBackgroundManagerIfNeeded();
270         mBackgroundManager.setDrawable(drawable);
271     }
272 
273     /**
274      * Load bitmap in background and pass result to BitmapLoadCallback.
275      */
loadBitmap(Object imageToken, BitmapLoadCallback callback)276     public void loadBitmap(Object imageToken, BitmapLoadCallback callback) {
277         Bitmap cachedBitmap = BitmapCache.getInstance().getCache(imageToken);
278         if (cachedBitmap != null) {
279             if (callback != null) {
280                 callback.onBitmapLoaded(cachedBitmap);
281                 return;
282             }
283         }
284         new LoadBitmapCallbackTask(callback).execute(new Request(imageToken));
285     }
286 }
287