BasicBitmapDrawable.java revision 2d10993c4276db5b28ef7cb909362fbbc26c460c
1fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod/*
22409d5f8d7dd8b535ce5ea29e933f7db27d33793Behdad Esfahbod * Copyright (C) 2013 The Android Open Source Project
3469524692bd0a258b28e63294c984e677a9c2477Behdad Esfahbod *
4fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod * Licensed under the Apache License, Version 2.0 (the "License");
5c755cb3e3ac55156d0d2ec05adea7a650b97cc41Behdad Esfahbod * you may not use this file except in compliance with the License.
6fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod * You may obtain a copy of the License at
7fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod *
8fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod *      http://www.apache.org/licenses/LICENSE-2.0
9fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod *
10fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod * Unless required by applicable law or agreed to in writing, software
11fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod * distributed under the License is distributed on an "AS IS" BASIS,
12fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod * See the License for the specific language governing permissions and
14fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod * limitations under the License.
15fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod */
16fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodpackage com.android.bitmap.drawable;
17fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod
18fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport android.content.res.Resources;
19fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport android.graphics.Canvas;
20fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport android.graphics.ColorFilter;
21fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport android.graphics.Paint;
22fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport android.graphics.PixelFormat;
23fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport android.graphics.Rect;
24fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport android.graphics.drawable.Drawable;
25fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport android.util.DisplayMetrics;
26f860366456d9e59b139a940da6d89c3c4fb9e96eBehdad Esfahbodimport android.util.Log;
27fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbod
28fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport com.android.bitmap.BitmapCache;
290f0cd9d361f1bb614aa3fd4616160d027062370eBehdad Esfahbodimport com.android.bitmap.DecodeTask;
300f0cd9d361f1bb614aa3fd4616160d027062370eBehdad Esfahbodimport com.android.bitmap.DecodeTask.DecodeCallback;
31fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport com.android.bitmap.DecodeTask.DecodeOptions;
32c57d454accff66e5f2c58006e8fb40bc020b6182Behdad Esfahbodimport com.android.bitmap.NamedThreadFactory;
332c80296aa5991ad67483889147f5c84fefe54af2Behdad Esfahbodimport com.android.bitmap.RequestKey;
34c57d454accff66e5f2c58006e8fb40bc020b6182Behdad Esfahbodimport com.android.bitmap.RequestKey.Cancelable;
3522da7fd94d6318c52df69d70470a85464ffc533dBehdad Esfahbodimport com.android.bitmap.RequestKey.FileDescriptorFactory;
361336ecdf8e4e9879b96b26ecfbf5c9ba6c49e2b9Behdad Esfahbodimport com.android.bitmap.ReusableBitmap;
37fd92a3dde32fd10df30c9eeb97641bc3c15b1e9bBehdad Esfahbodimport com.android.bitmap.util.BitmapUtils;
3823c86aa0009324433e78fcd0c47f2c0ff14b1949Behdad Esfahbodimport com.android.bitmap.util.RectUtils;
392e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbodimport com.android.bitmap.util.Trace;
402e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
412e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbodimport java.util.concurrent.Executor;
422e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbodimport java.util.concurrent.LinkedBlockingQueue;
4305ad6b50ac0a1b9a8da10d2ee2238068b7811e7dBehdad Esfahbodimport java.util.concurrent.ThreadPoolExecutor;
4405ad6b50ac0a1b9a8da10d2ee2238068b7811e7dBehdad Esfahbodimport java.util.concurrent.TimeUnit;
452e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
462e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod/**
472e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod * This class encapsulates the basic functionality needed to display a single image bitmap,
4805ad6b50ac0a1b9a8da10d2ee2238068b7811e7dBehdad Esfahbod * including request creation/cancelling, and data unbinding and re-binding.
4905ad6b50ac0a1b9a8da10d2ee2238068b7811e7dBehdad Esfahbod * <p>
5005ad6b50ac0a1b9a8da10d2ee2238068b7811e7dBehdad Esfahbod * The actual bitmap decode work is handled by {@link DecodeTask}.
5109675a8115b9d77261c33940401aa919cede8662Behdad Esfahbod * <p>
5209675a8115b9d77261c33940401aa919cede8662Behdad Esfahbod * If being used with a long-lived cache (static cache, attached to the Application instead of the
53832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod * Activity, etc) then make sure to call {@link BasicBitmapDrawable#unbind()} at the appropriate
542e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod * times so the cache has accurate unref counts. The
5509675a8115b9d77261c33940401aa919cede8662Behdad Esfahbod * {@link com.android.bitmap.view.BitmapDrawableImageView} class has been created to do the
56832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod * appropriate unbind operation when the view is detached from the window.
57832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod */
582e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbodpublic class BasicBitmapDrawable extends Drawable implements DecodeCallback,
592e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        Drawable.Callback, RequestKey.Callback {
602e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
612e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    protected static Rect sRect;
622e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
632e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    protected RequestKey mCurrKey;
642e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    protected RequestKey mPrevKey;
652e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    protected int mDecodeWidth;
662e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    protected int mDecodeHeight;
672e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
682e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    protected final Paint mPaint = new Paint();
692e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private final BitmapCache mCache;
702e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
712e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private final boolean mLimitDensity;
722e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private final float mDensity;
732e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private ReusableBitmap mBitmap;
742e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private DecodeTask mTask;
752e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private Cancelable mCreateFileDescriptorFactoryTask;
762e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
772e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    // based on framework CL:I015d77
782e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
792e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
802e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
812e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
822e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private static final Executor SMALL_POOL_EXECUTOR = new ThreadPoolExecutor(
832e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, 1, TimeUnit.SECONDS,
842e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod            new LinkedBlockingQueue<Runnable>(128), new NamedThreadFactory("decode"));
852e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private static final Executor EXECUTOR = SMALL_POOL_EXECUTOR;
862e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
872e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private static final int MAX_BITMAP_DENSITY = DisplayMetrics.DENSITY_HIGH;
882e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private static final float VERTICAL_CENTER = 1f / 2;
892e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private static final float NO_MULTIPLIER = 1f;
902e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
912e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private static final String TAG = BasicBitmapDrawable.class.getSimpleName();
922e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    private static final boolean DEBUG = DecodeTask.DEBUG;
932e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
942e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    public BasicBitmapDrawable(final Resources res, final BitmapCache cache,
952e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod            final boolean limitDensity) {
962e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        mDensity = res.getDisplayMetrics().density;
972e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        mCache = cache;
982e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        mLimitDensity = limitDensity;
992e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        mPaint.setFilterBitmap(true);
1002e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        mPaint.setAntiAlias(true);
1012e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        mPaint.setDither(true);
1022e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
1032e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        if (sRect == null) {
1042e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod            sRect = new Rect();
1052e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        }
1062e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    }
1072e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
1082e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    public final RequestKey getKey() {
1092e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        return mCurrKey;
1102e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    }
1112e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
1122e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    public final RequestKey getPreviousKey() {
1132e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        return mPrevKey;
1142e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    }
1152e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
1162e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    protected ReusableBitmap getBitmap() {
1172e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        return mBitmap;
1182e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    }
1192e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
1202e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    /**
1212e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * Set the dimensions to decode into. These dimensions should never change while the drawable is
1222e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * attached to the same cache, because caches can only contain bitmaps of one size for re-use.
1232e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     *
1242e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * All UI operations should be called from the UI thread.
1252e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     */
1262e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    public void setDecodeDimensions(int width, int height) {
1272e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        if (mDecodeWidth == 0 || mDecodeHeight == 0) {
1282e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod            mDecodeWidth = width;
1292e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod            mDecodeHeight = height;
1307e8c38954649c0bf2e6051d84ca08dce090ec169Behdad Esfahbod            setImage(mCurrKey);
1312e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        }
1322e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    }
1332e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
1342e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    /**
1352e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * Binds to the given key and start the decode process. This will first look in the cache, then
1362e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * decode from the request key if not found.
1372e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     *
1382e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * The key being replaced will be kept in {@link #mPrevKey}.
1392e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     *
1402e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * All UI operations should be called from the UI thread.
1412e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     */
1422e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    public void bind(RequestKey key) {
1432e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        Trace.beginSection("bind");
1442e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        if (mCurrKey != null && mCurrKey.equals(key)) {
1452e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod            Trace.endSection();
1462e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod            return;
1472e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        }
1482e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        setImage(key);
1492e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        Trace.endSection();
1502e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    }
1512e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
1522e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    /**
1532e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * Unbinds the current key and bitmap from the drawable. This will cause the bitmap to decrement
1542e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * its ref count.
1552e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     *
1562e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * This will assume that you do not want to keep the unbound key in {@link #mPrevKey}.
1572e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     *
1582e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * All UI operations should be called from the UI thread.
1592e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     */
1602e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    public void unbind() {
1612e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        unbind(false);
1622e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    }
1632e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod
1642e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    /**
1652e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * Unbinds the current key and bitmap from the drawable. This will cause the bitmap to decrement
1662e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * its ref count.
1672e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     *
1682e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod     * If the temporary parameter is true, we will keep the unbound key in {@link #mPrevKey}.
169101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod     *
170101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod     * All UI operations should be called from the UI thread.
171101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod     */
172101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    public void unbind(boolean temporary) {
173101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        Trace.beginSection("unbind");
174101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        setImage(null);
175101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        if (!temporary) {
176101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod            mPrevKey = null;
177101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        }
1782e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        Trace.endSection();
1792e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    }
1801a31f9f820c4538015ddaf4ca2b790649c5997edBehdad Esfahbod
18111fb16cb849285a178d9e80991b1d60a960326eeBehdad Esfahbod    /**
18211fb16cb849285a178d9e80991b1d60a960326eeBehdad Esfahbod     * Should only be overriden, not called.
18311fb16cb849285a178d9e80991b1d60a960326eeBehdad Esfahbod     */
18411fb16cb849285a178d9e80991b1d60a960326eeBehdad Esfahbod    protected void setImage(final RequestKey key) {
1851a31f9f820c4538015ddaf4ca2b790649c5997edBehdad Esfahbod        Trace.beginSection("set image");
1861a31f9f820c4538015ddaf4ca2b790649c5997edBehdad Esfahbod        Trace.beginSection("release reference");
1876faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod        if (mBitmap != null) {
188607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            mBitmap.releaseReference();
189607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            mBitmap = null;
1900f3fe37fccfb540437adf13dd580f2c5164a0b1fBehdad Esfahbod        }
191607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod        Trace.endSection();
1921a31f9f820c4538015ddaf4ca2b790649c5997edBehdad Esfahbod
1937627100f428ac0ec8509d961d368d2d25d8f0b6eBehdad Esfahbod        mPrevKey = mCurrKey;
1947627100f428ac0ec8509d961d368d2d25d8f0b6eBehdad Esfahbod        mCurrKey = key;
195607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod
196607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod        if (mTask != null) {
197607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            mTask.cancel();
1986faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod            mTask = null;
1993ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        }
2003ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        if (mCreateFileDescriptorFactoryTask != null) {
2017e08f1258da229dfaf7e1c4b5c41e5bb83906cb0Behdad Esfahbod            mCreateFileDescriptorFactoryTask.cancel();
2021a31f9f820c4538015ddaf4ca2b790649c5997edBehdad Esfahbod            mCreateFileDescriptorFactoryTask = null;
2037e08f1258da229dfaf7e1c4b5c41e5bb83906cb0Behdad Esfahbod        }
2047e08f1258da229dfaf7e1c4b5c41e5bb83906cb0Behdad Esfahbod
2056faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod        if (key == null) {
206607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            invalidateSelf();
207607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            Trace.endSection();
2081a31f9f820c4538015ddaf4ca2b790649c5997edBehdad Esfahbod            return;
209607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod        }
210607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod
2116faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod        // find cached entry here and skip decode if found.
2123ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        final ReusableBitmap cached = mCache.get(key, true /* incrementRefCount */);
2133ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        if (cached != null) {
214607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            setBitmap(cached);
215607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            if (DEBUG) {
216607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod                Log.d(TAG, String.format("CACHE HIT key=%s", mCurrKey));
217607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            }
2186faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod        } else {
219607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            loadFileDescriptorFactory();
220607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            if (DEBUG) {
221607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod                Log.d(TAG, String.format(
222607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod                        "CACHE MISS key=%s\ncache=%s", mCurrKey, mCache.toDebugString()));
223607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod            }
2246faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod        }
225607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod        Trace.endSection();
226607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod    }
2271a31f9f820c4538015ddaf4ca2b790649c5997edBehdad Esfahbod
228607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod    /**
229607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod     * Should only be overriden, not called.
2306faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod     */
231607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod    protected void setBitmap(ReusableBitmap bmp) {
232607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod        if (hasBitmap()) {
2331a31f9f820c4538015ddaf4ca2b790649c5997edBehdad Esfahbod            mBitmap.releaseReference();
234607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod        }
235607feb7cff0e50f8738d2e49ca463fc9d7d494deBehdad Esfahbod        mBitmap = bmp;
2366faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod        invalidateSelf();
2370b45479198d61d5135dad771e9c68408eb13f930Behdad Esfahbod    }
2380b45479198d61d5135dad771e9c68408eb13f930Behdad Esfahbod
2391a31f9f820c4538015ddaf4ca2b790649c5997edBehdad Esfahbod    /**
2400b45479198d61d5135dad771e9c68408eb13f930Behdad Esfahbod     * Should only be overriden, not called.
2410b45479198d61d5135dad771e9c68408eb13f930Behdad Esfahbod     */
2426faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod    protected void loadFileDescriptorFactory() {
243c52ddab72e025d1bee8274c8f3416208b12f68f1Behdad Esfahbod        if (mCurrKey == null || mDecodeWidth == 0 || mDecodeHeight == 0) {
244c52ddab72e025d1bee8274c8f3416208b12f68f1Behdad Esfahbod            return;
245c52ddab72e025d1bee8274c8f3416208b12f68f1Behdad Esfahbod        }
246c52ddab72e025d1bee8274c8f3416208b12f68f1Behdad Esfahbod
247c52ddab72e025d1bee8274c8f3416208b12f68f1Behdad Esfahbod        // Create file descriptor if request supports it.
2482e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        mCreateFileDescriptorFactoryTask = mCurrKey
249a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod                .createFileDescriptorFactoryAsync(mCurrKey, this);
250a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod        if (mCreateFileDescriptorFactoryTask == null) {
251a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod            // Use input stream if request does not.
2525d874d566fe5d2cc4cfaf02c79b663d8a626ca1eBehdad Esfahbod            decode(null);
2535d874d566fe5d2cc4cfaf02c79b663d8a626ca1eBehdad Esfahbod        }
2544751dec8be05883483fd5f6b474ebd22583ae566Behdad Esfahbod    }
255a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod
256a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod    @Override
257a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod    public void fileDescriptorFactoryCreated(final RequestKey key,
2582e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod            final FileDescriptorFactory factory) {
2592e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod        if (mCreateFileDescriptorFactoryTask == null) {
2602e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod            // Cancelled.
261a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod            return;
262a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod        }
263a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod        mCreateFileDescriptorFactoryTask = null;
264a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod
265a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod        if (key.equals(mCurrKey)) {
2665d874d566fe5d2cc4cfaf02c79b663d8a626ca1eBehdad Esfahbod            decode(factory);
267a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod        }
268a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod    }
269a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod
2702e96d2c6ee34142375373be07217c9953e7822ccBehdad Esfahbod    /**
271a3313e54008167e415b72c780ca7b9cda958d07eBehdad Esfahbod     * Should only be overriden, not called.
272101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod     */
273101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    protected void decode(final FileDescriptorFactory factory) {
274101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        Trace.beginSection("decode");
275101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        final int bufferW;
276101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        final int bufferH;
277101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        if (mLimitDensity) {
278101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod            final float scale =
2794751dec8be05883483fd5f6b474ebd22583ae566Behdad Esfahbod                    Math.min(1f, (float) MAX_BITMAP_DENSITY / DisplayMetrics.DENSITY_DEFAULT
280101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod                            / mDensity);
28149c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod            bufferW = (int) (mDecodeWidth * scale);
2823ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod            bufferH = (int) (mDecodeHeight * scale);
2833ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        } else {
2843ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod            bufferW = mDecodeWidth;
2852ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod            bufferH = mDecodeHeight;
2863ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        }
2872ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod
288101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        if (mTask != null) {
2892ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod            mTask.cancel();
2903ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        }
2913ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        final DecodeOptions opts = new DecodeOptions(bufferW, bufferH, getDecodeVerticalCenter(),
2923ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod                DecodeOptions.STRATEGY_ROUND_NEAREST);
2932ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod        mTask = new DecodeTask(mCurrKey, opts, factory, this, mCache);
2943ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        mTask.executeOnExecutor(getExecutor());
2952ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod        Trace.endSection();
296101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    }
2972ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod
2983ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod    protected Executor getExecutor() {
2992ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod        return EXECUTOR;
3003ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod    }
3012ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod
3022ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod    protected float getDrawVerticalCenter() {
30349c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod        return VERTICAL_CENTER;
3043ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod    }
30549c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod
3063ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod    protected float getDrawVerticalOffsetMultiplier() {
30749c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod        return NO_MULTIPLIER;
308101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    }
3094751dec8be05883483fd5f6b474ebd22583ae566Behdad Esfahbod
310a1f7b2856184959e965c9c2b80363f9f46d486a7Behdad Esfahbod    /**
3114751dec8be05883483fd5f6b474ebd22583ae566Behdad Esfahbod     * Clients can override this to specify which section of the source image to decode from.
3123ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod     * Possible applications include using face detection to always decode around facial features.
3134751dec8be05883483fd5f6b474ebd22583ae566Behdad Esfahbod     */
314101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    protected float getDecodeVerticalCenter() {
31549c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod        return VERTICAL_CENTER;
3163ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod    }
31749c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod
318a1f7b2856184959e965c9c2b80363f9f46d486a7Behdad Esfahbod    @Override
3192ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod    public void draw(final Canvas canvas) {
3202ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod        final Rect bounds = getBounds();
3213ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        if (bounds.isEmpty()) {
3222ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod            return;
323101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        }
3242ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod
3253ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod        if (hasBitmap()) {
3262ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod            BitmapUtils.calculateCroppedSrcRect(
3273ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod                    mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight(),
328a1f7b2856184959e965c9c2b80363f9f46d486a7Behdad Esfahbod                    bounds.width(), bounds.height(),
3293ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod                    bounds.height(), Integer.MAX_VALUE,
3302ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod                    getDrawVerticalCenter(), false /* absoluteFraction */,
3312ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod                    getDrawVerticalOffsetMultiplier(), sRect);
33249c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod
33349c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod            final int orientation = mBitmap.getOrientation();
3343ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod            // calculateCroppedSrcRect() gave us the source rectangle "as if" the orientation has
3353ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod            // been corrected. We need to decode the uncorrected source rectangle. Calculate true
3362ec3ba46a3c24469096e901750e38f6ee555479aBehdad Esfahbod            // coordinates.
33749c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod            RectUtils.rotateRectForOrientation(orientation,
3383ddf892b5328b74afb6e7d9da727d8771ca5d288Behdad Esfahbod                    new Rect(0, 0, mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight()),
33949c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod                    sRect);
34049c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod
34149c5ec51444f27f33e1eb6aa1959c61b08fa89c0Behdad Esfahbod            // We may need to rotate the canvas, so we also have to rotate the bounds.
34291689de2603e4151e2a2d3a3852c61667f0c6264Behdad Esfahbod            final Rect rotatedBounds = new Rect(bounds);
34391689de2603e4151e2a2d3a3852c61667f0c6264Behdad Esfahbod            RectUtils.rotateRect(orientation, bounds.centerX(), bounds.centerY(), rotatedBounds);
3446faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod
34591689de2603e4151e2a2d3a3852c61667f0c6264Behdad Esfahbod            // Rotate the canvas.
34691689de2603e4151e2a2d3a3852c61667f0c6264Behdad Esfahbod            canvas.save();
34791689de2603e4151e2a2d3a3852c61667f0c6264Behdad Esfahbod            canvas.rotate(orientation, bounds.centerX(), bounds.centerY());
34891689de2603e4151e2a2d3a3852c61667f0c6264Behdad Esfahbod            onDrawBitmap(canvas, sRect, rotatedBounds);
34991689de2603e4151e2a2d3a3852c61667f0c6264Behdad Esfahbod            canvas.restore();
3506faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod        }
351101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    }
352101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod
353101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    protected boolean hasBitmap() {
354101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        return mBitmap != null && mBitmap.bmp != null;
355101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    }
3566faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod
357101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    /**
358101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod     * Override this method to customize how to draw the bitmap to the canvas for the given bounds.
359101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod     * The bitmap to be drawn can be found at {@link #getBitmap()}.
360101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod     */
361101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    protected void onDrawBitmap(final Canvas canvas, final Rect src, final Rect dst) {
3626faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod        if (hasBitmap()) {
363101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod            canvas.drawBitmap(mBitmap.bmp, src, dst, mPaint);
364101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        }
365101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    }
366101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod
367101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    @Override
3686faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod    public void setAlpha(int alpha) {
369101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        final int old = mPaint.getAlpha();
370101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        mPaint.setAlpha(alpha);
371101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        if (alpha != old) {
372101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod            invalidateSelf();
373101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        }
374a1f7b2856184959e965c9c2b80363f9f46d486a7Behdad Esfahbod    }
375857027341423f15fd6084c7563cc355b06e7cbddBehdad Esfahbod
376857027341423f15fd6084c7563cc355b06e7cbddBehdad Esfahbod    @Override
377857027341423f15fd6084c7563cc355b06e7cbddBehdad Esfahbod    public void setColorFilter(ColorFilter cf) {
378857027341423f15fd6084c7563cc355b06e7cbddBehdad Esfahbod        mPaint.setColorFilter(cf);
379857027341423f15fd6084c7563cc355b06e7cbddBehdad Esfahbod        invalidateSelf();
380857027341423f15fd6084c7563cc355b06e7cbddBehdad Esfahbod    }
381a1f7b2856184959e965c9c2b80363f9f46d486a7Behdad Esfahbod
382a1f7b2856184959e965c9c2b80363f9f46d486a7Behdad Esfahbod    @Override
383a1f7b2856184959e965c9c2b80363f9f46d486a7Behdad Esfahbod    public int getOpacity() {
384832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod        return (hasBitmap() && (mBitmap.bmp.hasAlpha() || mPaint.getAlpha() < 255)) ?
385832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod                PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
386832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod    }
387832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod
388832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod    @Override
389832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod    public void onDecodeBegin(final RequestKey key) { }
390832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod
391832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod    @Override
392832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod    public void onDecodeComplete(final RequestKey key, final ReusableBitmap result) {
393832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod        if (key.equals(mCurrKey)) {
394832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod            setBitmap(result);
395832a6f99b34f334b1e82b8e3a7ad137e823d203cBehdad Esfahbod        } else {
396a1f7b2856184959e965c9c2b80363f9f46d486a7Behdad Esfahbod            // if the requests don't match (i.e. this request is stale), decrement the
397a1f7b2856184959e965c9c2b80363f9f46d486a7Behdad Esfahbod            // ref count to allow the bitmap to be pooled
39804dc52fa15f5b7f9eb5f448ea43e7ef1b2269e88Behdad Esfahbod            if (result != null) {
39904dc52fa15f5b7f9eb5f448ea43e7ef1b2269e88Behdad Esfahbod                result.releaseReference();
40004dc52fa15f5b7f9eb5f448ea43e7ef1b2269e88Behdad Esfahbod            }
40104dc52fa15f5b7f9eb5f448ea43e7ef1b2269e88Behdad Esfahbod        }
40204dc52fa15f5b7f9eb5f448ea43e7ef1b2269e88Behdad Esfahbod    }
40304dc52fa15f5b7f9eb5f448ea43e7ef1b2269e88Behdad Esfahbod
40404dc52fa15f5b7f9eb5f448ea43e7ef1b2269e88Behdad Esfahbod    @Override
40504dc52fa15f5b7f9eb5f448ea43e7ef1b2269e88Behdad Esfahbod    public void onDecodeCancel(final RequestKey key) { }
406101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod
407101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    @Override
4086faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod    public void invalidateDrawable(Drawable who) {
409101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        invalidateSelf();
410101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    }
411101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod
412101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    @Override
413101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    public void scheduleDrawable(Drawable who, Runnable what, long when) {
414101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        scheduleSelf(what, when);
4156faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod    }
416101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod
417101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    @Override
418101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    public void unscheduleDrawable(Drawable who, Runnable what) {
419101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod        unscheduleSelf(what);
420101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod    }
421101303dbf7cf15d044bf2518f14b3aec65970feaBehdad Esfahbod}
4226faff8e4132197ba06f0e685b82efe35b546cf64Behdad Esfahbod