diff options
author | Mark Wei <markwei@google.com> | 2013-10-30 14:23:50 -0700 |
---|---|---|
committer | Mark Wei <markwei@google.com> | 2013-10-31 17:35:05 -0700 |
commit | 2e4d0863dba53435372ec96538f2ef3e1c3675bf (patch) | |
tree | 6d7bbae6bcb78563036ffe5f685ef1a334bd512c | |
parent | 22955165fab693684cc3614c84ee81883ae933c8 (diff) | |
download | bitmap-2e4d0863dba53435372ec96538f2ef3e1c3675bf.tar.gz |
Make ExtendedBitmapDrawable extend BasicBitmapDrawable.
Modify sample app to use ExtendedBitmapDrawable with the following features:
Parallax,
Placholder and progress,
Decode aggregator.
Change-Id: Ia9e0fe6e6fdab018077a465ddf2a40109efbddf7
20 files changed, 277 insertions, 293 deletions
diff --git a/sample/res/drawable-hdpi/ic_placeholder.png b/sample/res/drawable-hdpi/ic_placeholder.png Binary files differnew file mode 100644 index 0000000..7b05b48 --- /dev/null +++ b/sample/res/drawable-hdpi/ic_placeholder.png diff --git a/sample/res/drawable-hdpi/ic_progress_inner.png b/sample/res/drawable-hdpi/ic_progress_inner.png Binary files differnew file mode 100644 index 0000000..e461e74 --- /dev/null +++ b/sample/res/drawable-hdpi/ic_progress_inner.png diff --git a/sample/res/drawable-hdpi/ic_progress_outer.png b/sample/res/drawable-hdpi/ic_progress_outer.png Binary files differnew file mode 100644 index 0000000..d495aa8 --- /dev/null +++ b/sample/res/drawable-hdpi/ic_progress_outer.png diff --git a/sample/res/drawable-mdpi/ic_placeholder.png b/sample/res/drawable-mdpi/ic_placeholder.png Binary files differnew file mode 100644 index 0000000..752149d --- /dev/null +++ b/sample/res/drawable-mdpi/ic_placeholder.png diff --git a/sample/res/drawable-mdpi/ic_progress_inner.png b/sample/res/drawable-mdpi/ic_progress_inner.png Binary files differnew file mode 100644 index 0000000..0c06981 --- /dev/null +++ b/sample/res/drawable-mdpi/ic_progress_inner.png diff --git a/sample/res/drawable-mdpi/ic_progress_outer.png b/sample/res/drawable-mdpi/ic_progress_outer.png Binary files differnew file mode 100644 index 0000000..46061db --- /dev/null +++ b/sample/res/drawable-mdpi/ic_progress_outer.png diff --git a/sample/res/drawable-xhdpi/ic_placeholder.png b/sample/res/drawable-xhdpi/ic_placeholder.png Binary files differnew file mode 100644 index 0000000..92ed99b --- /dev/null +++ b/sample/res/drawable-xhdpi/ic_placeholder.png diff --git a/sample/res/drawable-xhdpi/ic_progress_inner.png b/sample/res/drawable-xhdpi/ic_progress_inner.png Binary files differnew file mode 100644 index 0000000..273e8e8 --- /dev/null +++ b/sample/res/drawable-xhdpi/ic_progress_inner.png diff --git a/sample/res/drawable-xhdpi/ic_progress_outer.png b/sample/res/drawable-xhdpi/ic_progress_outer.png Binary files differnew file mode 100644 index 0000000..568e9eb --- /dev/null +++ b/sample/res/drawable-xhdpi/ic_progress_outer.png diff --git a/sample/res/drawable-xxhdpi/ic_placeholder.png b/sample/res/drawable-xxhdpi/ic_placeholder.png Binary files differnew file mode 100644 index 0000000..18ac128 --- /dev/null +++ b/sample/res/drawable-xxhdpi/ic_placeholder.png diff --git a/sample/res/drawable-xxhdpi/ic_progress_inner.png b/sample/res/drawable-xxhdpi/ic_progress_inner.png Binary files differnew file mode 100644 index 0000000..c95c1bf --- /dev/null +++ b/sample/res/drawable-xxhdpi/ic_progress_inner.png diff --git a/sample/res/drawable-xxhdpi/ic_progress_outer.png b/sample/res/drawable-xxhdpi/ic_progress_outer.png Binary files differnew file mode 100644 index 0000000..fbf00d3 --- /dev/null +++ b/sample/res/drawable-xxhdpi/ic_progress_outer.png diff --git a/sample/res/drawable/progress.xml b/sample/res/drawable/progress.xml new file mode 100644 index 0000000..2db9837 --- /dev/null +++ b/sample/res/drawable/progress.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2013, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <rotate + android:drawable="@drawable/ic_progress_outer" + android:pivotX="50%" + android:pivotY="50%" + android:fromDegrees="0" + android:toDegrees="1080" /> + </item> + <item> + <rotate + android:drawable="@drawable/ic_progress_inner" + android:pivotX="50%" + android:pivotY="50%" + android:fromDegrees="720" + android:toDegrees="0" /> + </item> +</layer-list>
\ No newline at end of file diff --git a/sample/res/layout/activity_main.xml b/sample/res/layout/activity_main.xml index 1b29402..d80841d 100644 --- a/sample/res/layout/activity_main.xml +++ b/sample/res/layout/activity_main.xml @@ -23,6 +23,8 @@ <ListView android:id="@+id/list" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:divider="@android:color/transparent" + android:dividerHeight="0px"/> </RelativeLayout> diff --git a/sample/src/com/example/bitmapsample/BitmapRequestKeyImpl.java b/sample/src/com/example/bitmapsample/BitmapRequestKeyImpl.java index 6c375f1..eeec78f 100644 --- a/sample/src/com/example/bitmapsample/BitmapRequestKeyImpl.java +++ b/sample/src/com/example/bitmapsample/BitmapRequestKeyImpl.java @@ -27,6 +27,8 @@ public class BitmapRequestKeyImpl implements RequestKey { public final String mUriString; public final URL mUrl; + private boolean mSlept; + public BitmapRequestKeyImpl(String uriString) { mUriString = uriString; URL url = null; @@ -36,6 +38,7 @@ public class BitmapRequestKeyImpl implements RequestKey { e.printStackTrace(); } mUrl = url; + mSlept = false; } @Override @@ -70,6 +73,20 @@ public class BitmapRequestKeyImpl implements RequestKey { @Override public InputStream createInputStream() throws IOException { + // Artificially sleep for (deterministically) random amount of time. + if (!mSlept) { + // Character difference between shortest and longest uri. + final long spread = 26; + // Maximum amount of time to sleep. + final long max = 2; + final long duration = (long) ((float) (mUriString.length() % spread) / spread * max + * 1000); + try { + Thread.sleep(duration); + } catch (InterruptedException ignored) { + } + mSlept = true; + } return mUrl.openStream(); } diff --git a/sample/src/com/example/bitmapsample/BitmapView.java b/sample/src/com/example/bitmapsample/BitmapView.java index f192963..0613168 100644 --- a/sample/src/com/example/bitmapsample/BitmapView.java +++ b/sample/src/com/example/bitmapsample/BitmapView.java @@ -17,13 +17,19 @@ package com.example.bitmapsample; import android.content.Context; +import android.graphics.Canvas; import android.util.AttributeSet; +import android.widget.ListView; import com.android.bitmap.drawable.BasicBitmapDrawable; +import com.android.bitmap.drawable.ExtendedBitmapDrawable; import com.android.bitmap.view.BitmapDrawableImageView; public class BitmapView extends BitmapDrawableImageView { - private float mDensity; + private final float mDensity; + + private ListView mListView; + private float mParallaxSpeedMultiplier; public BitmapView(Context c) { this(c, null); @@ -41,6 +47,24 @@ public class BitmapView extends BitmapDrawableImageView { @Override protected void onSizeChanged(final int w, final int h, int oldw, int oldh) { - getBasicBitmapDrawable().setDecodeDimensions(w, h); + ExtendedBitmapDrawable drawable = (ExtendedBitmapDrawable) getBasicBitmapDrawable(); + drawable.setDecodeDimensions(w, (int) (h * mParallaxSpeedMultiplier)); + } + + public void setListView(final ListView listView) { + mListView = listView; + } + + @Override + protected void onDraw(final Canvas canvas) { + ExtendedBitmapDrawable drawable = (ExtendedBitmapDrawable) getBasicBitmapDrawable(); + float fraction = (float) getBottom() / (mListView.getHeight() + getHeight()); + drawable.setParallaxFraction(fraction); + + super.onDraw(canvas); + } + + public void setParallaxSpeedMultiplier(final float parallaxSpeedMultiplier) { + mParallaxSpeedMultiplier = parallaxSpeedMultiplier; } }
\ No newline at end of file diff --git a/sample/src/com/example/bitmapsample/MainActivity.java b/sample/src/com/example/bitmapsample/MainActivity.java index 4ad18e5..96e99ec 100644 --- a/sample/src/com/example/bitmapsample/MainActivity.java +++ b/sample/src/com/example/bitmapsample/MainActivity.java @@ -17,6 +17,8 @@ package com.example.bitmapsample; import android.app.Activity; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; @@ -24,19 +26,33 @@ import android.widget.BaseAdapter; import android.widget.ListView; import com.android.bitmap.BitmapCache; +import com.android.bitmap.DecodeAggregator; import com.android.bitmap.UnrefedBitmapCache; -import com.android.bitmap.drawable.BasicBitmapDrawable; +import com.android.bitmap.drawable.ExtendedBitmapDrawable; public class MainActivity extends Activity { + private ListView mListView; + private final BitmapCache mCache = new UnrefedBitmapCache(TARGET_CACHE_SIZE_BYTES, 0.1f, 0); + private final DecodeAggregator mDecodeAggregator = new DecodeAggregator(); + private static Drawable PLACEHOLDER; + private static Drawable PROGRESS; + + private static final float NORMAL_PARALLAX_MULTIPLIER = 1.5f; private static final int TARGET_CACHE_SIZE_BYTES = 5 * 1024 * 1024; - private final BitmapCache mCache = new UnrefedBitmapCache(TARGET_CACHE_SIZE_BYTES, 0.1f, 0); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + + if (PLACEHOLDER == null) { + Resources res = getResources(); + PLACEHOLDER = res.getDrawable(R.drawable.ic_placeholder); + PROGRESS = res.getDrawable(R.drawable.progress); + } + mListView = (ListView) findViewById(R.id.list); mListView.setAdapter(new MyAdapter()); } @@ -91,9 +107,13 @@ public class MainActivity extends Activity { v = (BitmapView) convertView; } else { v = new BitmapView(MainActivity.this); - final BasicBitmapDrawable drawable = new BasicBitmapDrawable(getResources(), mCache, - true /* limit density */); + final ExtendedBitmapDrawable drawable = new ExtendedBitmapDrawable(getResources(), + mCache, true /* limit density */, mDecodeAggregator, PLACEHOLDER, PROGRESS); + drawable.setParallaxSpeedMultiplier(NORMAL_PARALLAX_MULTIPLIER); + v.setBasicBitmapDrawable(drawable); + v.setListView(mListView); + v.setParallaxSpeedMultiplier(NORMAL_PARALLAX_MULTIPLIER); } v.getBasicBitmapDrawable().bind(new BitmapRequestKeyImpl(mItems[position])); return v; diff --git a/src/com/android/bitmap/DecodeTask.java b/src/com/android/bitmap/DecodeTask.java index 76d6019..87beac1 100644 --- a/src/com/android/bitmap/DecodeTask.java +++ b/src/com/android/bitmap/DecodeTask.java @@ -134,16 +134,20 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> { } } - Trace.beginSection("create fd and stream"); if (mFactory != null) { + Trace.beginSection("create fd"); fd = mFactory.createFileDescriptor(); + Trace.endSection(); } else { in = reset(in); if (in == null) { return null; } } - Trace.endSection(); + + if (isCancelled()) { + return null; + } Trace.beginSection("get bytesize"); final long byteSize; @@ -198,6 +202,9 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> { if (in == null) { return null; } + if (isCancelled()) { + return null; + } } Trace.beginSection("decodeBounds"); @@ -286,8 +293,12 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> { if (in == null) { return null; } + if (isCancelled()) { + return null; + } } + Bitmap decodeResult = null; final Rect srcRect = new Rect(); // Not orientation corrected. True coordinates. if (CROP_DURING_DECODE) { diff --git a/src/com/android/bitmap/drawable/BasicBitmapDrawable.java b/src/com/android/bitmap/drawable/BasicBitmapDrawable.java index a7cfb48..53ab38d 100644 --- a/src/com/android/bitmap/drawable/BasicBitmapDrawable.java +++ b/src/com/android/bitmap/drawable/BasicBitmapDrawable.java @@ -57,12 +57,15 @@ import java.util.concurrent.TimeUnit; */ public class BasicBitmapDrawable extends Drawable implements DecodeCallback, Drawable.Callback, RequestKey.Callback { + protected static Rect sRect; protected RequestKey mCurrKey; - protected final Paint mPaint = new Paint(); + protected RequestKey mPrevKey; + protected final Paint mPaint = new Paint(); private final BitmapCache mCache; + private final boolean mLimitDensity; private final float mDensity; private ReusableBitmap mBitmap; @@ -83,6 +86,7 @@ public class BasicBitmapDrawable extends Drawable implements DecodeCallback, private static final int MAX_BITMAP_DENSITY = DisplayMetrics.DENSITY_HIGH; private static final float VERTICAL_CENTER = 1f / 2; + private static final float NO_MULTIPLIER = 1f; private static final String TAG = BasicBitmapDrawable.class.getSimpleName(); private static final boolean DEBUG = DecodeTask.DEBUG; @@ -105,6 +109,10 @@ public class BasicBitmapDrawable extends Drawable implements DecodeCallback, return mCurrKey; } + protected ReusableBitmap getBitmap() { + return mBitmap; + } + /** * Set the dimensions to decode into. */ @@ -122,7 +130,7 @@ public class BasicBitmapDrawable extends Drawable implements DecodeCallback, setImage(key); } - private void setImage(final RequestKey key) { + protected void setImage(final RequestKey key) { if (mCurrKey != null && mCurrKey.equals(key)) { return; } @@ -134,6 +142,8 @@ public class BasicBitmapDrawable extends Drawable implements DecodeCallback, mBitmap = null; } Trace.endSection(); + + mPrevKey = mCurrKey; mCurrKey = key; if (mTask != null) { @@ -168,6 +178,79 @@ public class BasicBitmapDrawable extends Drawable implements DecodeCallback, Trace.endSection(); } + protected void setBitmap(ReusableBitmap bmp) { + if (mBitmap != null && mBitmap != bmp) { + mBitmap.releaseReference(); + } + mBitmap = bmp; + invalidateSelf(); + } + + protected void loadFileDescriptorFactory() { + if (mCurrKey == null || mDecodeWidth == 0 || mDecodeHeight == 0) { + return; + } + + // Create file descriptor if request supports it. + mCreateFileDescriptorFactoryTask = mCurrKey + .createFileDescriptorFactoryAsync(mCurrKey, this); + if (mCreateFileDescriptorFactoryTask == null) { + // Use input stream if request does not. + decode(null); + } + } + + @Override + public void fileDescriptorFactoryCreated(final RequestKey key, + final FileDescriptorFactory factory) { + if (mCreateFileDescriptorFactoryTask == null) { + // Cancelled. + return; + } + mCreateFileDescriptorFactoryTask = null; + + if (key.equals(mCurrKey)) { + decode(factory); + } + } + + protected void decode(final FileDescriptorFactory factory) { + Trace.beginSection("decode"); + final int bufferW; + final int bufferH; + if (mLimitDensity) { + final float scale = + Math.min(1f, (float) MAX_BITMAP_DENSITY / DisplayMetrics.DENSITY_DEFAULT + / mDensity); + bufferW = (int) (mDecodeWidth * scale); + bufferH = (int) (mDecodeHeight * scale); + } else { + bufferW = mDecodeWidth; + bufferH = mDecodeHeight; + } + + if (mTask != null) { + mTask.cancel(); + } + final DecodeOptions opts = new DecodeOptions(bufferW, bufferH, getDecodeVerticalCenter(), + DecodeOptions.STRATEGY_ROUND_NEAREST); + mTask = new DecodeTask(mCurrKey, opts, factory, this, mCache); + mTask.executeOnExecutor(EXECUTOR); + Trace.endSection(); + } + + protected float getDrawVerticalCenter() { + return VERTICAL_CENTER; + } + + protected float getDrawVerticalOffsetMultiplier() { + return NO_MULTIPLIER; + } + + protected float getDecodeVerticalCenter() { + return VERTICAL_CENTER; + } + @Override public void draw(final Canvas canvas) { final Rect bounds = getBounds(); @@ -180,8 +263,8 @@ public class BasicBitmapDrawable extends Drawable implements DecodeCallback, mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight(), bounds.width(), bounds.height(), bounds.height(), Integer.MAX_VALUE, - VERTICAL_CENTER, false /* absoluteFraction */, - 1, sRect); + getDrawVerticalCenter(), false /* absoluteFraction */, + getDrawVerticalOffsetMultiplier(), sRect); final int orientation = mBitmap.getOrientation(); // calculateCroppedSrcRect() gave us the source rectangle "as if" the orientation has @@ -243,78 +326,6 @@ public class BasicBitmapDrawable extends Drawable implements DecodeCallback, @Override public void onDecodeCancel(final RequestKey key) { } - protected ReusableBitmap getBitmap() { - return mBitmap; - } - - private void setBitmap(ReusableBitmap bmp) { - if (mBitmap != null && mBitmap != bmp) { - mBitmap.releaseReference(); - } - mBitmap = bmp; - invalidateSelf(); - } - - private void loadFileDescriptorFactory() { - if (mCurrKey == null) { - return; - } - if (mDecodeWidth == 0 || mDecodeHeight == 0) { - return; - } - - // Create file descriptor if request supports it. - mCreateFileDescriptorFactoryTask = mCurrKey - .createFileDescriptorFactoryAsync(mCurrKey, this); - if (mCreateFileDescriptorFactoryTask == null) { - // Use input stream if request does not. - decode(null); - } - } - - @Override - public void fileDescriptorFactoryCreated(final RequestKey key, - final FileDescriptorFactory factory) { - if (mCreateFileDescriptorFactoryTask == null) { - // Cancelled. - return; - } - mCreateFileDescriptorFactoryTask = null; - - if (key.equals(mCurrKey)) { - decode(factory); - } - } - - private void decode(final FileDescriptorFactory factory) { - Trace.beginSection("decode"); - final int bufferW; - final int bufferH; - if (mLimitDensity) { - final float scale = - Math.min(1f, (float) MAX_BITMAP_DENSITY / DisplayMetrics.DENSITY_DEFAULT - / mDensity); - bufferW = (int) (mDecodeWidth * scale); - bufferH = (int) (mDecodeHeight * scale); - } else { - bufferW = mDecodeWidth; - bufferH = mDecodeHeight; - } - - if (bufferW == 0 || bufferH == 0) { - Trace.endSection(); - return; - } - if (mTask != null) { - mTask.cancel(); - } - final DecodeOptions opts = new DecodeOptions(bufferW, bufferH, VERTICAL_CENTER, - DecodeOptions.STRATEGY_ROUND_NEAREST); - mTask = new DecodeTask(mCurrKey, opts, factory, this, mCache); - mTask.executeOnExecutor(EXECUTOR); - Trace.endSection(); - } - @Override public void invalidateDrawable(Drawable who) { invalidateSelf(); diff --git a/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java b/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java index 59b65bc..6f52061 100644 --- a/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java +++ b/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java @@ -23,31 +23,21 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Handler; -import android.util.DisplayMetrics; import android.util.Log; import android.view.animation.LinearInterpolator; import com.android.bitmap.BitmapCache; import com.android.bitmap.DecodeAggregator; import com.android.bitmap.DecodeTask; -import com.android.bitmap.DecodeTask.DecodeOptions; import com.android.bitmap.R; import com.android.bitmap.RequestKey; +import com.android.bitmap.RequestKey.FileDescriptorFactory; import com.android.bitmap.ReusableBitmap; -import com.android.bitmap.util.BitmapUtils; -import com.android.bitmap.util.RectUtils; import com.android.bitmap.util.Trace; -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - /** * This class encapsulates all functionality needed to display a single image bitmap, * including request creation/cancelling, data unbinding and re-binding, and fancy animations @@ -56,47 +46,27 @@ import java.util.concurrent.TimeUnit; * The actual bitmap decode work is handled by {@link DecodeTask}. * TODO: have this class extend from BasicBitmapDrawable */ -public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.DecodeCallback, - Drawable.Callback, Runnable, Parallaxable, DecodeAggregator.Callback { - - private RequestKey mCurrKey; +public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements + Runnable, Parallaxable, DecodeAggregator.Callback { - private ReusableBitmap mBitmap; - private final BitmapCache mCache; - private final boolean mLimitDensity; + // Ordered display. private DecodeAggregator mDecodeAggregator; - private DecodeTask mTask; - private int mDecodeWidth; - private int mDecodeHeight; - private int mLoadState = LOAD_STATE_UNINITIALIZED; + + // Parallax. private float mParallaxFraction = 0.5f; private float mParallaxSpeedMultiplier; + private static final float DECODE_VERTICAL_CENTER = 1f / 3; - // each attachment gets its own placeholder and progress indicator, to be shown, hidden, - // and animated based on Drawable#setVisible() changes, which are in turn driven by - // #setLoadState(). - private Placeholder mPlaceholder; - private Progress mProgress; - - private static final Executor SMALL_POOL_EXECUTOR = new ThreadPoolExecutor(4, 4, - 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); - - private static final Executor EXECUTOR = SMALL_POOL_EXECUTOR; - - private static final int MAX_BITMAP_DENSITY = DisplayMetrics.DENSITY_HIGH; - - private static final float VERTICAL_CENTER = 1f / 3; - + // Placeholder and progress. private static final int LOAD_STATE_UNINITIALIZED = 0; private static final int LOAD_STATE_NOT_YET_LOADED = 1; private static final int LOAD_STATE_LOADING = 2; private static final int LOAD_STATE_LOADED = 3; private static final int LOAD_STATE_FAILED = 4; - - private final float mDensity; + private int mLoadState = LOAD_STATE_UNINITIALIZED; + private Placeholder mPlaceholder; + private Progress mProgress; private int mProgressDelayMs; - private final Paint mPaint = new Paint(); - private final Rect mSrcRect = new Rect(); private final Handler mHandler = new Handler(); public static final boolean DEBUG = false; @@ -105,12 +75,12 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod public ExtendedBitmapDrawable(final Resources res, final BitmapCache cache, final boolean limitDensity, final DecodeAggregator decodeAggregator, final Drawable placeholder, final Drawable progress) { - mDensity = res.getDisplayMetrics().density; - mCache = cache; - mLimitDensity = limitDensity; + super(res, cache, limitDensity); + + // Ordered display. this.mDecodeAggregator = decodeAggregator; - mPaint.setFilterBitmap(true); + // Placeholder and progress. final int fadeOutDurationMs = res.getInteger(R.integer.bitmap_fade_animation_duration); final int tileColor = res.getColor(R.color.bitmap_placeholder_background_color); mProgressDelayMs = res.getInteger(R.integer.bitmap_progress_animation_delay); @@ -126,91 +96,82 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod mProgress.setCallback(this); } - public RequestKey getKey() { - return mCurrKey; - } - - /** - * Set the dimensions to which to decode into. For a parallax effect, ensure the height is - * larger than the destination of the bitmap. - * TODO: test parallax - */ - public void setDecodeDimensions(int w, int h) { - mDecodeWidth = w; - mDecodeHeight = h; - decode(); + @Override + public void setParallaxFraction(float fraction) { + mParallaxFraction = fraction; + invalidateSelf(); } public void setParallaxSpeedMultiplier(final float parallaxSpeedMultiplier) { mParallaxSpeedMultiplier = parallaxSpeedMultiplier; + invalidateSelf(); } + /** + * This sets the drawable to the failed state, which remove all animations from the placeholder. + * This is different from unbinding to the uninitialized state, where we expect animations. + */ public void showStaticPlaceholder() { setLoadState(LOAD_STATE_FAILED); } - public void unbind() { - setImage(null); - } - - public void bind(RequestKey key) { - setImage(key); - } - - private void setImage(final RequestKey key) { + @Override + protected void setImage(final RequestKey key) { if (mCurrKey != null && mCurrKey.equals(key)) { return; } - Trace.beginSection("set image"); - Trace.beginSection("release reference"); - if (mBitmap != null) { - mBitmap.releaseReference(); - mBitmap = null; - } - Trace.endSection(); if (mCurrKey != null && mDecodeAggregator != null) { mDecodeAggregator.forget(mCurrKey); } - mCurrKey = key; - - if (mTask != null) { - mTask.cancel(); - mTask = null; - } mHandler.removeCallbacks(this); // start from a clean slate on every bind // this allows the initial transition to be specially instantaneous, so e.g. a cache hit // doesn't unnecessarily trigger a fade-in setLoadState(LOAD_STATE_UNINITIALIZED); - if (key == null) { - invalidateSelf(); - Trace.endSection(); - return; + setLoadState(LOAD_STATE_FAILED); } - // find cached entry here and skip decode if found. - final ReusableBitmap cached = mCache.get(key, true /* incrementRefCount */); - if (cached != null) { - setBitmap(cached); - if (DEBUG) { - Log.d(TAG, String.format("CACHE HIT key=%s", mCurrKey)); - } - } else { - decode(); - if (DEBUG) { - Log.d(TAG, String.format( - "CACHE MISS key=%s\ncache=%s", mCurrKey, mCache.toDebugString())); - } + super.setImage(key); + } + + @Override + protected void setBitmap(ReusableBitmap bmp) { + setLoadState((bmp != null) ? LOAD_STATE_LOADED : LOAD_STATE_FAILED); + + super.setBitmap(bmp); + } + + @Override + protected void decode(final FileDescriptorFactory factory) { + boolean executeStateChange = shouldExecuteStateChange(); + if (executeStateChange) { + setLoadState(LOAD_STATE_NOT_YET_LOADED); } - Trace.endSection(); + + super.decode(factory); + } + + protected boolean shouldExecuteStateChange() { + // TODO: AttachmentDrawable should override this method to match prev and curr request keys. + return /* opts.stateChanges */ true; } @Override - public void setParallaxFraction(float fraction) { - mParallaxFraction = fraction; + public float getDrawVerticalCenter() { + return mParallaxFraction; + } + + @Override + protected float getDrawVerticalOffsetMultiplier() { + return mParallaxSpeedMultiplier; + } + + @Override + protected float getDecodeVerticalCenter() { + return DECODE_VERTICAL_CENTER; } @Override @@ -220,32 +181,7 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod return; } - if (mBitmap != null && mBitmap.bmp != null) { - BitmapUtils.calculateCroppedSrcRect( - mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight(), - bounds.width(), bounds.height(), - bounds.height(), Integer.MAX_VALUE, - mParallaxFraction, false /* absoluteFraction */, - mParallaxSpeedMultiplier, mSrcRect); - - final int orientation = mBitmap.getOrientation(); - // calculateCroppedSrcRect() gave us the source rectangle "as if" the orientation has - // been corrected. We need to decode the uncorrected source rectangle. Calculate true - // coordinates. - RectUtils.rotateRectForOrientation(orientation, - new Rect(0, 0, mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight()), - mSrcRect); - - // We may need to rotate the canvas, so we also have to rotate the bounds. - final Rect rotatedBounds = new Rect(bounds); - RectUtils.rotateRect(orientation, bounds.centerX(), bounds.centerY(), rotatedBounds); - - // Rotate the canvas. - canvas.save(); - canvas.rotate(orientation, bounds.centerX(), bounds.centerY()); - canvas.drawBitmap(mBitmap.bmp, mSrcRect, rotatedBounds, mPaint); - canvas.restore(); - } + super.draw(canvas); // Draw the two possible overlay layers in reverse-priority order. // (each layer will no-op the draw when appropriate) @@ -257,7 +193,7 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod @Override public void setAlpha(int alpha) { final int old = mPaint.getAlpha(); - mPaint.setAlpha(alpha); + super.setAlpha(alpha); mPlaceholder.setAlpha(alpha); mProgress.setAlpha(alpha); if (alpha != old) { @@ -267,19 +203,13 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod @Override public void setColorFilter(ColorFilter cf) { - mPaint.setColorFilter(cf); + super.setColorFilter(cf); mPlaceholder.setColorFilter(cf); mProgress.setColorFilter(cf); invalidateSelf(); } @Override - public int getOpacity() { - return (mBitmap != null && (mBitmap.bmp.hasAlpha() || mPaint.getAlpha() < 255)) ? - PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; - } - - @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); @@ -294,6 +224,7 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod } else { onBecomeFirstExpected(key); } + super.onDecodeBegin(key); } @Override @@ -319,7 +250,7 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod mDecodeAggregator.execute(key, new Runnable() { @Override public void run() { - onDecodeCompleteImpl(key, result); + ExtendedBitmapDrawable.super.onDecodeComplete(key, result); } @Override @@ -328,19 +259,7 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod } }); } else { - onDecodeCompleteImpl(key, result); - } - } - - private void onDecodeCompleteImpl(final RequestKey key, final ReusableBitmap result) { - if (key.equals(mCurrKey)) { - setBitmap(result); - } else { - // if the requests don't match (i.e. this request is stale), decrement the - // ref count to allow the bitmap to be pooled - if (result != null) { - result.releaseReference(); - } + super.onDecodeComplete(key, result); } } @@ -349,53 +268,14 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod if (mDecodeAggregator != null) { mDecodeAggregator.forget(key); } + super.onDecodeCancel(key); } - private void setBitmap(ReusableBitmap bmp) { - if (mBitmap != null && mBitmap != bmp) { - mBitmap.releaseReference(); - } - mBitmap = bmp; - setLoadState((bmp != null) ? LOAD_STATE_LOADED : LOAD_STATE_FAILED); - invalidateSelf(); - } - - private void decode() { - final int bufferW; - final int bufferH; - - if (mCurrKey == null) { - return; - } - - Trace.beginSection("decode"); - if (mLimitDensity) { - final float scale = - Math.min(1f, (float) MAX_BITMAP_DENSITY / DisplayMetrics.DENSITY_DEFAULT - / mDensity); - bufferW = (int) (mDecodeWidth * scale); - bufferH = (int) (mDecodeHeight * scale); - } else { - bufferW = mDecodeWidth; - bufferH = mDecodeHeight; - } - - if (bufferW == 0 || bufferH == 0) { - Trace.endSection(); - return; - } - if (mTask != null) { - mTask.cancel(); - } - setLoadState(LOAD_STATE_NOT_YET_LOADED); - final DecodeOptions opts = new DecodeOptions(bufferW, bufferH, VERTICAL_CENTER, - DecodeOptions.STRATEGY_ROUND_NEAREST); - // TODO: file is null because we expect this class to extend BasicBitmapDrawable soon. - mTask = new DecodeTask(mCurrKey, opts, null /* file */, this, mCache); - mTask.executeOnExecutor(EXECUTOR); - Trace.endSection(); - } - + /** + * Each attachment gets its own placeholder and progress indicator, to be shown, hidden, + * and animated based on Drawable#setVisible() changes, which are in turn driven by + * setLoadState(). + */ private void setLoadState(int loadState) { if (DEBUG) { Log.v(TAG, String.format("IN setLoadState. old=%s new=%s key=%s this=%s", @@ -448,21 +328,6 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod } } - @Override - public void invalidateDrawable(Drawable who) { - invalidateSelf(); - } - - @Override - public void scheduleDrawable(Drawable who, Runnable what, long when) { - scheduleSelf(what, when); - } - - @Override - public void unscheduleDrawable(Drawable who, Runnable what) { - unscheduleSelf(what); - } - private static class Placeholder extends TileDrawable { private final ValueAnimator mPulseAnimator; |