summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMark Wei <markwei@google.com>2013-10-24 14:51:54 -0700
committerMark Wei <markwei@google.com>2013-10-29 20:22:10 -0700
commit9c6ac19d4a3d39b7c2992060957920118ff56a65 (patch)
tree7ee9bd8104520b791f101c4e037795e16e3ec30c /src
parent46616414f6dabb6d57b0ac433a5fabe2abb8b5a9 (diff)
downloadbitmap-9c6ac19d4a3d39b7c2992060957920118ff56a65.tar.gz
Relax BasicBitmapView to allow non-BasicBitmapDrawables to be assigned to it.
Rename BasicBitmapView to BitmapDrawableImageView. Allow asynchronous creating of file. Change-Id: I0407bf0bf36ae92ce45d2175121a15483f8f72f2
Diffstat (limited to 'src')
-rw-r--r--src/com/android/bitmap/DecodeTask.java33
-rw-r--r--src/com/android/bitmap/RequestKey.java101
-rw-r--r--src/com/android/bitmap/drawable/BasicBitmapDrawable.java58
-rw-r--r--src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java7
-rw-r--r--src/com/android/bitmap/view/BitmapDrawableImageView.java (renamed from src/com/android/bitmap/view/BasicImageView.java)67
5 files changed, 197 insertions, 69 deletions
diff --git a/src/com/android/bitmap/DecodeTask.java b/src/com/android/bitmap/DecodeTask.java
index c90d772..3bb1252 100644
--- a/src/com/android/bitmap/DecodeTask.java
+++ b/src/com/android/bitmap/DecodeTask.java
@@ -25,6 +25,7 @@ import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.util.Log;
+import com.android.bitmap.RequestKey.FileDescriptorFactory;
import com.android.bitmap.util.BitmapUtils;
import com.android.bitmap.util.Exif;
import com.android.bitmap.util.RectUtils;
@@ -49,9 +50,9 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
private final RequestKey mKey;
private final DecodeOptions mDecodeOpts;
+ private final FileDescriptorFactory mFactory;
private final DecodeCallback mDecodeCallback;
private final BitmapCache mCache;
-
private final BitmapFactory.Options mOpts = new BitmapFactory.Options();
private ReusableBitmap mInBitmap = null;
@@ -85,17 +86,20 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
}
/**
- * Create new DecodeTask.
- *
- * @param requestKey The request to decode, also the key to use for the cache.
- * @param decodeOpts The decode options.
- * @param callback The callback to notify of decode state changes.
- * @param cache The cache and pool.
- */
+ * Create new DecodeTask.
+ *
+ * @param requestKey The request to decode, also the key to use for the cache.
+ * @param decodeOpts The decode options.
+ * @param factory The factory to obtain file descriptors to decode from. If this factory is
+ * null, then we will decode from requestKey.createInputStream().
+ * @param callback The callback to notify of decode state changes.
+ * @param cache The cache and pool.
+ */
public DecodeTask(RequestKey requestKey, DecodeOptions decodeOpts,
- DecodeCallback callback, BitmapCache cache) {
+ FileDescriptorFactory factory, DecodeCallback callback, BitmapCache cache) {
mKey = requestKey;
mDecodeOpts = decodeOpts;
+ mFactory = factory;
mDecodeCallback = callback;
mCache = cache;
}
@@ -131,14 +135,15 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
}
Trace.beginSection("create fd and stream");
- fd = mKey.createFd();
- Trace.endSection();
- if (fd == null) {
+ if (mFactory != null) {
+ fd = mFactory.createFileDescriptor();
+ } else {
in = reset(in);
if (in == null) {
return null;
}
}
+ Trace.endSection();
Trace.beginSection("get bytesize");
final long byteSize;
@@ -155,8 +160,8 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
if (fd != null) {
// Creating an input stream from the file descriptor makes it useless
// afterwards.
- Trace.beginSection("create fd and stream");
- final ParcelFileDescriptor orientationFd = mKey.createFd();
+ Trace.beginSection("create orientation fd and stream");
+ final ParcelFileDescriptor orientationFd = mFactory.createFileDescriptor();
in = new AutoCloseInputStream(orientationFd);
Trace.endSection();
}
diff --git a/src/com/android/bitmap/RequestKey.java b/src/com/android/bitmap/RequestKey.java
index 0e92164..d9c15ae 100644
--- a/src/com/android/bitmap/RequestKey.java
+++ b/src/com/android/bitmap/RequestKey.java
@@ -23,32 +23,111 @@ import java.io.InputStream;
/**
* The decode task uses this class to get input to decode. You must implement at least one of
- * {@link #createFd()} or {@link #createInputStream()}. {@link DecodeTask} will prioritize
- * {@link #createFd()} before falling back to {@link #createInputStream()}.
- * <p>
- * Objects of this type will also serve as cache keys to fetch cached data for {@link PooledCache}s,
- * so they must implement {@link #equals(Object)} and {@link #hashCode()}.
+ * {@link #createFileDescriptorFactoryAsync(RequestKey, Callback)} or {@link #createInputStream()}.
+ * {@link DecodeTask} will prioritize
+ * {@link #createFileDescriptorFactoryAsync(RequestKey, Callback)} before falling back to
+ * {@link #createInputStream()}.
+ *
* <p>
* Clients of this interface must also implement {@link #equals(Object)} and {@link #hashCode()} as
* this object will be used as a cache key.
+ *
+ * <p>
+ * The following is a high level view of the interactions between RequestKey and the rest of the
+ * system.
+ *
+ * BasicBitmapDrawable
+ * UI Thread
+ * ++
+ * bind() || Background Thread
+ * |+-------------------->+
+ * || createFDFasync() ||
+ * || || Download from url
+ * || || Cache on disk
+ * || ||
+ * || vv
+ * |<--------------------+x
+ * || FDFcreated()
+ * ||
+ * ||
+ * || DecodeTask
+ * || AsyncTask Thread
+ * |+-------------------->+
+ * || new().execute() ||
+ * || || Decode from FDF
+ * || || or createInputStream()
+ * || ||
+ * || vv
+ * |<--------------------+x
+ * || onDecodeComplete()
+ * vv
+ * invalidate() xx
*/
-
public interface RequestKey {
+
/**
- * Create an {@link ParcelFileDescriptor} for a local file stored on the device. This method
- * will be called first; if it returns null, {@link #createInputStream()} will be called.
+ * Create an {@link FileDescriptorFactory} for a local file stored on the device and pass it to
+ * the given callback. This method will be called first; if it returns null,
+ * {@link #createInputStream()} will be called.
+ *
+ * This method must be called from the UI thread.
+ *
+ * @param key The key to create a FileDescriptorFactory for. This key will be passed to the
+ * callback so it can check whether the key has changed.
+ * @param callback The callback to notify once the FileDescriptorFactory has been created. Do
+ * not invoke the callback directly from this method. Instead, create a handler
+ * and post a Runnable.
+ *
+ * @return If the client will attempt to create a FileDescriptorFactory, return a Cancelable
+ * object to cancel the asynchronous task. If the client wants to create an InputStream instead,
+ * return null. The callback must be notified if and only if the client returns a Cancelable
+ * object and not null.
*/
- public ParcelFileDescriptor createFd() throws IOException;
+ public Cancelable createFileDescriptorFactoryAsync(RequestKey key, Callback callback);
/**
- * Create an {@link InputStream} for a file. This method will be called if {@link #createFd()}
- * returns null.
+ * Create an {@link InputStream} for the source. This method will be called if
+ * {@link #createFileDescriptorFactoryAsync(RequestKey, Callback)} returns null.
+ *
+ * This method can be called from any thread.
*/
public InputStream createInputStream() throws IOException;
/**
* Return true if the image source may have be oriented in either portrait or landscape, and
* will need to be automatically re-oriented based on accompanying Exif metadata.
+ *
+ * This method can be called from any thread.
*/
public boolean hasOrientationExif() throws IOException;
+
+ /**
+ * Callback for creating the {@link FileDescriptorFactory} asynchronously.
+ */
+ public interface Callback {
+
+ /**
+ * Notifies that the {@link FileDescriptorFactory} has been created. This must be called on
+ * the UI thread.
+ * @param key The key that the FileDescriptorFactory was created for. The callback should
+ * check that the key has not changed.
+ * @param factory The FileDescriptorFactory to decode from.
+ */
+ void fileDescriptorFactoryCreated(RequestKey key, FileDescriptorFactory factory);
+ }
+
+ public interface FileDescriptorFactory {
+ ParcelFileDescriptor createFileDescriptor();
+ }
+
+ /**
+ * Interface for a background task that is cancelable.
+ */
+ public interface Cancelable {
+
+ /**
+ * Cancel the background task. This must be called on the UI thread.
+ */
+ void cancel();
+ }
} \ No newline at end of file
diff --git a/src/com/android/bitmap/drawable/BasicBitmapDrawable.java b/src/com/android/bitmap/drawable/BasicBitmapDrawable.java
index b172373..a7cfb48 100644
--- a/src/com/android/bitmap/drawable/BasicBitmapDrawable.java
+++ b/src/com/android/bitmap/drawable/BasicBitmapDrawable.java
@@ -27,14 +27,16 @@ import android.util.Log;
import com.android.bitmap.BitmapCache;
import com.android.bitmap.DecodeTask;
+import com.android.bitmap.DecodeTask.DecodeCallback;
import com.android.bitmap.DecodeTask.DecodeOptions;
import com.android.bitmap.NamedThreadFactory;
import com.android.bitmap.RequestKey;
+import com.android.bitmap.RequestKey.Cancelable;
+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 com.android.bitmap.view.BasicImageView;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
@@ -49,11 +51,12 @@ import java.util.concurrent.TimeUnit;
* <p>
* If being used with a long-lived cache (static cache, attached to the Application instead of the
* Activity, etc) then make sure to call {@link BasicBitmapDrawable#unbind()} at the appropriate
- * times so the cache has accurate unref counts. The {@link BasicImageView} class has been created
- * to do the appropriate unbind operation when the view is detached from the window.
+ * times so the cache has accurate unref counts. The
+ * {@link com.android.bitmap.view.BitmapDrawableImageView} class has been created to do the
+ * appropriate unbind operation when the view is detached from the window.
*/
-public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCallback,
- Drawable.Callback {
+public class BasicBitmapDrawable extends Drawable implements DecodeCallback,
+ Drawable.Callback, RequestKey.Callback {
protected static Rect sRect;
protected RequestKey mCurrKey;
@@ -64,6 +67,7 @@ public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCa
private final float mDensity;
private ReusableBitmap mBitmap;
private DecodeTask mTask;
+ private Cancelable mCreateFileDescriptorFactoryTask;
private int mDecodeWidth;
private int mDecodeHeight;
@@ -107,7 +111,7 @@ public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCa
public void setDecodeDimensions(int w, int h) {
mDecodeWidth = w;
mDecodeHeight = h;
- decode();
+ loadFileDescriptorFactory();
}
public void unbind() {
@@ -136,6 +140,10 @@ public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCa
mTask.cancel();
mTask = null;
}
+ if (mCreateFileDescriptorFactoryTask != null) {
+ mCreateFileDescriptorFactoryTask.cancel();
+ mCreateFileDescriptorFactoryTask = null;
+ }
if (key == null) {
invalidateSelf();
@@ -151,7 +159,7 @@ public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCa
Log.d(TAG, String.format("CACHE HIT key=%s", mCurrKey));
}
} else {
- decode();
+ loadFileDescriptorFactory();
if (DEBUG) {
Log.d(TAG, String.format(
"CACHE MISS key=%s\ncache=%s", mCurrKey, mCache.toDebugString()));
@@ -247,15 +255,41 @@ public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCa
invalidateSelf();
}
- private void decode() {
- final int bufferW;
- final int bufferH;
-
+ 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
@@ -276,7 +310,7 @@ public class BasicBitmapDrawable extends Drawable implements DecodeTask.DecodeCa
}
final DecodeOptions opts = new DecodeOptions(bufferW, bufferH, VERTICAL_CENTER,
DecodeOptions.STRATEGY_ROUND_NEAREST);
- mTask = new DecodeTask(mCurrKey, opts, this, mCache);
+ mTask = new DecodeTask(mCurrKey, opts, factory, this, mCache);
mTask.executeOnExecutor(EXECUTOR);
Trace.endSection();
}
diff --git a/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java b/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java
index 61efa12..59b65bc 100644
--- a/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java
+++ b/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java
@@ -32,11 +32,11 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.view.animation.LinearInterpolator;
-import com.android.bitmap.DecodeTask.DecodeOptions;
-import com.android.bitmap.R;
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.ReusableBitmap;
import com.android.bitmap.util.BitmapUtils;
@@ -390,7 +390,8 @@ public class ExtendedBitmapDrawable extends Drawable implements DecodeTask.Decod
setLoadState(LOAD_STATE_NOT_YET_LOADED);
final DecodeOptions opts = new DecodeOptions(bufferW, bufferH, VERTICAL_CENTER,
DecodeOptions.STRATEGY_ROUND_NEAREST);
- mTask = new DecodeTask(mCurrKey, opts, this, mCache);
+ // 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();
}
diff --git a/src/com/android/bitmap/view/BasicImageView.java b/src/com/android/bitmap/view/BitmapDrawableImageView.java
index 3e48af5..a55b864 100644
--- a/src/com/android/bitmap/view/BasicImageView.java
+++ b/src/com/android/bitmap/view/BitmapDrawableImageView.java
@@ -27,68 +27,77 @@ import com.android.bitmap.drawable.BasicBitmapDrawable;
/**
* A helpful ImageView replacement that can generally be used in lieu of ImageView.
- * BasicImageView has logic to unbind its BasicBitmapDrawable when it is detached from the window.
+ * BitmapDrawableImageView has logic to unbind its BasicBitmapDrawable when it is detached from the
+ * window.
*/
-public class BasicImageView extends ImageView {
+public class BitmapDrawableImageView extends ImageView {
private BasicBitmapDrawable mDrawable;
- public BasicImageView(final Context context) {
+ public BitmapDrawableImageView(final Context context) {
this(context, null);
}
- public BasicImageView(final Context context, final AttributeSet attrs) {
+ public BitmapDrawableImageView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
- public BasicImageView(final Context context, final AttributeSet attrs, final int defStyle) {
+ public BitmapDrawableImageView(final Context context, final AttributeSet attrs,
+ final int defStyle) {
super(context, attrs, defStyle);
}
/**
- * Set the given BasicBitmapDrawable as the source for this BasicImageView.
+ * Get the source BasicBitmapDrawable for this BitmapDrawableImageView.
+ * @return The source drawable.
+ */
+ public BasicBitmapDrawable getBasicBitmapDrawable() {
+ return mDrawable;
+ }
+
+ /**
+ * Set the given BasicBitmapDrawable as the source for this BitmapDrawableImageView.
* @param drawable The source drawable.
*/
- public void setDrawable(BasicBitmapDrawable drawable) {
+ public void setBasicBitmapDrawable(BasicBitmapDrawable drawable) {
super.setImageDrawable(drawable);
+ unbindDrawable();
mDrawable = drawable;
}
- public BasicBitmapDrawable getDrawable() {
- return mDrawable;
+ private void unbindDrawable() {
+ if (mDrawable != null) {
+ mDrawable.unbind();
+ mDrawable = null;
+ }
}
@Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- mDrawable.unbind();
+ public void setImageResource(final int resId) {
+ super.setImageResource(resId);
+ unbindDrawable();
}
@Override
- public void setImageDrawable(final Drawable drawable) {
- throw new UnsupportedOperationException(
- "BasicImageView is only compatible with BasicBitmapDrawable. Use setDrawable() "
- + "instead.");
+ public void setImageURI(final Uri uri) {
+ super.setImageURI(uri);
+ unbindDrawable();
}
@Override
- public void setImageResource(final int resId) {
- throw new UnsupportedOperationException(
- "BasicImageView is only compatible with BasicBitmapDrawable. Use setDrawable() "
- + "instead.");
+ public void setImageDrawable(final Drawable drawable) {
+ super.setImageDrawable(drawable);
+ unbindDrawable();
}
@Override
- public void setImageURI(final Uri uri) {
- throw new UnsupportedOperationException(
- "BasicImageView is only compatible with BasicBitmapDrawable. Use setDrawable() "
- + "instead.");
+ public void setImageBitmap(final Bitmap bm) {
+ super.setImageBitmap(bm);
+ unbindDrawable();
}
@Override
- public void setImageBitmap(final Bitmap bm) {
- throw new UnsupportedOperationException(
- "BasicImageView is only compatible with BasicBitmapDrawable. Use setDrawable() "
- + "instead.");
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ unbindDrawable();
}
}