diff options
author | Chih-Chung Chang <> | 2009-03-24 19:22:29 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-24 19:22:29 -0700 |
commit | 9bc8d1b47e5bab978cc9571ddfdedc8309a0b65d (patch) | |
tree | c0c2ef2e082481123f124c6a4e72d854f07acbbe /src | |
parent | 5ae6532b568d0be90e5b1b53178e7d7927529201 (diff) | |
download | LegacyCamera-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.java | 225 | ||||
-rwxr-xr-x | src/com/android/camera/ImageManager.java | 9 | ||||
-rw-r--r-- | src/com/android/camera/ThumbnailController.java | 249 | ||||
-rw-r--r-- | src/com/android/camera/VideoCamera.java | 319 |
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(); } } } |