Home | History | Annotate | Download | only in leanback
      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 
     55         private BitmapCache() {
     56         }
     57 
     58         /**
     59          * Get cached bitmap by token, returns null if missing cache.
     60          */
     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          */
     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          */
     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          */
     93         public abstract void onBitmapLoaded(Bitmap bitmap);
     94     }
     95 
     96     static class Request {
     97         Object mImageToken;
     98         Bitmap mResult;
     99 
    100         Request(Object imageToken) {
    101             mImageToken = imageToken;
    102         }
    103     }
    104 
    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 
    113         LoadBackgroundRunnable(Object imageToken) {
    114             mRequest = new Request(imageToken);
    115         }
    116 
    117         @Override
    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
    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
    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
    144         protected void onCancelled(Request request) {
    145             if (DEBUG) Log.v(TAG, "onCancelled");
    146         }
    147 
    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
    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 
    172         LoadBitmapCallbackTask(BitmapLoadCallback callback) {
    173             mCallback = callback;
    174         }
    175 
    176         @Override
    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 
    193     void createBackgroundManagerIfNeeded() {
    194         if (mBackgroundManager == null) {
    195             mBackgroundManager = BackgroundManager.getInstance(mActivity);
    196         }
    197     }
    198 
    199     /**
    200      * Attach BackgroundManager to activity window.
    201      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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