summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChih-Chung Chang <>2009-03-24 19:22:29 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-24 19:22:29 -0700
commit9bc8d1b47e5bab978cc9571ddfdedc8309a0b65d (patch)
treec0c2ef2e082481123f124c6a4e72d854f07acbbe /src
parent5ae6532b568d0be90e5b1b53178e7d7927529201 (diff)
downloadLegacyCamera-9bc8d1b47e5bab978cc9571ddfdedc8309a0b65d.zip
LegacyCamera-9bc8d1b47e5bab978cc9571ddfdedc8309a0b65d.tar.gz
LegacyCamera-9bc8d1b47e5bab978cc9571ddfdedc8309a0b65d.tar.bz2
Automated import from //branches/donutburger/...@141053,141053
Diffstat (limited to 'src')
-rw-r--r--src/com/android/camera/Camera.java225
-rwxr-xr-xsrc/com/android/camera/ImageManager.java9
-rw-r--r--src/com/android/camera/ThumbnailController.java249
-rw-r--r--src/com/android/camera/VideoCamera.java319
4 files changed, 429 insertions, 373 deletions
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index 8cd1756..1e9a3a8 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -16,12 +16,7 @@
package com.android.camera;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -29,7 +24,6 @@ import java.io.OutputStream;
import java.util.ArrayList;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -38,19 +32,10 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.AssetFileDescriptor;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.RectF;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.TransitionDrawable;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.Size;
import android.location.Location;
@@ -79,7 +64,6 @@ import android.view.OrientationEventListener;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
@@ -158,15 +142,6 @@ public class Camera extends Activity implements View.OnClickListener,
private ArrayList<MenuItem> mGalleryItems = new ArrayList<MenuItem>();
- private ImageView mLastPictureButton;
- private LayerDrawable mVignette;
- private Animation mShowLastPictureButtonAnimation = new AlphaAnimation(0F, 1F);
- private boolean mShouldShowLastPictureButton;
- private TransitionDrawable mThumbnailTransition;
- private Drawable[] mThumbnails;
- private boolean mShouldTransitionThumbnails;
- private Uri mLastPictureUri;
- private Bitmap mLastPictureThumb;
private LocationManager mLocationManager = null;
private ShutterButton mShutterButton;
@@ -188,9 +163,12 @@ public class Camera extends Activity implements View.OnClickListener,
private boolean mKeepAndRestartPreview;
- // mPostCaptureAlert is non-null only if isImageCaptureIntent() is true.
+ private boolean mIsImageCaptureIntent;
+ // mPostCaptureAlert, mLastPictureButton, mThumbController
+ // are non-null only if isImageCaptureIntent() is true.
private View mPostCaptureAlert;
-
+ private ImageView mLastPictureButton;
+ private ThumbnailController mThumbController;
private Handler mHandler = new MainHandler();
@@ -480,7 +458,7 @@ public class Camera extends Activity implements View.OnClickListener,
}
public void storeImage(byte[] data, android.hardware.Camera camera, Location loc) {
- boolean captureOnly = isImageCaptureIntent();
+ boolean captureOnly = mIsImageCaptureIntent;
if (!captureOnly) {
storeImage(data, loc);
@@ -618,7 +596,7 @@ public class Camera extends Activity implements View.OnClickListener,
mKeepAndRestartPreview = true;
- boolean getContentAction = isImageCaptureIntent();
+ boolean getContentAction = mIsImageCaptureIntent;
if (getContentAction) {
mImageCapture.initiate(true);
} else {
@@ -637,63 +615,8 @@ public class Camera extends Activity implements View.OnClickListener,
private void setLastPictureThumb(byte[] data, Uri uri) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 16;
-
Bitmap lastPictureThumb = BitmapFactory.decodeByteArray(data, 0, data.length, options);
-
- setLastPictureThumb(lastPictureThumb, uri);
- }
-
- private static Bitmap makeRoundedCorner(Bitmap thumb, int rx, int ry) {
- if (thumb == null) return null;
- int width = thumb.getWidth();
- int height = thumb.getHeight();
-
- Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setStyle(Paint.Style.FILL);
- canvas.drawRoundRect(new RectF(0, 0, width, height), rx, ry, paint);
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(thumb, 0, 0, paint);
- return result;
- }
-
- private void setLastPictureThumb(Bitmap lastPictureThumb, Uri uri) {
-
- final int PADDING_WIDTH = 6;
- final int PADDING_HEIGHT = 6;
- LayoutParams layoutParams = mLastPictureButton.getLayoutParams();
- // Make the mini-thumbnail size smaller than the button size so that the image corners
- // don't peek out from the rounded corners of the frame_thumbnail graphic:
- final int miniThumbWidth = layoutParams.width - 2 * PADDING_WIDTH;
- final int miniThumbHeight = layoutParams.height - 2 * PADDING_HEIGHT;
- mLastPictureThumb = ImageManager.extractMiniThumb(
- lastPictureThumb, miniThumbWidth, miniThumbHeight);
- lastPictureThumb = makeRoundedCorner(mLastPictureThumb, 3, 3);
-
- Drawable[] vignetteLayers = new Drawable[2];
- vignetteLayers[0] = getResources().getDrawable(R.drawable.frame_thumbnail);
- if (mThumbnails == null) {
- mThumbnails = new Drawable[2];
- mThumbnails[1] = new BitmapDrawable(lastPictureThumb);
- vignetteLayers[1] = mThumbnails[1];
- } else {
- mThumbnails[0] = mThumbnails[1];
- mThumbnails[1] = new BitmapDrawable(lastPictureThumb);
- mThumbnailTransition = new TransitionDrawable(mThumbnails);
- mShouldTransitionThumbnails = true;
- vignetteLayers[1] = mThumbnailTransition;
- }
-
- mVignette = new LayerDrawable(vignetteLayers);
- mVignette.setLayerInset(1, PADDING_WIDTH, PADDING_HEIGHT,
- PADDING_WIDTH, PADDING_HEIGHT);
- mLastPictureButton.setImageDrawable(mVignette);
-
- if (mLastPictureButton.getVisibility() != View.VISIBLE) {
- mShouldShowLastPictureButton = true;
- }
- mLastPictureUri = uri;
+ mThumbController.setData(uri, lastPictureThumb);
}
static private String createName(long dateTaken) {
@@ -769,10 +692,15 @@ public class Camera extends Activity implements View.OnClickListener,
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- if (!isImageCaptureIntent()) {
+ mIsImageCaptureIntent = isImageCaptureIntent();
+
+ if (!mIsImageCaptureIntent) {
mLastPictureButton = (ImageView) findViewById(R.id.last_picture_button);
mLastPictureButton.setOnClickListener(this);
- loadLastThumb();
+ Drawable frame = getResources().getDrawable(R.drawable.frame_thumbnail);
+ mThumbController = new ThumbnailController(mLastPictureButton,
+ frame, mContentResolver);
+ mThumbController.loadData(ImageManager.getLastImageThumbPath());
}
mShutterButton = (ShutterButton) findViewById(R.id.shutter_button);
@@ -800,7 +728,7 @@ public class Camera extends Activity implements View.OnClickListener,
mFocusBlinkAnimation.setRepeatMode(Animation.REVERSE);
// We load the post_picture_panel layout only if it is needed.
- if (isImageCaptureIntent()) {
+ if (mIsImageCaptureIntent) {
ViewGroup cameraView = (ViewGroup)findViewById(R.id.camera);
getLayoutInflater().inflate(R.layout.post_picture_panel,
cameraView);
@@ -1049,78 +977,10 @@ public class Camera extends Activity implements View.OnClickListener,
}
}
- private ImageManager.DataLocation dataLocation() {
+ private static ImageManager.DataLocation dataLocation() {
return ImageManager.DataLocation.EXTERNAL;
}
- private static final int BUFSIZE = 4096;
-
- // Stores the thumbnail and URI of last-picture-taken to SD card, so we can
- // load it the next time the Camera app starts.
- private void storeLastThumb() {
- if (mLastPictureUri != null && mLastPictureThumb != null) {
- try {
- FileOutputStream f = new FileOutputStream(ImageManager.getLastThumbPath());
- try {
- BufferedOutputStream b = new BufferedOutputStream(f, BUFSIZE);
- try {
- DataOutputStream d = new DataOutputStream(b);
- try {
- d.writeUTF(mLastPictureUri.toString());
- mLastPictureThumb.compress(Bitmap.CompressFormat.PNG, 100, d);
- } finally {
- d.close();
- b = null;
- f = null;
- }
- } finally {
- if (b != null) {
- b.close();
- f = null;
- }
- }
- } finally {
- if (f != null) {
- f.close();
- }
- }
- } catch (IOException e) {
- }
- }
- }
-
- // Loads the thumbnail and URI of last-picture-taken from SD card.
- private void loadLastThumb() {
- try {
- FileInputStream f = new FileInputStream(ImageManager.getLastThumbPath());
- try {
- BufferedInputStream b = new BufferedInputStream(f, BUFSIZE);
- try {
- DataInputStream d = new DataInputStream(b);
- try {
- Uri lastUri = Uri.parse(d.readUTF());
- Bitmap lastThumb = BitmapFactory.decodeStream(d);
- setLastPictureThumb(lastThumb, lastUri);
- } finally {
- d.close();
- b = null;
- f = null;
- }
- } finally {
- if (b != null) {
- b.close();
- f = null;
- }
- }
- } finally {
- if (f != null) {
- f.close();
- }
- }
- } catch (IOException e) {
- }
- }
-
@Override
public void onStop() {
keep();
@@ -1153,7 +1013,10 @@ public class Camera extends Activity implements View.OnClickListener,
mFocusToneGenerator = null;
}
- storeLastThumb();
+ if (!mIsImageCaptureIntent) {
+ mThumbController.storeData(ImageManager.getLastImageThumbPath());
+ }
+
if (mStorageHint != null) {
mStorageHint.cancel();
mStorageHint = null;
@@ -1347,19 +1210,6 @@ public class Camera extends Activity implements View.OnClickListener,
return mCameraDevice != null;
}
- private boolean isLastPictureValid() {
- boolean isValid = true;
- if (mLastPictureUri == null) return false;
- try {
- mContentResolver.openFileDescriptor(mLastPictureUri, "r").close();
- }
- catch (Exception ex) {
- isValid = false;
- Log.e(TAG, ex.toString());
- }
- return isValid;
- }
-
private void updateLastImage() {
ImageManager.IImageList list = ImageManager.instance().allImages(
this,
@@ -1371,12 +1221,10 @@ public class Camera extends Activity implements View.OnClickListener,
int count = list.getCount();
if (count > 0) {
ImageManager.IImage image = list.getImageAt(count-1);
- mLastPictureUri = image.fullSizeImageUri();
- Log.v(TAG, "updateLastImage: count="+ count +
- ", lastPictureUri="+mLastPictureUri);
- setLastPictureThumb(image.miniThumbBitmap(), mLastPictureUri);
+ Uri uri = image.fullSizeImageUri();
+ mThumbController.setData(uri, image.miniThumbBitmap());
} else {
- mLastPictureUri = null;
+ mThumbController.setData(null, null);
}
list.deactivate();
}
@@ -1396,21 +1244,12 @@ public class Camera extends Activity implements View.OnClickListener,
// let the user take a picture, and delete that file if needed to save the new photo.
calculatePicturesRemaining();
- if (!isImageCaptureIntent() && !isLastPictureValid()) {
+ if (!mIsImageCaptureIntent && !mThumbController.isUriValid()) {
updateLastImage();
}
- if (mShouldShowLastPictureButton) {
- mShouldShowLastPictureButton = false;
- mLastPictureButton.setVisibility(View.VISIBLE);
- Animation a = mShowLastPictureButtonAnimation;
- a.setDuration(500);
- mLastPictureButton.setAnimation(a);
- }
-
- if (mShouldTransitionThumbnails) {
- mShouldTransitionThumbnails = false;
- mThumbnailTransition.startTransition(500);
+ if (!mIsImageCaptureIntent) {
+ mThumbController.updateDisplayIfNeeded();
}
}
@@ -1553,8 +1392,8 @@ public class Camera extends Activity implements View.OnClickListener,
}
private void viewLastImage() {
- Uri targetUri = mLastPictureUri;
- if (targetUri != null && isLastPictureValid()) {
+ if (mThumbController.isUriValid()) {
+ Uri targetUri = mThumbController.getUri();
targetUri = targetUri.buildUpon().
appendQueryParameter("bucketId", ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
Intent intent = new Intent(Intent.ACTION_VIEW, targetUri);
@@ -1703,7 +1542,7 @@ public class Camera extends Activity implements View.OnClickListener,
}
private void showPostCaptureAlert() {
- if (isImageCaptureIntent()) {
+ if (mIsImageCaptureIntent) {
mPostCaptureAlert.setVisibility(View.VISIBLE);
int[] pickIds = {R.id.attach, R.id.cancel};
for(int id : pickIds) {
@@ -1717,7 +1556,7 @@ public class Camera extends Activity implements View.OnClickListener,
}
private void hidePostCaptureAlert() {
- if (isImageCaptureIntent()) {
+ if (mIsImageCaptureIntent) {
mPostCaptureAlert.setVisibility(View.INVISIBLE);
}
}
@@ -1726,7 +1565,7 @@ public class Camera extends Activity implements View.OnClickListener,
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- if (isImageCaptureIntent()) {
+ if (mIsImageCaptureIntent) {
// No options menu for attach mode.
return false;
} else {
diff --git a/src/com/android/camera/ImageManager.java b/src/com/android/camera/ImageManager.java
index d7cfc2b..073b8db 100755
--- a/src/com/android/camera/ImageManager.java
+++ b/src/com/android/camera/ImageManager.java
@@ -4197,8 +4197,13 @@ public class ImageManager {
return bitmap;
}
- public static String getLastThumbPath() {
+ public static String getLastImageThumbPath() {
return Environment.getExternalStorageDirectory().toString() +
- "/DCIM/.thumbnails/camera_last_thumb";
+ "/DCIM/.thumbnails/image_last_thumb";
+ }
+
+ public static String getLastVideoThumbPath() {
+ return Environment.getExternalStorageDirectory().toString() +
+ "/DCIM/.thumbnails/video_last_thumb";
}
}
diff --git a/src/com/android/camera/ThumbnailController.java b/src/com/android/camera/ThumbnailController.java
new file mode 100644
index 0000000..7588c3d
--- /dev/null
+++ b/src/com/android/camera/ThumbnailController.java
@@ -0,0 +1,249 @@
+/*
+ * 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;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import android.content.ContentResolver;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.net.Uri;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.ImageView;
+
+// A controller shows thumbnail picture on a button. The thumbnail picture
+// corresponds to a URI of the original picture/video. The thumbnail bitmap
+// and the URI can be saved to a file (and later loaded from it).
+//
+// public ThumbnailController(ImageView button)
+// public void setData(Uri uri, Bitmap original)
+// public void updateDisplayIfNeeded()
+// public Uri getUri()
+// public Bitmap getThumb()
+// public boolean storeData(String filePath)
+// public boolean loadData(String filePath)
+//
+
+public class ThumbnailController {
+ private static final String TAG = "ThumbnailController";
+ private ContentResolver mContentResolver;
+ private Uri mUri;
+ private Bitmap mThumb;
+ private ImageView mButton;
+ private Drawable mFrame;
+ private Drawable[] mThumbs;
+ private TransitionDrawable mThumbTransition;
+ private boolean mShouldAnimateThumb;
+ private Animation mShowButtonAnimation = new AlphaAnimation(0F, 1F);
+ private boolean mShouldAnimateButton;
+
+ // The "frame" is a drawable we want to put on top of the thumbnail.
+ public ThumbnailController(ImageView button, Drawable frame,
+ ContentResolver contentResolver) {
+ mButton = button;
+ mFrame = frame;
+ mContentResolver = contentResolver;
+ mShowButtonAnimation.setDuration(500);
+ }
+
+ public void setData(Uri uri, Bitmap original) {
+ // Make sure uri and original are consistently both null or both non-null.
+ if (uri == null || original == null) {
+ uri = null;
+ original = null;
+ }
+ mUri = uri;
+ updateThumb(original);
+ }
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ public Bitmap getThumb() {
+ return mThumb;
+ }
+
+ private static final int BUFSIZE = 4096;
+
+ // Stores the data from the specified file.
+ // Returns true for success.
+ public boolean storeData(String filePath) {
+ if (mUri == null) {
+ return false;
+ }
+
+ FileOutputStream f = null;
+ BufferedOutputStream b = null;
+ DataOutputStream d = null;
+ try {
+ f = new FileOutputStream(filePath);
+ b = new BufferedOutputStream(f, BUFSIZE);
+ d = new DataOutputStream(b);
+ d.writeUTF(mUri.toString());
+ mThumb.compress(Bitmap.CompressFormat.PNG, 100, d);
+ d.close();
+ } catch (IOException e) {
+ return false;
+ } finally {
+ closeSilently(f);
+ closeSilently(b);
+ closeSilently(d);
+ }
+ return true;
+ }
+
+ // Loads the data from the specified file.
+ // Returns true for success.
+ public boolean loadData(String filePath) {
+ FileInputStream f = null;
+ BufferedInputStream b = null;
+ DataInputStream d = null;
+ try {
+ f = new FileInputStream(filePath);
+ b = new BufferedInputStream(f, BUFSIZE);
+ d = new DataInputStream(b);
+ Uri uri = Uri.parse(d.readUTF());
+ Bitmap thumb = BitmapFactory.decodeStream(d);
+ setData(uri, thumb);
+ d.close();
+ } catch (IOException e) {
+ return false;
+ } finally {
+ closeSilently(f);
+ closeSilently(b);
+ closeSilently(d);
+ }
+ return true;
+ }
+
+ private void closeSilently(Closeable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ public void updateDisplayIfNeeded() {
+ if (mUri == null) {
+ mButton.setVisibility(View.INVISIBLE);
+ return;
+ }
+
+ if (mShouldAnimateButton) {
+ mButton.setVisibility(View.VISIBLE);
+ mButton.startAnimation(mShowButtonAnimation);
+ mShouldAnimateButton = false;
+ }
+
+ if (mShouldAnimateThumb) {
+ mThumbTransition.startTransition(500);
+ mShouldAnimateThumb = false;
+ }
+ }
+
+ private void updateThumb(Bitmap original) {
+ if (original == null) {
+ mThumb = null;
+ mThumbs = null;
+ return;
+ }
+
+ // Make the mini-thumb size smaller than the button size so that the image corners
+ // don't peek out from the rounded corners of the frame_thumb graphic:
+ final int PADDING_WIDTH = 6;
+ final int PADDING_HEIGHT = 6;
+ LayoutParams layoutParams = mButton.getLayoutParams();
+ final int miniThumbWidth = layoutParams.width - 2 * PADDING_WIDTH;
+ final int miniThumbHeight = layoutParams.height - 2 * PADDING_HEIGHT;
+ mThumb = ImageManager.extractMiniThumb(original,
+ miniThumbWidth, miniThumbHeight, false);
+ mThumb = makeRoundedCorner(mThumb, 3, 3);
+
+ Drawable[] vignetteLayers = new Drawable[2];
+ vignetteLayers[0] = mFrame;
+ if (mThumbs == null) {
+ mThumbs = new Drawable[2];
+ mThumbs[1] = new BitmapDrawable(mThumb);
+ vignetteLayers[1] = mThumbs[1];
+ mShouldAnimateThumb = false;
+ } else {
+ mThumbs[0] = mThumbs[1];
+ mThumbs[1] = new BitmapDrawable(mThumb);
+ mThumbTransition = new TransitionDrawable(mThumbs);
+ vignetteLayers[1] = mThumbTransition;
+ mShouldAnimateThumb = true;
+ }
+
+ LayerDrawable mVignette = new LayerDrawable(vignetteLayers);
+ mVignette.setLayerInset(1, PADDING_WIDTH, PADDING_HEIGHT,
+ PADDING_WIDTH, PADDING_HEIGHT);
+ mButton.setImageDrawable(mVignette);
+
+ if (mButton.getVisibility() != View.VISIBLE) {
+ mShouldAnimateButton = true;
+ }
+ }
+
+ private static Bitmap makeRoundedCorner(Bitmap thumb, int rx, int ry) {
+ if (thumb == null) return null;
+ int width = thumb.getWidth();
+ int height = thumb.getHeight();
+
+ Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(result);
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawRoundRect(new RectF(0, 0, width, height), rx, ry, paint);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(thumb, 0, 0, paint);
+ return result;
+ }
+
+ public boolean isUriValid() {
+ if (mUri == null) return false;
+ try {
+ mContentResolver.openFileDescriptor(mUri, "r").close();
+ } catch (Exception ex) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java
index 0d6e74d..11d1a4e 100644
--- a/src/com/android/camera/VideoCamera.java
+++ b/src/com/android/camera/VideoCamera.java
@@ -19,14 +19,11 @@ package com.android.camera;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import android.app.Activity;
-import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -34,21 +31,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.RectF;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.TransitionDrawable;
import android.location.LocationManager;
-import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Bundle;
@@ -67,7 +53,6 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.View;
-import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.view.MenuItem.OnMenuItemClickListener;
@@ -84,8 +69,6 @@ public class VideoCamera extends Activity implements View.OnClickListener,
private static final boolean DEBUG = true;
private static final boolean DEBUG_SUPPRESS_AUDIO_RECORDING = DEBUG && false;
- private static final boolean DEBUG_DO_NOT_REUSE_MEDIA_RECORDER = DEBUG && true;
- private static final boolean DEBUG_LOG_APP_LIFECYCLE = DEBUG && false;
private static final int CLEAR_SCREEN_DELAY = 4;
private static final int UPDATE_RECORD_TIME = 5;
@@ -114,14 +97,13 @@ public class VideoCamera extends Activity implements View.OnClickListener,
private static final float VIDEO_ASPECT_RATIO = 176.0f / 144.0f;
VideoPreview mVideoPreview;
SurfaceHolder mSurfaceHolder = null;
- ImageView mBlackout = null;
ImageView mVideoFrame;
- Bitmap mVideoFrameBitmap;
- private TransitionDrawable mThumbnailTransition;
- private Drawable[] mThumbnails;
- private boolean mShouldTransitionThumbnails;
+
+ private boolean mIsVideoCaptureIntent;
+ // mLastPictureButton and mThumbController
+ // are non-null only if isVideoCaptureIntent() is true;
private ImageView mLastPictureButton;
- private Bitmap mLastPictureThumb;
+ private ThumbnailController mThumbController;
private static final int MAX_RECORDING_DURATION_MS = 10 * 60 * 1000;
@@ -268,9 +250,6 @@ public class VideoCamera extends Activity implements View.OnClickListener,
/** Called with the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
- if (DEBUG_LOG_APP_LIFECYCLE) {
- Log.v(TAG, "onCreate " + this.hashCode());
- }
super.onCreate(icicle);
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
@@ -292,9 +271,6 @@ public class VideoCamera extends Activity implements View.OnClickListener,
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- mBlackout = (ImageView) findViewById(R.id.blackout);
- mBlackout.setBackgroundDrawable(new ColorDrawable(0xFF000000));
-
mPostPictureAlert = findViewById(R.id.post_picture_panel);
int[] ids = new int[]{R.id.play, R.id.share, R.id.discard,
@@ -307,8 +283,15 @@ public class VideoCamera extends Activity implements View.OnClickListener,
mShutterButton.setOnShutterButtonListener(this);
mRecordingTimeView = (TextView) findViewById(R.id.recording_time);
mVideoFrame = (ImageView) findViewById(R.id.video_frame);
- mLastPictureButton = (ImageView) findViewById(R.id.last_picture_button);
- mLastPictureButton.setOnClickListener(this);
+ mIsVideoCaptureIntent = isVideoCaptureIntent();
+ if (!mIsVideoCaptureIntent) {
+ mLastPictureButton = (ImageView) findViewById(R.id.last_picture_button);
+ mLastPictureButton.setOnClickListener(this);
+ Drawable frame = getResources().getDrawable(R.drawable.frame_thumbnail);
+ mThumbController = new ThumbnailController(mLastPictureButton,
+ frame, mContentResolver);
+ mThumbController.loadData(ImageManager.getLastVideoThumbPath());
+ }
}
private void startShareVideoActivity() {
@@ -359,7 +342,7 @@ public class VideoCamera extends Activity implements View.OnClickListener,
}
case R.id.last_picture_button: {
- stopPreviewAndShowAlert();
+ stopVideoRecordingAndShowAlert();
break;
}
}
@@ -373,15 +356,18 @@ public class VideoCamera extends Activity implements View.OnClickListener,
switch (button.getId()) {
case R.id.shutter_button:
if (mMediaRecorderRecording) {
- if (isVideoCaptureIntent()) {
+ if (mIsVideoCaptureIntent) {
stopVideoRecordingAndShowAlert();
} else {
- stopVideoRecordingAndShowThumbnail();
- doStartCaptureMode();
+ stopVideoRecordingAndGetThumbnail();
+ initializeVideo();
+ }
+ } else if (isAlertVisible()) {
+ if (mIsVideoCaptureIntent) {
+ discardCurrentVideoAndStartPreview();
+ } else {
+ hideAlertAndStartVideoRecording();
}
- } else if (mVideoFrame.getVisibility() == View.VISIBLE) {
- doStartCaptureMode();
- startVideoRecording();
} else {
startVideoRecording();
}
@@ -389,14 +375,6 @@ public class VideoCamera extends Activity implements View.OnClickListener,
}
}
- private void doStartCaptureMode() {
- if (isVideoCaptureIntent()) {
- discardCurrentVideoAndStartPreview();
- } else {
- hideVideoFrameAndStartPreview();
- }
- }
-
private void doPlayCurrentVideo() {
Log.e(TAG, "Playing current video: " + mCurrentVideoUri);
Intent intent = new Intent(Intent.ACTION_VIEW, mCurrentVideoUri);
@@ -409,7 +387,7 @@ public class VideoCamera extends Activity implements View.OnClickListener,
private void discardCurrentVideoAndStartPreview() {
deleteCurrentVideo();
- hideVideoFrameAndStartPreview();
+ hideAlertAndStartPreview();
}
private OnScreenHint mStorageHint;
@@ -452,9 +430,6 @@ public class VideoCamera extends Activity implements View.OnClickListener,
@Override
public void onResume() {
- if (DEBUG_LOG_APP_LIFECYCLE) {
- Log.v(TAG, "onResume " + this.hashCode());
- }
super.onResume();
setScreenTimeoutLong();
@@ -477,40 +452,40 @@ public class VideoCamera extends Activity implements View.OnClickListener,
}
}, 200);
- mBlackout.setVisibility(View.INVISIBLE);
- if (mVideoFrameBitmap == null) {
- initializeVideo();
- } else {
- showVideoFrame();
- }
+ initializeVideo();
}
@Override
public void onStop() {
- if (DEBUG_LOG_APP_LIFECYCLE) {
- Log.v(TAG, "onStop " + this.hashCode());
- }
- stopVideoRecording();
setScreenTimeoutSystemDefault();
super.onStop();
}
@Override
protected void onPause() {
- if (DEBUG_LOG_APP_LIFECYCLE) {
- Log.v(TAG, "onPause " + this.hashCode());
- }
super.onPause();
- stopVideoRecording();
- hideVideoFrame();
+ // This is similar to what mShutterButton.performClick() does,
+ // but not quite the same.
+ if (mMediaRecorderRecording) {
+ if (mIsVideoCaptureIntent) {
+ stopVideoRecordingAndShowAlert();
+ } else {
+ stopVideoRecordingAndGetThumbnail();
+ }
+ } else {
+ stopVideoRecording();
+ }
mPausing = true;
unregisterReceiver(mReceiver);
- mBlackout.setVisibility(View.VISIBLE);
setScreenTimeoutSystemDefault();
+ if (!mIsVideoCaptureIntent) {
+ mThumbController.storeData(ImageManager.getLastVideoThumbPath());
+ }
+
if (mStorageHint != null) {
mStorageHint.cancel();
mStorageHint = null;
@@ -526,8 +501,8 @@ public class VideoCamera extends Activity implements View.OnClickListener,
if (mMediaRecorderRecording) {
mShutterButton.performClick();
return true;
- } else if(isPostRecordingAlertVisible()) {
- hideVideoFrameAndStartPreview();
+ } else if(isAlertVisible()) {
+ hideAlertAndStartPreview();
return true;
}
break;
@@ -599,7 +574,7 @@ public class VideoCamera extends Activity implements View.OnClickListener,
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- if (isVideoCaptureIntent()) {
+ if (mIsVideoCaptureIntent) {
// No options menu for attach mode.
return false;
} else {
@@ -685,14 +660,18 @@ public class VideoCamera extends Activity implements View.OnClickListener,
}
}
+ // initializeVideo() starts preview and prepare media recorder.
// Returns false if initializeVideo fails
private boolean initializeVideo() {
Log.v(TAG, "initializeVideo");
- boolean isCaptureIntent = isVideoCaptureIntent();
+
+ // We will call initializeVideo() again when the alert is hidden.
+ if (isAlertVisible()) return false;
+
Intent intent = getIntent();
Bundle myExtras = intent.getExtras();
- if (isCaptureIntent && myExtras != null) {
+ if (mIsVideoCaptureIntent && myExtras != null) {
Uri saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
if (saveUri != null) {
try {
@@ -782,6 +761,15 @@ public class VideoCamera extends Activity implements View.OnClickListener,
return false;
}
mMediaRecorderRecording = false;
+
+ if (!mIsVideoCaptureIntent && !mThumbController.isUriValid()) {
+ updateLastVideo();
+ }
+
+ if (!mIsVideoCaptureIntent) {
+ mThumbController.updateDisplayIfNeeded();
+ }
+
return true;
}
@@ -795,25 +783,6 @@ public class VideoCamera extends Activity implements View.OnClickListener,
}
}
- private void restartPreview() {
- if (DEBUG_DO_NOT_REUSE_MEDIA_RECORDER) {
- Log.v(TAG, "DEBUG_DO_NOT_REUSE_MEDIA_RECORDER recreating mMediaRecorder.");
- initializeVideo();
- } else {
- try {
- mMediaRecorder.prepare();
- } catch (IOException exception) {
- Log.e(TAG, "prepare failed for " + mCameraVideoFilename);
- releaseMediaRecorder();
- // TODO: add more exception handling logic here
- }
- }
- if (mShouldTransitionThumbnails) {
- mShouldTransitionThumbnails = false;
- mThumbnailTransition.startTransition(500);
- }
- }
-
private int getIntPreference(String key, int defaultValue) {
String s = mPreferences.getString(key, "");
int result = defaultValue;
@@ -982,7 +951,6 @@ public class VideoCamera extends Activity implements View.OnClickListener,
mHandler.sendEmptyMessage(UPDATE_RECORD_TIME);
setScreenTimeoutInfinite();
hideLastPictureButton();
- recycleVideoFrameBitmap();
}
}
@@ -993,37 +961,23 @@ public class VideoCamera extends Activity implements View.OnClickListener,
mShutterButton.setImageDrawable(drawable);
}
- private void stopVideoRecordingAndShowThumbnail() {
- Log.v(TAG, "stopVideoRecordingAndShowThumbnail");
- if (mMediaRecorderRecording) {
- stopVideoRecording();
- acquireVideoFrame();
- setLastPictureThumb(mVideoFrameBitmap);
- showLastPictureButton();
- }
+ private void stopVideoRecordingAndGetThumbnail() {
+ stopVideoRecording();
+ acquireVideoThumb();
}
private void stopVideoRecordingAndShowAlert() {
- Log.v(TAG, "stopVideoRecordingAndShowAlert");
- if (mMediaRecorderRecording) {
- stopVideoRecording();
- acquireVideoFrame();
- showVideoFrame();
- }
- }
-
- private void stopPreviewAndShowAlert() {
stopVideoRecording();
- showVideoFrame();
+ showAlert();
}
- private void showVideoFrame() {
+ private void showAlert() {
int[] pickIds = {R.id.attach, R.id.cancel};
int[] normalIds = {R.id.gallery, R.id.share, R.id.discard};
int[] alwaysOnIds = {R.id.play};
int[] hideIds = pickIds;
int[] connectIds = normalIds;
- if (isVideoCaptureIntent()) {
+ if (mIsVideoCaptureIntent) {
hideIds = normalIds;
connectIds = pickIds;
}
@@ -1037,11 +991,31 @@ public class VideoCamera extends Activity implements View.OnClickListener,
connectAndFadeIn(connectIds);
connectAndFadeIn(alwaysOnIds);
hideLastPictureButton();
- mVideoFrame.setVisibility(View.VISIBLE);
mPostPictureAlert.setVisibility(View.VISIBLE);
+
+ // There are two cases we are here:
+ // (1) We are in a capture video intent, and we are reviewing the video
+ // we just taken.
+ // (2) The thumbnail button is clicked: we review the video associated
+ // with the thumbnail.
+ // For the second case, we copy the associated URI and filename to
+ // mCurrentVideoUri and mCurrentVideoFilename, so the video frame shown
+ // and the target for actions (play, delete, ...) will be correct.
+
+ if (!mIsVideoCaptureIntent) {
+ mCurrentVideoUri = mThumbController.getUri();
+ mCurrentVideoFilename = getDataPath(mCurrentVideoUri);
+ }
+
+ String path = mCurrentVideoFilename;
+ if (path != null) {
+ Bitmap videoFrame = ImageManager.createVideoThumbnail(path);
+ mVideoFrame.setImageBitmap(videoFrame);
+ mVideoFrame.setVisibility(View.VISIBLE);
+ }
}
- private void hideVideoFrame() {
+ private void hideAlert() {
mVideoFrame.setVisibility(View.INVISIBLE);
mPostPictureAlert.setVisibility(View.INVISIBLE);
showLastPictureButton();
@@ -1053,11 +1027,11 @@ public class VideoCamera extends Activity implements View.OnClickListener,
view.setOnClickListener(this);
Animation animation = new AlphaAnimation(0F, 1F);
animation.setDuration(500);
- view.setAnimation(animation);
+ view.startAnimation(animation);
}
}
- private boolean isPostRecordingAlertVisible() {
+ private boolean isAlertVisible() {
return mPostPictureAlert.getVisibility() == View.VISIBLE;
}
@@ -1130,82 +1104,71 @@ public class VideoCamera extends Activity implements View.OnClickListener,
}
}
- private void hideVideoFrameAndStartPreview() {
- hideVideoFrame();
- restartPreview();
+ private void hideAlertAndStartPreview() {
+ hideAlert();
+ initializeVideo();
}
- private void acquireVideoFrame() {
- recycleVideoFrameBitmap();
- mVideoFrameBitmap = ImageManager.createVideoThumbnail(mCurrentVideoFilename);
- mVideoFrame.setImageBitmap(mVideoFrameBitmap);
- Log.v(TAG, "acquireVideoFrame:" + mVideoFrameBitmap);
+ private void hideAlertAndStartVideoRecording() {
+ hideAlert();
+ startVideoRecording();
}
- //TODO: Refactor the code so that the following code is shared between
- // VideoCamera.java and Camera.java
- private static Bitmap makeRoundedCorner(Bitmap thumb, int rx, int ry) {
- if (thumb == null) return null;
- int width = thumb.getWidth();
- int height = thumb.getHeight();
-
- Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setStyle(Paint.Style.FILL);
- canvas.drawRoundRect(new RectF(0, 0, width, height), rx, ry, paint);
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(thumb, 0, 0, paint);
- return result;
+ private void acquireVideoThumb() {
+ Bitmap videoFrame = ImageManager.createVideoThumbnail(mCurrentVideoFilename);
+ mThumbController.setData(mCurrentVideoUri, videoFrame);
}
- private void setLastPictureThumb(Bitmap videoFrame) {
-
- final int PADDING_WIDTH = 6;
- final int PADDING_HEIGHT = 6;
- LayoutParams layoutParams = mLastPictureButton.getLayoutParams();
- // Make the mini-thumbnail size smaller than the button size so that the image corners
- // don't peek out from the rounded corners of the frame_thumbnail graphic:
- final int miniThumbWidth = layoutParams.width - 2 * PADDING_WIDTH;
- final int miniThumbHeight = layoutParams.height - 2 * PADDING_HEIGHT;
-
- Bitmap lastPictureThumb = ImageManager.extractMiniThumb(videoFrame,
- miniThumbWidth, miniThumbHeight, false);
- lastPictureThumb = makeRoundedCorner(lastPictureThumb, 3, 3);
-
- Drawable[] vignetteLayers = new Drawable[2];
- vignetteLayers[0] = getResources().getDrawable(R.drawable.frame_thumbnail);
- if (mThumbnails == null) {
- mThumbnails = new Drawable[2];
- mThumbnails[1] = new BitmapDrawable(lastPictureThumb);
- vignetteLayers[1] = mThumbnails[1];
- } else {
- mThumbnails[0] = mThumbnails[1];
- mThumbnails[1] = new BitmapDrawable(lastPictureThumb);
- mThumbnailTransition = new TransitionDrawable(mThumbnails);
- mShouldTransitionThumbnails = true;
- vignetteLayers[1] = mThumbnailTransition;
+ private void showLastPictureButton() {
+ if (!mIsVideoCaptureIntent) {
+ mLastPictureButton.setVisibility(View.VISIBLE);
}
+ }
- LayerDrawable vignette = new LayerDrawable(vignetteLayers);
- vignette.setLayerInset(1, PADDING_WIDTH, PADDING_HEIGHT,
- PADDING_WIDTH, PADDING_HEIGHT);
- mLastPictureButton.setImageDrawable(vignette);
+ private void hideLastPictureButton() {
+ if (!mIsVideoCaptureIntent) {
+ mLastPictureButton.setVisibility(View.INVISIBLE);
+ }
}
- private void showLastPictureButton() {
- mLastPictureButton.setVisibility(View.VISIBLE);
+ private static ImageManager.DataLocation dataLocation() {
+ return ImageManager.DataLocation.EXTERNAL;
}
- private void hideLastPictureButton() {
- mLastPictureButton.setVisibility(View.INVISIBLE);
+ private void updateLastVideo() {
+ ImageManager.IImageList list = ImageManager.instance().allImages(
+ this,
+ mContentResolver,
+ dataLocation(),
+ ImageManager.INCLUDE_VIDEOS,
+ ImageManager.SORT_ASCENDING,
+ ImageManager.CAMERA_IMAGE_BUCKET_ID);
+ int count = list.getCount();
+ if (count > 0) {
+ ImageManager.IImage image = list.getImageAt(count-1);
+ Uri uri = image.fullSizeImageUri();
+ mThumbController.setData(uri, image.miniThumbBitmap());
+ } else {
+ mThumbController.setData(null, null);
+ }
+ list.deactivate();
}
- private void recycleVideoFrameBitmap() {
- if (mVideoFrameBitmap != null) {
- mVideoFrame.setImageDrawable(null);
- mVideoFrameBitmap.recycle();
- mVideoFrameBitmap = null;
+ private static final String[] DATA_PATH_PROJECTION = new String[] {
+ "_data"
+ };
+
+ private String getDataPath(Uri uri) {
+ Cursor c = null;
+ try {
+ c = mContentResolver.query(uri, DATA_PATH_PROJECTION, null, null, null);
+ if (c != null && c.moveToFirst()) {
+ return c.getString(0);
+ } else {
+ return null;
+ }
+ } finally {
+ if (c != null) c.close();
}
}
}