diff options
author | Chih-Chung Chang <chihchung@google.com> | 2009-04-27 19:43:36 +0800 |
---|---|---|
committer | Chih-Chung Chang <chihchung@google.com> | 2009-04-28 10:26:38 +0800 |
commit | ff9922fb1a15f70ba34f35bc50c5b5ef52c174b2 (patch) | |
tree | a7ebc84bdd4040c8a25e41aa2a1868fd07b5e140 /src/com | |
parent | ebd325f9fd8fec33e7961a876c7f5d5934f36411 (diff) | |
download | LegacyCamera-ff9922fb1a15f70ba34f35bc50c5b5ef52c174b2.zip LegacyCamera-ff9922fb1a15f70ba34f35bc50c5b5ef52c174b2.tar.gz LegacyCamera-ff9922fb1a15f70ba34f35bc50c5b5ef52c174b2.tar.bz2 |
In Gallery, make visible thumbnails appear faster.
Put checkThumbnails and getBitmap to the same thread in ImageLoader,
and only do checkThumbnails when there are no getBitmap requests.
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/camera/GridViewSpecial.java | 12 | ||||
-rw-r--r-- | src/com/android/camera/ImageGallery.java | 130 | ||||
-rw-r--r-- | src/com/android/camera/ImageLoader.java | 125 | ||||
-rwxr-xr-x | src/com/android/camera/ImageManager.java | 3 | ||||
-rw-r--r-- | src/com/android/camera/gallery/BaseImage.java | 4 | ||||
-rw-r--r-- | src/com/android/camera/gallery/BaseImageList.java | 33 | ||||
-rw-r--r-- | src/com/android/camera/gallery/DrmImageList.java | 9 | ||||
-rw-r--r-- | src/com/android/camera/gallery/IImageList.java | 17 | ||||
-rw-r--r-- | src/com/android/camera/gallery/Image.java | 3 | ||||
-rw-r--r-- | src/com/android/camera/gallery/ImageListUber.java | 13 |
10 files changed, 200 insertions, 149 deletions
diff --git a/src/com/android/camera/GridViewSpecial.java b/src/com/android/camera/GridViewSpecial.java index ce5c52e..5d9deff 100644 --- a/src/com/android/camera/GridViewSpecial.java +++ b/src/com/android/camera/GridViewSpecial.java @@ -30,6 +30,7 @@ class GridViewSpecial extends View { ImageBlockManager mImageBlockManager; private Handler mHandler; + private ImageLoader mLoader; private LayoutSpec mCurrentSpec; boolean mShowSelection = false; @@ -91,7 +92,7 @@ class GridViewSpecial extends View { public void invalidateAllImages() { this.clearCache(); - mImageBlockManager = new ImageBlockManager(); + mImageBlockManager = new ImageBlockManager(mLoader); mImageBlockManager.moveDataWindow(true); } @@ -307,7 +308,7 @@ class GridViewSpecial extends View { * (spec.mCellSpacing + spec.mCellHeight)) - (bottom - top) ; if (mImageBlockManager == null) { - mImageBlockManager = new ImageBlockManager(); + mImageBlockManager = new ImageBlockManager(mLoader); mImageBlockManager.moveDataWindow(true); } mLayoutComplete = true; @@ -334,8 +335,8 @@ class GridViewSpecial extends View { private Thread mWorkerThread; - ImageBlockManager() { - mLoader = new ImageLoader(mHandler); + ImageBlockManager(ImageLoader loader) { + mLoader = loader; mBlockCache = new ImageBlock[sRowsPerPage * (sPagesPreCache + sPagesPostCache + 1)]; @@ -887,8 +888,9 @@ class GridViewSpecial extends View { } } - public void init(Handler handler) { + public void init(Handler handler, ImageLoader loader) { mHandler = handler; + mLoader = loader; } @Override diff --git a/src/com/android/camera/ImageGallery.java b/src/com/android/camera/ImageGallery.java index 7dfdd80..a926f34 100644 --- a/src/com/android/camera/ImageGallery.java +++ b/src/com/android/camera/ImageGallery.java @@ -79,9 +79,8 @@ public class ImageGallery extends Activity implements Handler mHandler = new Handler(); boolean mLayoutComplete; boolean mPausing = false; - boolean mStopThumbnailChecking = false; + ImageLoader mLoader; - BitmapThread mThumbnailCheckThread; GridViewSpecial mGvs; // The index of the first picture in GridViewSpecial. @@ -117,6 +116,8 @@ public class ImageGallery extends Activity implements mGvs.setOnCreateContextMenuListener( new CreateContextMenuListener()); } + + mLoader = new ImageLoader(mHandler); } private MenuItem addSlideShowMenu(Menu menu, int position) { @@ -347,7 +348,7 @@ public class ImageGallery extends Activity implements mAllImages = allImages(!unmounted); mGvs.setImageList(mAllImages); mGvs.setDrawAdapter(this); - mGvs.init(mHandler); + mGvs.init(mHandler, mLoader); mGvs.start(); mGvs.requestLayout(); checkThumbnails(); @@ -427,51 +428,14 @@ public class ImageGallery extends Activity implements } private void stopCheckingThumbnails() { - mStopThumbnailChecking = true; - if (mThumbnailCheckThread != null) { - mThumbnailCheckThread.join(); - } - mStopThumbnailChecking = false; + mLoader.stopCheckingThumbnails(); } private void checkThumbnails() { - final long startTime = System.currentTimeMillis(); - mThumbnailCheckThread = new BitmapThread(new Runnable() { - public void run() { - Resources resources = getResources(); - TextView progressTextView = - (TextView) findViewById(R.id.loading_text); - String progressTextFormatString = - resources.getString( - R.string.loading_progress_format_string); - - PowerManager pm = (PowerManager) - getSystemService(Context.POWER_SERVICE); - PowerManager.WakeLock mWakeLock = pm.newWakeLock( - PowerManager.SCREEN_BRIGHT_WAKE_LOCK, - "ImageGallery.checkThumbnails"); - mWakeLock.acquire(); - IImageList.ThumbCheckCallback r = new MyThumbCheckCallback( - progressTextView, startTime, progressTextFormatString); - IImageList imageList = allImages(true); - imageList.checkThumbnails(r, imageList.getCount()); - mWakeLock.release(); - mThumbnailCheckThread = null; - mHandler.post(new Runnable() { - public void run() { - findViewById(R.id.loading_indicator).setVisibility( - View.GONE); - } - }); - } - }); - - mThumbnailCheckThread.setName("check_thumbnails"); - mThumbnailCheckThread.start(); - mThumbnailCheckThread.toBackground(); - - IImageList list = allImages(true); - mNoImagesView.setVisibility(list.getCount() > 0 + ImageLoader.ThumbCheckCallback cb = new MyThumbCheckCallback(); + IImageList imageList = allImages(true); + mLoader.startCheckingThumbnails(imageList, cb); + mNoImagesView.setVisibility(imageList.getCount() > 0 ? View.GONE : View.VISIBLE); } @@ -667,25 +631,27 @@ public class ImageGallery extends Activity implements } } - private final class MyThumbCheckCallback implements IImageList.ThumbCheckCallback { - private final TextView progressTextView; - private final long startTime; - private final String progressTextFormatString; + private final class MyThumbCheckCallback implements + ImageLoader.ThumbCheckCallback { + private final TextView mProgressTextView; + private final String mProgressTextFormatString; boolean mDidSetProgress = false; - - private MyThumbCheckCallback(TextView progressTextView, long startTime, - String progressTextFormatString) { - this.progressTextView = progressTextView; - this.startTime = startTime; - this.progressTextFormatString = progressTextFormatString; + private PowerManager.WakeLock mWakeLock; + + private MyThumbCheckCallback() { + Resources resources = getResources(); + mProgressTextView = (TextView) findViewById(R.id.loading_text); + mProgressTextFormatString = resources.getString( + R.string.loading_progress_format_string); + PowerManager pm = (PowerManager) + getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock( + PowerManager.SCREEN_BRIGHT_WAKE_LOCK, + "ImageGallery.checkThumbnails"); + mWakeLock.acquire(); } - public boolean checking(final int count, - final int maxCount) { - if (mStopThumbnailChecking) { - return false; - } - + public boolean checking(final int count, final int maxCount) { if (!mLayoutComplete) { return true; } @@ -693,31 +659,38 @@ public class ImageGallery extends Activity implements if (!mDidSetProgress) { mHandler.post(new Runnable() { public void run() { - findViewById( - R.id.loading_indicator) - .setVisibility(View.VISIBLE); + View v = findViewById(R.id.loading_indicator); + v.setVisibility(View.VISIBLE); } }); mDidSetProgress = true; } mGvs.postInvalidate(); - // If there is a new image done and it has been - // one second, update the progress text. - if (System.currentTimeMillis() - - startTime > 1000) { - mHandler.post(new Runnable() { - public void run() { - String s = String.format( - progressTextFormatString, - maxCount - count); - progressTextView.setText(s); - } - }); - } + // Update the progress text. + mHandler.post(new Runnable() { + public void run() { + String s = String.format(mProgressTextFormatString, + maxCount - count); + mProgressTextView.setText(s); + } + }); return !mPausing; } + + public void done() { + // done() should only be called once. Use mWakeLock to verify this. + assert mWakeLock.isHeld(); + + mWakeLock.release(); + mHandler.post(new Runnable() { + public void run() { + findViewById(R.id.loading_indicator).setVisibility( + View.GONE); + } + }); + } } private class CreateContextMenuListener implements @@ -902,7 +875,4 @@ public class ImageGallery extends Activity implements return mMissingVideoThumbnailBitmap; } } - } - - diff --git a/src/com/android/camera/ImageLoader.java b/src/com/android/camera/ImageLoader.java index ad67b14..667bd5b 100644 --- a/src/com/android/camera/ImageLoader.java +++ b/src/com/android/camera/ImageLoader.java @@ -17,11 +17,14 @@ package com.android.camera; import com.android.camera.gallery.IImage; +import com.android.camera.gallery.IImageList; import android.graphics.Bitmap; import android.net.Uri; import android.os.Handler; +import android.util.Log; +import java.io.IOException; import java.util.ArrayList; /** @@ -36,11 +39,22 @@ public class ImageLoader { private final ArrayList<WorkItem> mInProgress = new ArrayList<WorkItem>(); // the worker thread and a done flag so we know when to exit - // currently we only exit from finalize private boolean mDone; private Thread mDecodeThread; private final Handler mHandler; + // Thumbnail checking will be done when there is no getBitmap requests + // need to be processed. + private ThumbnailChecker mThumbnailChecker; + + /** + * Notify interface of how many thumbnails are processed. + */ + public interface ThumbCheckCallback { + public boolean checking(int current, int count); + public void done(); + } + public interface LoadedCallback { public void run(Bitmap result); } @@ -118,6 +132,7 @@ public class ImageLoader { public ImageLoader(Handler handler) { mHandler = handler; + mThumbnailChecker = new ThumbnailChecker(); start(); } @@ -134,15 +149,25 @@ public class ImageLoader { workItem = mQueue.remove(0); mInProgress.add(workItem); } else { - try { - mQueue.wait(); - } catch (InterruptedException ex) { - // ignore the exception + if (!mThumbnailChecker.hasMoreThumbnailsToCheck()) { + try { + mQueue.wait(); + } catch (InterruptedException ex) { + // ignore the exception + } + continue; } - continue; } } + // This holds if and only if the above + // hasMoreThumbnailsToCheck() returns true. (We put the call + // here because we want to release the lock on mQueue. + if (workItem == null) { + mThumbnailChecker.checkNextThumbnail(); + continue; + } + final Bitmap b = workItem.mImage.miniThumbBitmap(); synchronized (mQueue) { @@ -197,5 +222,93 @@ public class ImageLoader { // so now what? } } + stopCheckingThumbnails(); + } + + // Passthrough to ThumbnailChecker. + public void startCheckingThumbnails(IImageList imageList, + ThumbCheckCallback cb) { + mThumbnailChecker.startCheckingThumbnails(imageList, cb); + // Kick WorkerThread to start working. + synchronized (mQueue) { + mQueue.notifyAll(); + } + } + + public void stopCheckingThumbnails() { + mThumbnailChecker.stopCheckingThumbnails(); + } +} + +// This is part of ImageLoader which is responsible for checking thumbnails. +// +// The methods of ThumbnailChecker need to be synchronized because the data +// will also be accessed by the WorkerThread. The methods of ThumbnailChecker +// is only called by ImageLoader. +class ThumbnailChecker { + private static final String TAG = "ThumbnailChecker"; + + private IImageList mImageListToCheck; // The image list we will check. + private int mTotalToCheck; // total number of thumbnails to check. + private int mNextToCheck; // next thumbnail to check, + // -1 if no further checking is needed. + private ImageLoader.ThumbCheckCallback mThumbCheckCallback; + + ThumbnailChecker() { + mNextToCheck = -1; + } + + // Both imageList and cb must be non-null. + synchronized void startCheckingThumbnails(IImageList imageList, + ImageLoader.ThumbCheckCallback cb) { + assert imageList != null; + assert cb != null; + mImageListToCheck = imageList; + mTotalToCheck = imageList.getCount(); + mNextToCheck = 0; + mThumbCheckCallback = cb; + + if (!ImageManager.hasStorage()) { + Log.v(TAG, "bailing from the image checker -- no storage"); + stopCheckingThumbnails(); + } + } + + synchronized void stopCheckingThumbnails() { + if (mThumbCheckCallback == null) return; // alreay stopped. + mThumbCheckCallback.done(); + mImageListToCheck = null; + mTotalToCheck = 0; + mNextToCheck = -1; + mThumbCheckCallback = null; + } + + synchronized boolean hasMoreThumbnailsToCheck() { + return mNextToCheck != -1; + } + + synchronized void checkNextThumbnail() { + if (mNextToCheck == -1) { + return; + } + + if (mNextToCheck >= mTotalToCheck) { + stopCheckingThumbnails(); + return; + } + + try { + mImageListToCheck.checkThumbnail(mNextToCheck); + } catch (IOException ex) { + Log.e(TAG, "Failed to check thumbnail..." + + " was the sd card removed? - " + ex.getMessage()); + stopCheckingThumbnails(); + } + + if (!mThumbCheckCallback.checking(mNextToCheck, mTotalToCheck)) { + stopCheckingThumbnails(); + } + + mNextToCheck++; } } diff --git a/src/com/android/camera/ImageManager.java b/src/com/android/camera/ImageManager.java index 8a4b055..acd11aa 100755 --- a/src/com/android/camera/ImageManager.java +++ b/src/com/android/camera/ImageManager.java @@ -314,8 +314,7 @@ public class ImageManager { } private static class EmptyImageList implements IImageList { - public void checkThumbnails(IImageList.ThumbCheckCallback cb, - int totalThumbnails) { + public void checkThumbnail(int index) { } public void deactivate() { diff --git a/src/com/android/camera/gallery/BaseImage.java b/src/com/android/camera/gallery/BaseImage.java index 66d9289..33554ce 100644 --- a/src/com/android/camera/gallery/BaseImage.java +++ b/src/com/android/camera/gallery/BaseImage.java @@ -342,7 +342,7 @@ public abstract class BaseImage implements IImage { long dbMagic = mMiniThumbMagic; if (dbMagic == 0 || dbMagic == id) { dbMagic = ((BaseImageList) getContainer()) - .checkThumbnail(this, getCursor(), getRow()); + .checkThumbnail(this, getRow(), null); } synchronized (sMiniThumbData) { @@ -353,7 +353,7 @@ public abstract class BaseImage implements IImage { byte[][] createdThumbData = new byte[1][]; try { dbMagic = ((BaseImageList) getContainer()) - .checkThumbnail(this, getCursor(), getRow(), + .checkThumbnail(this, getRow(), createdThumbData); } catch (IOException ex) { // Typically IOException because the sd card is full. diff --git a/src/com/android/camera/gallery/BaseImageList.java b/src/com/android/camera/gallery/BaseImageList.java index e26f972..dba6398 100644 --- a/src/com/android/camera/gallery/BaseImageList.java +++ b/src/com/android/camera/gallery/BaseImageList.java @@ -207,17 +207,14 @@ public abstract class BaseImageList implements IImageList { return bitmap; } - // returns id - public long checkThumbnail(BaseImage existingImage, Cursor c, int i) - throws IOException { - return checkThumbnail(existingImage, c, i, null); + public void checkThumbnail(int index) throws IOException { + checkThumbnail(null, index, null); } /** * Checks to see if a mini thumbnail exists in the cache. If not, tries to * create it and add it to the cache. * @param existingImage - * @param c * @param i * @param createdThumbnailData if this parameter is non-null, and a new * mini-thumbnail bitmap is created, the new bitmap's data will be @@ -228,13 +225,14 @@ public abstract class BaseImageList implements IImageList { * thumbnail even if the sdcard is full. * @throws IOException */ - public long checkThumbnail(BaseImage existingImage, Cursor c, int i, + public long checkThumbnail(BaseImage existingImage, int i, byte[][] createdThumbnailData) throws IOException { long magic, id; if (BitmapManager.instance().acquireResourceLock() == false) { return -1; } + Cursor c = getCursor(); try { if (existingImage == null) { // if we don't have an Image object then get the id and magic @@ -321,29 +319,6 @@ public abstract class BaseImageList implements IImageList { } } - public void checkThumbnails(ThumbCheckCallback cb, int totalThumbnails) { - if (!ImageManager.hasStorage()) { - Log.v(TAG, "bailing from the image checker thread -- no storage"); - return; - } - - Cursor c = getCursor(); - for (int i = 0; i < c.getCount(); i++) { - try { - checkThumbnail(null, c, i); - } catch (IOException ex) { - Log.e(TAG, "!!!!! failed to check thumbnail..." - + " was the sd card removed? - " + ex.getMessage()); - break; - } - if (cb != null) { - if (!cb.checking(i, totalThumbnails)) { - break; - } - } - } - } - protected Uri contentUri(long id) { try { // does our uri already have an id (single image query)? diff --git a/src/com/android/camera/gallery/DrmImageList.java b/src/com/android/camera/gallery/DrmImageList.java index ee976b9..68e9c07 100644 --- a/src/com/android/camera/gallery/DrmImageList.java +++ b/src/com/android/camera/gallery/DrmImageList.java @@ -48,14 +48,7 @@ public class DrmImageList extends ImageList implements IImageList { } @Override - public void checkThumbnails( - IImageList.ThumbCheckCallback cb, int totalCount) { - // do nothing - } - - @Override - public long checkThumbnail(BaseImage existingImage, Cursor c, int i) { - return 0; + public void checkThumbnail(int index) { } private class DrmImage extends Image { diff --git a/src/com/android/camera/gallery/IImageList.java b/src/com/android/camera/gallery/IImageList.java index 8be69ce..6820a35 100644 --- a/src/com/android/camera/gallery/IImageList.java +++ b/src/com/android/camera/gallery/IImageList.java @@ -18,6 +18,7 @@ package com.android.camera.gallery; import android.net.Uri; +import java.io.IOException; import java.util.HashMap; // @@ -46,16 +47,6 @@ import java.util.HashMap; public interface IImageList { public HashMap<String, String> getBucketIds(); - /** - * Notify interface of how many thumbnails are processed. - */ - public interface ThumbCheckCallback { - public boolean checking(int current, int count); - } - - public abstract void checkThumbnails( - IImageList.ThumbCheckCallback cb, int totalThumbnails); - public abstract void deactivate(); /** @@ -99,4 +90,10 @@ public interface IImageList { * @param i the position */ public abstract void removeImageAt(int i); + + /** + * Generate thumbnail for the image (if it has not been generated.) + * @param index the position of the image + */ + public abstract void checkThumbnail(int index) throws IOException; } diff --git a/src/com/android/camera/gallery/Image.java b/src/com/android/camera/gallery/Image.java index 04ae09f..a8a6796 100644 --- a/src/com/android/camera/gallery/Image.java +++ b/src/com/android/camera/gallery/Image.java @@ -328,8 +328,7 @@ public class Image extends BaseImage implements IImage { // fresh thumbs mMiniThumbMagic = 0; try { - mContainer.checkThumbnail( - this, mContainer.getCursor(), this.getRow()); + mContainer.checkThumbnail(this, this.getRow(), null); } catch (IOException e) { // Ignore inability to store mini thumbnail. } diff --git a/src/com/android/camera/gallery/ImageListUber.java b/src/com/android/camera/gallery/ImageListUber.java index 7cbc88b..4cd1ba1 100644 --- a/src/com/android/camera/gallery/ImageListUber.java +++ b/src/com/android/camera/gallery/ImageListUber.java @@ -20,6 +20,7 @@ import com.android.camera.ImageManager; import android.net.Uri; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -54,11 +55,13 @@ public class ImageListUber implements IImageList { mSort = sort; } - public void checkThumbnails(ThumbCheckCallback cb, int totalThumbnails) { - for (IImageList i : mSubList) { - int count = i.getCount(); - i.checkThumbnails(cb, totalThumbnails); - totalThumbnails -= count; + public void checkThumbnail(int index) throws IOException { + for (IImageList list : mSubList) { + int count = list.getCount(); + if (count > index) { + list.checkThumbnail(index); + } + index -= count; } } |