summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
authorRay Chen <raychen@google.com>2009-08-27 17:59:29 -0700
committerRay Chen <raychen@google.com>2009-09-23 11:35:46 -0700
commit9f1480b2c27d744c816c71cb3c512d37bc48c524 (patch)
tree49fd63ada65ba117a259ec1659569596787df4a8 /src/com
parent723f84eadb3c1a79337ef20e4d0a014341537adb (diff)
downloadLegacyCamera-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.java86
-rw-r--r--src/com/android/camera/ImageLoader.java150
-rw-r--r--src/com/android/camera/ImageManager.java9
-rw-r--r--src/com/android/camera/ThumbnailController.java3
-rw-r--r--src/com/android/camera/Util.java85
-rw-r--r--src/com/android/camera/VideoCamera.java5
-rw-r--r--src/com/android/camera/gallery/BaseImage.java53
-rw-r--r--src/com/android/camera/gallery/BaseImageList.java238
-rw-r--r--src/com/android/camera/gallery/DrmImageList.java4
-rw-r--r--src/com/android/camera/gallery/IImageList.java7
-rw-r--r--src/com/android/camera/gallery/Image.java73
-rw-r--r--src/com/android/camera/gallery/ImageList.java4
-rw-r--r--src/com/android/camera/gallery/ImageListUber.java15
-rw-r--r--src/com/android/camera/gallery/MiniThumbFile.java194
-rw-r--r--src/com/android/camera/gallery/SingleImageList.java3
-rw-r--r--src/com/android/camera/gallery/VideoObject.java21
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();
}