diff options
author | Ray Chen <raychen@google.com> | 2009-08-27 17:59:29 -0700 |
---|---|---|
committer | Ray Chen <raychen@google.com> | 2009-09-23 11:35:46 -0700 |
commit | 9f1480b2c27d744c816c71cb3c512d37bc48c524 (patch) | |
tree | 49fd63ada65ba117a259ec1659569596787df4a8 /src/com | |
parent | 723f84eadb3c1a79337ef20e4d0a014341537adb (diff) | |
download | LegacyCamera-9f1480b2c27d744c816c71cb3c512d37bc48c524.zip LegacyCamera-9f1480b2c27d744c816c71cb3c512d37bc48c524.tar.gz LegacyCamera-9f1480b2c27d744c816c71cb3c512d37bc48c524.tar.bz2 |
Change Camera & Gallery code to use the new thumbnail APIs.
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/camera/ImageGallery.java | 86 | ||||
-rw-r--r-- | src/com/android/camera/ImageLoader.java | 150 | ||||
-rw-r--r-- | src/com/android/camera/ImageManager.java | 9 | ||||
-rw-r--r-- | src/com/android/camera/ThumbnailController.java | 3 | ||||
-rw-r--r-- | src/com/android/camera/Util.java | 85 | ||||
-rw-r--r-- | src/com/android/camera/VideoCamera.java | 5 | ||||
-rw-r--r-- | src/com/android/camera/gallery/BaseImage.java | 53 | ||||
-rw-r--r-- | src/com/android/camera/gallery/BaseImageList.java | 238 | ||||
-rw-r--r-- | src/com/android/camera/gallery/DrmImageList.java | 4 | ||||
-rw-r--r-- | src/com/android/camera/gallery/IImageList.java | 7 | ||||
-rw-r--r-- | src/com/android/camera/gallery/Image.java | 73 | ||||
-rw-r--r-- | src/com/android/camera/gallery/ImageList.java | 4 | ||||
-rw-r--r-- | src/com/android/camera/gallery/ImageListUber.java | 15 | ||||
-rw-r--r-- | src/com/android/camera/gallery/MiniThumbFile.java | 194 | ||||
-rw-r--r-- | src/com/android/camera/gallery/SingleImageList.java | 3 | ||||
-rw-r--r-- | src/com/android/camera/gallery/VideoObject.java | 21 |
16 files changed, 47 insertions, 903 deletions
diff --git a/src/com/android/camera/ImageGallery.java b/src/com/android/camera/ImageGallery.java index 26456ae..8e0c97f 100644 --- a/src/com/android/camera/ImageGallery.java +++ b/src/com/android/camera/ImageGallery.java @@ -29,7 +29,6 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -40,7 +39,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Parcelable; -import android.os.PowerManager; import android.preference.PreferenceManager; import android.provider.MediaStore; import android.util.Log; @@ -204,7 +202,6 @@ public class ImageGallery extends Activity implements private final Runnable mDeletePhotoRunnable = new Runnable() { public void run() { if (!canHandleEvent()) return; - stopCheckingThumbnails(); IImage currentImage = getCurrentImage(); @@ -218,7 +215,6 @@ public class ImageGallery extends Activity implements mGvs.setImageList(mAllImages); mGvs.start(); - checkThumbnails(); mNoImagesView.setVisibility(mAllImages.isEmpty() ? View.VISIBLE : View.GONE); @@ -377,7 +373,6 @@ public class ImageGallery extends Activity implements } private void rebake(boolean unmounted, boolean scanning) { - stopCheckingThumbnails(); mGvs.stop(); if (mAllImages != null) { mAllImages.close(); @@ -405,7 +400,6 @@ public class ImageGallery extends Activity implements mGvs.setDrawAdapter(this); mGvs.setLoader(mLoader); mGvs.start(); - checkThumbnails(); mNoImagesView.setVisibility(mAllImages.getCount() > 0 ? View.GONE : View.VISIBLE); @@ -476,15 +470,6 @@ public class ImageGallery extends Activity implements getContentResolver())); } - private void stopCheckingThumbnails() { - mLoader.stopCheckingThumbnails(); - } - - private void checkThumbnails() { - ImageLoader.ThumbCheckCallback cb = new MyThumbCheckCallback(); - mLoader.startCheckingThumbnails(mAllImages, cb); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { if (isPickIntent()) { @@ -693,77 +678,6 @@ public class ImageGallery extends Activity implements } } - private final class MyThumbCheckCallback implements - ImageLoader.ThumbCheckCallback { - private final TextView mProgressTextView; - private final String mProgressTextFormatString; - boolean mDidSetProgress = false; - private long mLastUpdateTime; // initialized to 0 - private final 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 (!mLayoutComplete) { - return true; - } - - if (!mDidSetProgress) { - mHandler.post(new Runnable() { - public void run() { - findViewById(R.id.loading_text).setVisibility( - View.VISIBLE); - findViewById(android.R.id.progress).setVisibility( - View.VISIBLE); - } - }); - mDidSetProgress = true; - } - mGvs.postInvalidate(); - - // Update the progress text. (Only if it has been one - // second since last update, to avoid the UI thread - // being overwhelmed by the update). - long currentTime = System.currentTimeMillis(); - if (currentTime - mLastUpdateTime > 1000) { - mHandler.post(new Runnable() { - public void run() { - String s = String.format(mProgressTextFormatString, - maxCount - count); - mProgressTextView.setText(s); - } - }); - mLastUpdateTime = currentTime; - } - 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_text).setVisibility(View.GONE); - findViewById(android.R.id.progress).setVisibility( - View.GONE); - } - }); - } - } - private class CreateContextMenuListener implements View.OnCreateContextMenuListener { public void onCreateContextMenu(ContextMenu menu, View v, diff --git a/src/com/android/camera/ImageLoader.java b/src/com/android/camera/ImageLoader.java index d93934b..4552123 100644 --- a/src/com/android/camera/ImageLoader.java +++ b/src/com/android/camera/ImageLoader.java @@ -17,17 +17,13 @@ package com.android.camera; import com.android.camera.gallery.IImage; -import com.android.camera.gallery.IImageList; import android.graphics.Bitmap; import android.os.Handler; import android.util.Log; -import java.io.IOException; import java.util.ArrayList; -import static com.android.camera.Util.Assert; - /** * A dedicated decoding thread used by ImageGallery. */ @@ -42,18 +38,6 @@ public class ImageLoader { private boolean mDone; private Thread mDecodeThread; - // 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); } @@ -119,19 +103,11 @@ public class ImageLoader { } public ImageLoader(Handler handler) { - mThumbnailChecker = new ThumbnailChecker(); start(); } private class WorkerThread implements Runnable { - // IDLE_TIME is the time we wait before we start checking thumbnail. - // This gives the thumbnail generation work priority because there - // may be a short period of time when the queue is empty while - // ImageBlockManager is calculating what to load next. - private static final long IDLE_TIME = 1000000000; // in nanoseconds. - private long mLastWorkTime = System.nanoTime(); - // Pick off items on the queue, one by one, and compute their bitmap. // Place the resulting bitmap in the cache, then call back by executing // the given runnable so things can get updated appropriately. @@ -145,46 +121,20 @@ public class ImageLoader { if (!mQueue.isEmpty()) { workItem = mQueue.remove(0); } else { - if (!mThumbnailChecker.hasMoreThumbnailsToCheck()) { - try { - mQueue.wait(); - } catch (InterruptedException ex) { - // ignore the exception - } - continue; - } else { - // Calculate the time we need to be idle before we - // start checking thumbnail. - long t = IDLE_TIME - - (System.nanoTime() - mLastWorkTime); - t = t / 1000000; // convert to milliseconds. - if (t > 0) { - try { - mQueue.wait(t); - } catch (InterruptedException ex) { - // ignore the exception - } - continue; - } + try { + mQueue.wait(); + } catch (InterruptedException ex) { + // ignore the exception } + 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(); if (workItem.mOnLoadedRunnable != null) { workItem.mOnLoadedRunnable.run(b); } - - mLastWorkTime = System.nanoTime(); } } } @@ -216,95 +166,5 @@ 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; // already 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(); - return; - } - - if (!mThumbCheckCallback.checking(mNextToCheck, mTotalToCheck)) { - stopCheckingThumbnails(); - return; - } - - mNextToCheck++; } } diff --git a/src/com/android/camera/ImageManager.java b/src/com/android/camera/ImageManager.java index 529c5bc..4b9b0cf 100644 --- a/src/com/android/camera/ImageManager.java +++ b/src/com/android/camera/ImageManager.java @@ -269,13 +269,12 @@ public class ImageManager { Image image = new Image(il, cr, id, 0, il.contentUri(id), null, 0, null, 0, null, null, 0); String[] projection = new String[] { - ImageColumns._ID, - ImageColumns.MINI_THUMB_MAGIC, ImageColumns.DATA}; + ImageColumns._ID, ImageColumns.DATA}; Cursor c = cr.query(uri, projection, null, null, null); String filepath; try { c.moveToPosition(0); - filepath = c.getString(2); + filepath = c.getString(1); } finally { c.close(); } @@ -335,7 +334,6 @@ public class ImageManager { // This is the factory function to create an image list. public static IImageList makeImageList(ContentResolver cr, ImageListParam param) { - DataLocation location = param.mLocation; int inclusion = param.mInclusion; int sort = param.mSort; @@ -432,9 +430,6 @@ public class ImageManager { public void close() { } - public void checkThumbnail(int index) { - } - public HashMap<String, String> getBucketIds() { return new HashMap<String, String>(); } diff --git a/src/com/android/camera/ThumbnailController.java b/src/com/android/camera/ThumbnailController.java index 78d8551..04a353f 100644 --- a/src/com/android/camera/ThumbnailController.java +++ b/src/com/android/camera/ThumbnailController.java @@ -24,6 +24,7 @@ import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; +import android.media.ThumbnailUtil; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.util.Log; @@ -178,7 +179,7 @@ public class ThumbnailController { LayoutParams param = mButton.getLayoutParams(); final int miniThumbWidth = param.width - 2 * PADDING_WIDTH; final int miniThumbHeight = param.height - 2 * PADDING_HEIGHT; - mThumb = Util.extractMiniThumb( + mThumb = ThumbnailUtil.extractMiniThumb( original, miniThumbWidth, miniThumbHeight, Util.NO_RECYCLE_INPUT); Drawable drawable; diff --git a/src/com/android/camera/Util.java b/src/com/android/camera/Util.java index a417e32..34deb3d 100644 --- a/src/com/android/camera/Util.java +++ b/src/com/android/camera/Util.java @@ -27,7 +27,6 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; -import android.media.MediaMetadataRetriever; import android.net.Uri; import android.os.Handler; import android.os.ParcelFileDescriptor; @@ -39,7 +38,6 @@ import android.view.animation.TranslateAnimation; import com.android.camera.gallery.IImage; -import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.FileDescriptor; import java.io.IOException; @@ -240,83 +238,6 @@ public class Util { return b2; } - /** - * Creates a centered bitmap of the desired size. - * @param source - * @param recycle whether we want to recycle the input - */ - public static Bitmap extractMiniThumb( - Bitmap source, int width, int height, boolean recycle) { - if (source == null) { - return null; - } - - float scale; - if (source.getWidth() < source.getHeight()) { - scale = width / (float) source.getWidth(); - } else { - scale = height / (float) source.getHeight(); - } - Matrix matrix = new Matrix(); - matrix.setScale(scale, scale); - Bitmap miniThumbnail = transform(matrix, source, width, height, - true, recycle); - return miniThumbnail; - } - - /** - * Creates a byte[] for a given bitmap of the desired size. Recycles the - * input bitmap. - */ - public static byte[] miniThumbData(Bitmap source) { - if (source == null) return null; - - Bitmap miniThumbnail = extractMiniThumb( - source, IImage.MINI_THUMB_TARGET_SIZE, - IImage.MINI_THUMB_TARGET_SIZE, - Util.RECYCLE_INPUT); - - ByteArrayOutputStream miniOutStream = new ByteArrayOutputStream(); - miniThumbnail.compress(Bitmap.CompressFormat.JPEG, 75, miniOutStream); - miniThumbnail.recycle(); - - try { - miniOutStream.close(); - byte [] data = miniOutStream.toByteArray(); - return data; - } catch (java.io.IOException ex) { - Log.e(TAG, "got exception ex " + ex); - } - return null; - } - - /** - * Create a video thumbnail for a video. May return null if the video is - * corrupt. - * - * @param filePath - */ - public static Bitmap createVideoThumbnail(String filePath) { - Bitmap bitmap = null; - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - try { - retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY); - retriever.setDataSource(filePath); - bitmap = retriever.captureFrame(); - } catch (IllegalArgumentException ex) { - // Assume this is a corrupt video file - } catch (RuntimeException ex) { - // Assume this is a corrupt video file. - } finally { - try { - retriever.release(); - } catch (RuntimeException ex) { - // Ignore failures while cleaning up. - } - } - return bitmap; - } - public static <T> int indexOf(T [] array, T s) { for (int i = 0; i < array.length; i++) { if (array[i].equals(s)) { @@ -350,12 +271,6 @@ public class Util { * @param uri */ public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, - Uri uri, ContentResolver cr) { - return makeBitmap(minSideLength, maxNumOfPixels, uri, cr, - IImage.NO_NATIVE); - } - - public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, Uri uri, ContentResolver cr, boolean useNative) { ParcelFileDescriptor input = null; try { diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java index dc2ae94..9582323 100644 --- a/src/com/android/camera/VideoCamera.java +++ b/src/com/android/camera/VideoCamera.java @@ -32,6 +32,7 @@ import android.graphics.drawable.Drawable; import android.hardware.Camera.Size; import android.hardware.Camera.Parameters; import android.media.MediaRecorder; +import android.media.ThumbnailUtil; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -1155,7 +1156,7 @@ public class VideoCamera extends Activity implements View.OnClickListener, fadeOut(findViewById(R.id.shutter_button)); if (mCurrentVideoFilename != null) { mVideoFrame.setImageBitmap( - Util.createVideoThumbnail(mCurrentVideoFilename)); + ThumbnailUtil.createVideoThumbnail(mCurrentVideoFilename)); mVideoFrame.setVisibility(View.VISIBLE); } int[] pickIds = {R.id.btn_retake, R.id.btn_done, R.id.btn_play}; @@ -1267,7 +1268,7 @@ public class VideoCamera extends Activity implements View.OnClickListener, } private void acquireVideoThumb() { - Bitmap videoFrame = Util.createVideoThumbnail(mCurrentVideoFilename); + Bitmap videoFrame = ThumbnailUtil.createVideoThumbnail(mCurrentVideoFilename); mThumbController.setData(mCurrentVideoUri, videoFrame); } diff --git a/src/com/android/camera/gallery/BaseImage.java b/src/com/android/camera/gallery/BaseImage.java index 9539c21..aa7116d 100644 --- a/src/com/android/camera/gallery/BaseImage.java +++ b/src/com/android/camera/gallery/BaseImage.java @@ -22,8 +22,10 @@ import com.android.camera.Util; import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.media.ThumbnailUtil; import android.net.Uri; import android.os.ParcelFileDescriptor; +import android.provider.MediaStore.Images; import android.util.Log; import java.io.FileNotFoundException; @@ -39,13 +41,8 @@ import java.io.OutputStream; public abstract class BaseImage implements IImage { private static final String TAG = "BaseImage"; private static final int UNKNOWN_LENGTH = -1; - - private static final byte [] sMiniThumbData = - new byte[MiniThumbFile.BYTES_PER_MINTHUMB]; - protected ContentResolver mContentResolver; - // Database field protected Uri mUri; protected long mId; @@ -213,51 +210,19 @@ public abstract class BaseImage implements IImage { } public Bitmap miniThumbBitmap() { + Bitmap b = null; try { long id = mId; - - synchronized (sMiniThumbData) { - byte [] data = null; - - // Try to get it from the file. - if (mMiniThumbMagic != 0) { - data = mContainer.getMiniThumbFromFile(id, sMiniThumbData, - mMiniThumbMagic); - } - - // If it does not exist, try to create the thumbnail - if (data == null) { - byte[][] createdThumbData = new byte[1][]; - try { - ((BaseImageList) getContainer()) - .checkThumbnail(this, createdThumbData); - } catch (IOException ex) { - // Typically IOException because the sd card is full. - // But createdThumbData may have been filled in, so - // continue on. - } - data = createdThumbData[0]; - } - - if (data == null) { - // Unable to get mini-thumb. - } - - if (data != null) { - Bitmap b = BitmapFactory.decodeByteArray(data, 0, - data.length); - if (b == null) { - Log.v(TAG, "couldn't decode byte array, " - + "length was " + data.length); - } - return b; - } - } - return null; + b = Images.Thumbnails.getThumbnail(mContentResolver, id, + Images.Thumbnails.MICRO_KIND, null); } catch (Throwable ex) { Log.e(TAG, "miniThumbBitmap got exception", ex); return null; } + if (b != null) { + b = Util.rotate(b, getDegreesRotated()); + } + return b; } protected void onRemove() { diff --git a/src/com/android/camera/gallery/BaseImageList.java b/src/com/android/camera/gallery/BaseImageList.java index cf6e853..7379fb0 100644 --- a/src/com/android/camera/gallery/BaseImageList.java +++ b/src/com/android/camera/gallery/BaseImageList.java @@ -24,18 +24,14 @@ import android.content.ContentUris; import android.content.ContentValues; import android.database.Cursor; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.media.ExifInterface; import android.net.Uri; import android.provider.BaseColumns; -import android.provider.MediaStore.Images.ImageColumns; import android.provider.MediaStore.Images.Thumbnails; import android.util.Log; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; -import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -54,7 +50,6 @@ public abstract class BaseImageList implements IImageList { protected Uri mBaseUri; protected Cursor mCursor; protected String mBucketId; - protected MiniThumbFile mMiniThumbFile; protected Uri mThumbUri; protected boolean mCursorDeactivated = false; @@ -63,7 +58,6 @@ public abstract class BaseImageList implements IImageList { mSort = sort; mBaseUri = uri; mBucketId = bucketId; - mMiniThumbFile = new MiniThumbFile(uri); mContentResolver = resolver; mCursor = createCursor(); @@ -84,7 +78,6 @@ public abstract class BaseImageList implements IImageList { // IllegalStateException may be thrown if the cursor is stale. Log.e(TAG, "Caught exception while deactivating cursor.", e); } - mMiniThumbFile.deactivate(); mContentResolver = null; if (mCursor != null) { mCursor.close(); @@ -92,230 +85,8 @@ public abstract class BaseImageList implements IImageList { } } - /** - * Store a given thumbnail in the database. - */ - protected Bitmap storeThumbnail(Bitmap thumb, long imageId) { - if (thumb == null) return null; - try { - Uri uri = getThumbnailUri(imageId, thumb.getWidth(), - thumb.getHeight()); - if (uri == null) { - return thumb; - } - OutputStream thumbOut = mContentResolver.openOutputStream(uri); - thumb.compress(Bitmap.CompressFormat.JPEG, 60, thumbOut); - thumbOut.close(); - return thumb; - } catch (Exception ex) { - Log.e(TAG, "Unable to store thumbnail", ex); - return thumb; - } - } - - /** - * Store a JPEG thumbnail from the EXIF header in the database. - */ - protected boolean storeThumbnail( - byte[] jpegThumbnail, long imageId, int width, int height) { - if (jpegThumbnail == null) return false; - - Uri uri = getThumbnailUri(imageId, width, height); - if (uri == null) { - return false; - } - try { - OutputStream thumbOut = mContentResolver.openOutputStream(uri); - thumbOut.write(jpegThumbnail); - thumbOut.close(); - return true; - } catch (FileNotFoundException ex) { - return false; - } catch (IOException ex) { - return false; - } - } - - private static final String[] THUMB_PROJECTION = new String[] { - BaseColumns._ID - }; - - private Uri getThumbnailUri(long imageId, int width, int height) { - - // we do not store thumbnails for DRM'd images - if (mThumbUri == null) { - return null; - } - - Cursor c = mContentResolver.query(mThumbUri, THUMB_PROJECTION, - Thumbnails.IMAGE_ID + "=?", - new String[]{String.valueOf(imageId)}, null); - try { - if (c.moveToNext()) { - return ContentUris.withAppendedId(mThumbUri, c.getLong(0)); - } - } finally { - c.close(); - } - ContentValues values = new ContentValues(4); - values.put(Thumbnails.KIND, Thumbnails.MINI_KIND); - values.put(Thumbnails.IMAGE_ID, imageId); - values.put(Thumbnails.HEIGHT, height); - values.put(Thumbnails.WIDTH, width); - try { - return mContentResolver.insert(mThumbUri, values); - } catch (Exception ex) { - return null; - } - } - - private static final Random sRandom = - new Random(System.currentTimeMillis()); - - // If the photo has an EXIF thumbnail and it's big enough, extract it and - // save that JPEG as the large thumbnail without re-encoding it. We still - // have to decompress it though, in order to generate the minithumb. - private Bitmap createThumbnailFromEXIF(String filePath, long id) { - if (filePath == null) return null; - - ExifInterface exif; - try { - exif = new ExifInterface(filePath); - } catch (IOException ex) { - Log.e(TAG, "cannot read exif", ex); - return null; - } - byte [] thumbData = exif.getThumbnail(); - if (thumbData == null) return null; - - // Sniff the size of the EXIF thumbnail before decoding it. Photos - // from the device will pass, but images that are side loaded from - // other cameras may not. - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, options); - int width = options.outWidth; - int height = options.outHeight; - if (width >= IImage.THUMBNAIL_TARGET_SIZE - && height >= IImage.THUMBNAIL_TARGET_SIZE) { - - // We do not check the return value of storeThumbnail because - // we should return the mini thumb even if the storing fails. - storeThumbnail(thumbData, id, width, height); - - // this is used for *encoding* the minithumb, so - // we don't want to dither or convert to 565 here. - // - // Decode with a scaling factor - // to match MINI_THUMB_TARGET_SIZE closely - // which will produce much better scaling quality - // and is significantly faster. - options.inSampleSize = - Util.computeSampleSize(options, - IImage.MINI_THUMB_TARGET_SIZE, - IImage.MINI_THUMB_MAX_NUM_PIXELS); - options.inDither = false; - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - options.inJustDecodeBounds = false; - return BitmapFactory.decodeByteArray( - thumbData, 0, thumbData.length, options); - } - return null; - } - - // The fallback case is to decode the original photo to thumbnail size, - // then encode it as a JPEG. We return the thumbnail Bitmap in order to - // create the minithumb from it. - private Bitmap createThumbnailFromUri(Uri uri, long id) { - Bitmap bitmap = Util.makeBitmap(IImage.THUMBNAIL_TARGET_SIZE, - IImage.THUMBNAIL_MAX_NUM_PIXELS, uri, mContentResolver); - if (bitmap != null) { - storeThumbnail(bitmap, id); - } else { - bitmap = Util.makeBitmap(IImage.MINI_THUMB_TARGET_SIZE, - IImage.MINI_THUMB_MAX_NUM_PIXELS, uri, mContentResolver); - } - return bitmap; - } - - public void checkThumbnail(int index) throws IOException { - checkThumbnail((BaseImage) getImageAt(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 createdThumbnailData if this parameter is non-null, and a new - * mini-thumbnail bitmap is created, the new bitmap's data will be - * stored in createdThumbnailData[0]. Note that if the sdcard is - * full, it's possible that createdThumbnailData[0] will be set - * even if the method throws an IOException. This is actually - * useful, because it allows the caller to use the created - * thumbnail even if the sdcard is full. - * @throws IOException - */ - public void checkThumbnail(BaseImage existingImage, - byte[][] createdThumbnailData) throws IOException { - long magic, id; - - magic = existingImage.mMiniThumbMagic; - id = existingImage.fullSizeImageId(); - - if (magic != 0) { - long fileMagic = mMiniThumbFile.getMagic(id); - if (fileMagic == magic) { - return; - } - } - - // If we can't retrieve the thumbnail, first check if there is one - // embedded in the EXIF data. If not, or it's not big enough, - // decompress the full size image. - Bitmap bitmap = null; - String filePath = existingImage.getDataPath(); - - if (filePath != null) { - boolean isVideo = ImageManager.isVideo(existingImage); - if (isVideo) { - bitmap = Util.createVideoThumbnail(filePath); - } else { - bitmap = createThumbnailFromEXIF(filePath, id); - if (bitmap == null) { - bitmap = createThumbnailFromUri( - ContentUris.withAppendedId(mBaseUri, id), id); - } - } - int degrees = existingImage.getDegreesRotated(); - if (degrees != 0) { - bitmap = Util.rotate(bitmap, degrees); - } - } - - // make a new magic number since things are out of sync - do { - magic = sRandom.nextLong(); - } while (magic == 0); - - if (bitmap != null) { - byte [] data = Util.miniThumbData(bitmap); - if (createdThumbnailData != null) { - createdThumbnailData[0] = data; - } - - // This could throw IOException. - saveMiniThumbToFile(data, id, magic); - } - - ContentValues values = new ContentValues(); - values.put(ImageColumns.MINI_THUMB_MAGIC, magic); - mContentResolver.update( - existingImage.fullSizeImageUri(), values, null, null); - existingImage.mMiniThumbMagic = magic; - } - // TODO: Change public to protected public Uri contentUri(long id) { - // TODO: avoid using exception for most cases try { // does our uri already have an id (single image query)? @@ -367,15 +138,6 @@ public abstract class BaseImageList implements IImageList { return result; } - byte [] getMiniThumbFromFile(long id, byte [] data, long magicCheck) { - return mMiniThumbFile.getMiniThumbFromFile(id, data, magicCheck); - } - - void saveMiniThumbToFile(byte[] data, long id, long magic) - throws IOException { - mMiniThumbFile.saveMiniThumbToFile(data, id, magic); - } - public boolean removeImage(IImage image) { // TODO: need to delete the thumbnails as well if (mContentResolver.delete(image.fullSizeImageUri(), null, null) > 0) { diff --git a/src/com/android/camera/gallery/DrmImageList.java b/src/com/android/camera/gallery/DrmImageList.java index 4ff9057..3acd43c 100644 --- a/src/com/android/camera/gallery/DrmImageList.java +++ b/src/com/android/camera/gallery/DrmImageList.java @@ -55,10 +55,6 @@ public class DrmImageList extends ImageList implements IImageList { mBaseUri, DRM_IMAGE_PROJECTION, null, null, sortOrder()); } - @Override - public void checkThumbnail(int index) { - } - private static class DrmImage extends Image { protected DrmImage(BaseImageList container, ContentResolver cr, diff --git a/src/com/android/camera/gallery/IImageList.java b/src/com/android/camera/gallery/IImageList.java index be85e72..07a6bb2 100644 --- a/src/com/android/camera/gallery/IImageList.java +++ b/src/com/android/camera/gallery/IImageList.java @@ -18,7 +18,6 @@ package com.android.camera.gallery; import android.net.Uri; -import java.io.IOException; import java.util.HashMap; // @@ -91,12 +90,6 @@ public interface IImageList { public int getImageIndex(IImage image); /** - * Generate thumbnail for the image (if it has not been generated.) - * @param index the position of the image - */ - public void checkThumbnail(int index) throws IOException; - - /** * Closes this list to release resources, no further operation is allowed. */ public void close(); diff --git a/src/com/android/camera/gallery/Image.java b/src/com/android/camera/gallery/Image.java index 9110a9b..340ba79 100644 --- a/src/com/android/camera/gallery/Image.java +++ b/src/com/android/camera/gallery/Image.java @@ -16,24 +16,19 @@ package com.android.camera.gallery; -import com.android.camera.BitmapManager; import com.android.camera.Util; import android.content.ContentResolver; -import android.content.ContentUris; import android.content.ContentValues; -import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.ExifInterface; import android.net.Uri; -import android.os.ParcelFileDescriptor; import android.provider.BaseColumns; +import android.provider.MediaStore.Images; import android.provider.MediaStore.Images.ImageColumns; -import android.provider.MediaStore.Images.Thumbnails; import android.util.Log; -import java.io.FileNotFoundException; import java.io.IOException; /** @@ -162,14 +157,6 @@ public class Image extends BaseImage implements IImage { setExifRotation(newDegrees); setDegreesRotated(newDegrees); - // setting this to zero will force the call to checkCursor to generate - // fresh thumbs - mMiniThumbMagic = 0; - try { - mContainer.checkThumbnail(this, null); - } catch (IOException e) { - // Ignore inability to store mini thumbnail. - } return true; } @@ -179,30 +166,11 @@ public class Image extends BaseImage implements IImage { public Bitmap thumbBitmap(boolean rotateAsNeeded) { Bitmap bitmap = null; - if (mContainer.mThumbUri != null) { - Cursor c = mContentResolver.query( - mContainer.mThumbUri, THUMB_PROJECTION, - Thumbnails.IMAGE_ID + "=?", - new String[] { String.valueOf(fullSizeImageId()) }, - null); - try { - if (c.moveToFirst()) { - bitmap = decodeCurrentImage(c.getLong(0)); - } - } catch (RuntimeException ex) { - // sdcard removed? - return null; - } finally { - c.close(); - } - } - - if (bitmap == null) { - bitmap = fullSizeBitmap(THUMBNAIL_TARGET_SIZE, - THUMBNAIL_MAX_NUM_PIXELS, NO_ROTATE, NO_NATIVE); - // No thumbnail found... storing the new one. - bitmap = mContainer.storeThumbnail(bitmap, mId); - } + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inDither = false; + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + bitmap = Images.Thumbnails.getThumbnail( + mContentResolver, mId, Images.Thumbnails.MINI_KIND, options); if (bitmap != null && rotateAsNeeded) { bitmap = Util.rotate(bitmap, getDegreesRotated()); @@ -210,31 +178,4 @@ public class Image extends BaseImage implements IImage { return bitmap; } - - private Bitmap decodeCurrentImage(long id) { - Uri thumbUri = ContentUris.withAppendedId( - mContainer.mThumbUri, id); - ParcelFileDescriptor pfdInput; - Bitmap bitmap = null; - try { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inDither = false; - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - pfdInput = mContentResolver.openFileDescriptor(thumbUri, "r"); - bitmap = BitmapManager.instance().decodeFileDescriptor( - pfdInput.getFileDescriptor(), options); - pfdInput.close(); - } catch (FileNotFoundException ex) { - Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex); - } catch (IOException ex) { - Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex); - } catch (NullPointerException ex) { - // we seem to get this if the file doesn't exist anymore - Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex); - } catch (OutOfMemoryError ex) { - Log.e(TAG, "failed to allocate memory for thumbnail " - + thumbUri + "; " + ex); - } - return bitmap; - } -} +}
\ No newline at end of file diff --git a/src/com/android/camera/gallery/ImageList.java b/src/com/android/camera/gallery/ImageList.java index 421eb0b..b461106 100644 --- a/src/com/android/camera/gallery/ImageList.java +++ b/src/com/android/camera/gallery/ImageList.java @@ -65,11 +65,11 @@ public class ImageList extends BaseImageList implements IImageList { } private static final String WHERE_CLAUSE = - "(" + Media.MIME_TYPE + " in (?, ?, ?))"; + "(" + Media.MIME_TYPE + " in (?, ?, ?)) AND " + + Media.MINI_THUMB_MAGIC + "<> 0"; private static final String WHERE_CLAUSE_WITH_BUCKET_ID = WHERE_CLAUSE + " AND " + Media.BUCKET_ID + " = ?"; - protected String whereClause() { return mBucketId == null ? WHERE_CLAUSE : WHERE_CLAUSE_WITH_BUCKET_ID; } diff --git a/src/com/android/camera/gallery/ImageListUber.java b/src/com/android/camera/gallery/ImageListUber.java index 837e2a8..95146ae 100644 --- a/src/com/android/camera/gallery/ImageListUber.java +++ b/src/com/android/camera/gallery/ImageListUber.java @@ -21,7 +21,6 @@ import android.net.Uri; import com.android.camera.ImageManager; import com.android.camera.Util; -import java.io.IOException; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; @@ -77,20 +76,6 @@ public class ImageListUber implements IImageList { return hashMap; } - public void checkThumbnail(int index) throws IOException { - // The index is not refer to the index of the image but in another order - // sequence. Since this function is only used to check all thumbnails - // is created, it should be fine. - for (IImageList list : mSubList) { - int count = list.getCount(); - if (count > index) { - list.checkThumbnail(index); - return; - } - index -= count; - } - } - public int getCount() { int count = 0; for (IImageList subList : mSubList) { diff --git a/src/com/android/camera/gallery/MiniThumbFile.java b/src/com/android/camera/gallery/MiniThumbFile.java deleted file mode 100644 index cee91c0..0000000 --- a/src/com/android/camera/gallery/MiniThumbFile.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -package com.android.camera.gallery; - -import android.net.Uri; -import android.os.Environment; -import android.util.Log; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; - -// This class handles the mini-thumb file. A mini-thumb file consists -// of blocks, indexed by id. Each block has BYTES_PER_MINTHUMB bytes in the -// following format: -// -// 1 byte status (0 = empty, 1 = mini-thumb available) -// 8 bytes magic (a magic number to match what's in the database) -// 4 bytes data length (LEN) -// LEN bytes jpeg data -// (the remaining bytes are unused) -// -class MiniThumbFile { - private static final String TAG = "MiniThumbFile"; - private static final int MINI_THUMB_DATA_FILE_VERSION = 3; - public static final int BYTES_PER_MINTHUMB = 10000; - private static final int HEADER_SIZE = 1 + 8 + 4; - private Uri mUri; - private RandomAccessFile mMiniThumbFile; - - private String randomAccessFilePath(int version) { - String directoryName = - Environment.getExternalStorageDirectory().toString() - + "/DCIM/.thumbnails"; - return directoryName + "/.thumbdata" + version + "-" + mUri.hashCode(); - } - - private void removeOldFile() { - String oldPath = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION - 1); - File oldFile = new File(oldPath); - if (oldFile.exists()) { - try { - oldFile.delete(); - } catch (SecurityException ex) { - // ignore - } - } - } - - private RandomAccessFile miniThumbDataFile() { - if (mMiniThumbFile == null) { - removeOldFile(); - String path = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION); - File directory = new File(path).getParentFile(); - if (!directory.isDirectory()) { - if (!directory.mkdirs()) { - Log.e(TAG, "Unable to create .thumbnails directory " - + directory.toString()); - } - } - File f = new File(path); - try { - mMiniThumbFile = new RandomAccessFile(f, "rw"); - } catch (IOException ex) { - // Open as read-only so we can at least read the existing - // thumbnails. - try { - mMiniThumbFile = new RandomAccessFile(f, "r"); - } catch (IOException ex2) { - // ignore exception - } - } - } - return mMiniThumbFile; - } - - public MiniThumbFile(Uri uri) { - mUri = uri; - } - - public void deactivate() { - if (mMiniThumbFile != null) { - try { - mMiniThumbFile.close(); - mMiniThumbFile = null; - } catch (IOException ex) { - // ignore exception - } - } - } - - // Get the magic number for the specified id in the mini-thumb file. - // Returns 0 if the magic is not available. - public long getMagic(long id) { - // check the mini thumb file for the right data. Right is - // defined as having the right magic number at the offset - // reserved for this "id". - RandomAccessFile r = miniThumbDataFile(); - if (r != null) { - synchronized (r) { - long pos = id * BYTES_PER_MINTHUMB; - try { - // check that we can read the following 9 bytes - // (1 for the "status" and 8 for the long) - if (r.length() >= pos + 1 + 8) { - r.seek(pos); - if (r.readByte() == 1) { - long fileMagic = r.readLong(); - return fileMagic; - } - } - } catch (IOException ex) { - Log.v(TAG, "Got exception checking file magic: ", ex); - } - } - } - return 0; - } - - public void saveMiniThumbToFile(byte[] data, long id, long magic) - throws IOException { - RandomAccessFile r = miniThumbDataFile(); - if (r == null) return; - - long pos = id * BYTES_PER_MINTHUMB; - synchronized (r) { - try { - if (data != null) { - if (data.length > BYTES_PER_MINTHUMB - HEADER_SIZE) { - // not enough space to store it. - return; - } - r.seek(pos); - r.writeByte(0); // we have no data in this slot - - // if magic is 0 then leave it alone - if (magic == 0) { - r.skipBytes(8); - } else { - r.writeLong(magic); - } - r.writeInt(data.length); - r.write(data); - r.seek(pos); - r.writeByte(1); // we have data in this slot - } - } catch (IOException ex) { - Log.e(TAG, "couldn't save mini thumbnail data for " - + id + "; ", ex); - throw ex; - } - } - } - - byte [] getMiniThumbFromFile(long id, byte [] data, long magicCheck) { - RandomAccessFile r = miniThumbDataFile(); - if (r == null) return null; - - long pos = id * BYTES_PER_MINTHUMB; - synchronized (r) { - try { - r.seek(pos); - if (r.readByte() == 1) { - long magic = r.readLong(); - if (magic != magicCheck) { - return null; - } - int length = r.readInt(); - int got = r.read(data, 0, length); - if (got != length) return null; - return data; - } else { - return null; - } - } catch (IOException ex) { - return null; - } - } - } -} diff --git a/src/com/android/camera/gallery/SingleImageList.java b/src/com/android/camera/gallery/SingleImageList.java index b6850aa..8abb306 100644 --- a/src/com/android/camera/gallery/SingleImageList.java +++ b/src/com/android/camera/gallery/SingleImageList.java @@ -70,9 +70,6 @@ public class SingleImageList implements IImageList { return uri.equals(mUri) ? mSingleImage : null; } - public void checkThumbnail(int index) { - } - public void close() { mSingleImage = null; mUri = null; diff --git a/src/com/android/camera/gallery/VideoObject.java b/src/com/android/camera/gallery/VideoObject.java index 39a03e2..75f3148 100644 --- a/src/com/android/camera/gallery/VideoObject.java +++ b/src/com/android/camera/gallery/VideoObject.java @@ -18,9 +18,10 @@ package com.android.camera.gallery; import android.content.ContentResolver; import android.graphics.Bitmap; +import android.media.ThumbnailUtil; import android.net.Uri; - -import com.android.camera.Util; +import android.provider.MediaStore.Video; +import android.util.Log; import java.io.IOException; import java.io.InputStream; @@ -31,7 +32,7 @@ import java.io.InputStream; * path to the actual video data. */ public class VideoObject extends BaseImage implements IImage { - + private static final String TAG = "VideoObject"; /** * Constructor. * @@ -65,7 +66,7 @@ public class VideoObject extends BaseImage implements IImage { @Override public Bitmap fullSizeBitmap(int minSideLength, int maxNumberOfPixels, boolean rotateAsNeeded, boolean useNative) { - return Util.createVideoThumbnail(mDataPath); + return ThumbnailUtil.createVideoThumbnail(mDataPath); } @Override @@ -111,6 +112,18 @@ public class VideoObject extends BaseImage implements IImage { } @Override + public Bitmap miniThumbBitmap() { + try { + long id = mId; + return Video.Thumbnails.getThumbnail(mContentResolver, id, + Video.Thumbnails.MICRO_KIND, null); + } catch (Throwable ex) { + Log.e(TAG, "miniThumbBitmap got exception", ex); + return null; + } + } + + @Override public String toString() { return new StringBuilder("VideoObject").append(mId).toString(); } |