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