summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-01-15 16:12:13 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-01-15 16:12:13 -0800
commit9c061072c8f4ec16acf25e0af7ca3d8317d1026f (patch)
treeed8b90b59a2688e918680d1fe530647a2c1e75b4 /src
parentb97ccf3f20bee44daf70f10966809e39e30ab4f7 (diff)
downloadLegacyCamera-9c061072c8f4ec16acf25e0af7ca3d8317d1026f.zip
LegacyCamera-9c061072c8f4ec16acf25e0af7ca3d8317d1026f.tar.gz
LegacyCamera-9c061072c8f4ec16acf25e0af7ca3d8317d1026f.tar.bz2
auto import from //branches/cupcake/...@126645
Diffstat (limited to 'src')
-rw-r--r--src/com/android/camera/Camera.java91
-rw-r--r--src/com/android/camera/CropImage.java161
-rw-r--r--src/com/android/camera/GalleryPicker.java25
-rw-r--r--src/com/android/camera/MenuHelper.java71
-rw-r--r--src/com/android/camera/MovieView.java97
-rw-r--r--src/com/android/camera/VideoCamera.java136
6 files changed, 356 insertions, 225 deletions
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index 993e15c..ed2f7a4 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -94,9 +94,6 @@ public class Camera extends Activity implements View.OnClickListener, SurfaceHol
private static final int SCREEN_DELAY = 2 * 60 * 1000;
private static final int FOCUS_BEEP_VOLUME = 100;
- private static final int NO_STORAGE_ERROR = -1;
- private static final int CANNOT_STAT_ERROR = -2;
-
public static final int MENU_SWITCH_TO_VIDEO = 0;
public static final int MENU_SWITCH_TO_CAMERA = 1;
public static final int MENU_FLASH_SETTING = 2;
@@ -742,6 +739,8 @@ public class Camera extends Activity implements View.OnClickListener, SurfaceHol
mLastPictureButton.setOnClickListener(this);
mLastPictureButton.setVisibility(View.INVISIBLE);
+ findViewById(R.id.mode_indicator).setOnClickListener(this);
+
try {
mClickSound = new MediaPlayer();
AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.camera_click);
@@ -814,27 +813,17 @@ public class Camera extends Activity implements View.OnClickListener, SurfaceHol
public void onClick(View v) {
switch (v.getId()) {
-
- case R.id.last_picture_button: {
- viewLastImage();
- break;
- }
+ case R.id.mode_indicator:
+ doSnap(true);
+ break;
+ case R.id.last_picture_button:
+ viewLastImage();
+ break;
}
}
private void showStorageToast() {
- String noStorageText = null;
- int remaining = calculatePicturesRemaining();
-
- if (remaining == NO_STORAGE_ERROR) {
- noStorageText = getString(R.string.no_storage);
- } else if (remaining < 1) {
- noStorageText = getString(R.string.not_enough_space);
- }
-
- if (noStorageText != null) {
- Toast.makeText(this, noStorageText, 5000).show();
- }
+ MenuHelper.showStorageToast(this);
}
@Override
@@ -1007,27 +996,7 @@ public class Camera extends Activity implements View.OnClickListener, SurfaceHol
case KeyEvent.KEYCODE_CAMERA:
case KeyEvent.KEYCODE_DPAD_CENTER:
if (event.getRepeatCount() == 0) {
- // The camera operates in focus-priority mode, meaning that we take a picture
- // when focusing completes, and only if it completes successfully. If the user
- // has half-pressed the shutter and already locked focus, we can take the photo
- // right away, otherwise we need to start AF.
- if (mIsFocused || !mPreviewing) {
- // doesn't get set until the idler runs
- if (mCaptureObject != null) {
- mCaptureObject.onSnap();
- }
- clearFocus();
- updateFocusIndicator();
- } else {
- // Half pressing the shutter (i.e. the focus button event) will already have
- // requested AF for us, so just request capture on focus here. If AF has
- // already failed, we don't want to trigger it again.
- mCaptureOnFocus = true;
- if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && !mIsFocusButtonPressed) {
- // But we do need to start AF for DPAD_CENTER
- autoFocus();
- }
- }
+ doSnap(keyCode == KeyEvent.KEYCODE_DPAD_CENTER);
}
return true;
}
@@ -1046,6 +1015,30 @@ public class Camera extends Activity implements View.OnClickListener, SurfaceHol
return super.onKeyUp(keyCode, event);
}
+ private void doSnap(boolean needAutofocus) {
+ // The camera operates in focus-priority mode, meaning that we take a picture
+ // when focusing completes, and only if it completes successfully. If the user
+ // has half-pressed the shutter and already locked focus, we can take the photo
+ // right away, otherwise we need to start AF.
+ if (mIsFocused || !mPreviewing) {
+ // doesn't get set until the idler runs
+ if (mCaptureObject != null) {
+ mCaptureObject.onSnap();
+ }
+ clearFocus();
+ updateFocusIndicator();
+ } else {
+ // Half pressing the shutter (i.e. the focus button event) will already have
+ // requested AF for us, so just request capture on focus here. If AF has
+ // already failed, we don't want to trigger it again.
+ mCaptureOnFocus = true;
+ if (needAutofocus && !mIsFocusButtonPressed) {
+ // But we do need to start AF for DPAD_CENTER
+ autoFocus();
+ }
+ }
+ }
+
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// if we're creating the surface, start the preview as well.
boolean preview = holder.isCreating();
@@ -1571,21 +1564,7 @@ public class Camera extends Activity implements View.OnClickListener, SurfaceHol
};
private int calculatePicturesRemaining() {
- try {
- if (!ImageManager.instance().hasStorage()) {
- mPicturesRemaining = NO_STORAGE_ERROR;
- } else {
- String storageDirectory = Environment.getExternalStorageDirectory().toString();
- StatFs stat = new StatFs(storageDirectory);
- float remaining = ((float)stat.getAvailableBlocks() * (float)stat.getBlockSize()) / 400000F;
- mPicturesRemaining = (int)remaining;
- }
- } catch (Exception ex) {
- // if we can't stat the filesystem then we don't know how many
- // pictures are remaining. it might be zero but just leave it
- // blank since we really don't know.
- mPicturesRemaining = CANNOT_STAT_ERROR;
- }
+ mPicturesRemaining = MenuHelper.calculatePicturesRemaining();
return mPicturesRemaining;
}
diff --git a/src/com/android/camera/CropImage.java b/src/com/android/camera/CropImage.java
index a7f6404..489c644 100644
--- a/src/com/android/camera/CropImage.java
+++ b/src/com/android/camera/CropImage.java
@@ -87,7 +87,7 @@ public class CropImage extends Activity {
HighlightView mMotionHighlightView = null;
float mLastX, mLastY;
int mMotionEdge;
-
+
public CropImageView(Context context) {
super(context);
}
@@ -96,7 +96,7 @@ public class CropImage extends Activity {
protected boolean doesScrolling() {
return false;
}
-
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -110,11 +110,11 @@ public class CropImage extends Activity {
}
}
}
-
+
public CropImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
-
+
protected void zoomTo(float scale, float centerX, float centerY) {
super.zoomTo(scale, centerX, centerY);
for (HighlightView hv : mHighlightViews) {
@@ -130,7 +130,7 @@ public class CropImage extends Activity {
hv.invalidate();
}
}
-
+
protected void zoomOut() {
super.zoomOut();
for (HighlightView hv : mHighlightViews) {
@@ -139,12 +139,12 @@ public class CropImage extends Activity {
}
}
-
+
@Override
protected boolean usePerfectFitBitmap() {
return false;
}
-
+
@Override
protected void postTranslate(float deltaX, float deltaY) {
super.postTranslate(deltaX, deltaY);
@@ -161,7 +161,7 @@ public class CropImage extends Activity {
hv.setFocus(false);
hv.invalidate();
}
-
+
for (int i = 0; i < mHighlightViews.size(); i++) {
HighlightView hv = mHighlightViews.get(i);
int edge = hv.getHit(event.getX(), event.getY());
@@ -175,13 +175,13 @@ public class CropImage extends Activity {
}
invalidate();
}
-
+
@Override
public boolean onTouchEvent(MotionEvent event) {
CropImage cropImage = (CropImage)mContext;
if (cropImage.mSaving)
return false;
-
+
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (cropImage.mWaitingToPick) {
@@ -195,8 +195,8 @@ public class CropImage extends Activity {
mMotionHighlightView = hv;
mLastX = event.getX();
mLastY = event.getY();
- mMotionHighlightView.setMode(edge == HighlightView.MOVE
- ? HighlightView.ModifyMode.Move
+ mMotionHighlightView.setMode(edge == HighlightView.MOVE
+ ? HighlightView.ModifyMode.Move
: HighlightView.ModifyMode.Grow);
break;
}
@@ -251,17 +251,17 @@ public class CropImage extends Activity {
break;
case MotionEvent.ACTION_MOVE:
// if we're not zoomed then there's no point in even allowing
- // the user to move the image around. This call to center
+ // the user to move the image around. This call to center
// puts it back to the normalized location (with false meaning
// don't animate).
if (getScale() == 1F)
center(true, true, false);
break;
}
-
+
return true;
}
-
+
private void ensureVisible(HighlightView hv) {
Rect r = hv.mDrawRect;
@@ -277,32 +277,32 @@ public class CropImage extends Activity {
if (panDeltaX != 0 || panDeltaY != 0)
panBy(panDeltaX, panDeltaY);
}
-
+
private void centerBasedOnHighlightView(HighlightView hv) {
Rect drawRect = hv.mDrawRect;
-
+
float width = drawRect.width();
float height = drawRect.height();
-
+
float thisWidth = getWidth();
float thisHeight = getHeight();
-
+
float z1 = thisWidth / width * .6F;
float z2 = thisHeight / height * .6F;
-
+
float zoom = Math.min(z1, z2);
zoom = zoom * this.getScale();
zoom = Math.max(1F, zoom);
-
+
if ((Math.abs(zoom - getScale()) / zoom) > .1) {
float [] coordinates = new float[] { hv.mCropRect.centerX(), hv.mCropRect.centerY() };
getImageMatrix().mapPoints(coordinates);
zoomTo(zoom, coordinates[0], coordinates[1], 300F);
}
-
+
ensureVisible(hv);
}
-
+
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -310,15 +310,15 @@ public class CropImage extends Activity {
mHighlightViews.get(i).draw(canvas);
}
}
-
+
public HighlightView get(int i) {
return mHighlightViews.get(i);
}
-
+
public int size() {
return mHighlightViews.size();
}
-
+
public void add(HighlightView hv) {
mHighlightViews.add(hv);
invalidate();
@@ -333,7 +333,7 @@ public class CropImage extends Activity {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
c.drawRect(0F, 0F, width, height, paint);
}
-
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -344,6 +344,8 @@ public class CropImage extends Activity {
mImageView = (CropImageView) findViewById(R.id.image);
+ MenuHelper.showStorageToast(this);
+
try {
android.content.Intent intent = getIntent();
Bundle extras = intent.getExtras();
@@ -379,15 +381,16 @@ public class CropImage extends Activity {
Uri target = intent.getData();
mAllImages = ImageManager.makeImageList(target, CropImage.this, ImageManager.SORT_ASCENDING);
mImage = mAllImages.getImageForUri(target);
-
- // don't read in really large bitmaps. max out at 1000.
- // TODO when saving the resulting bitmap use the decode/crop/encode
- // api so we don't lose any resolution
- mBitmap = mImage.thumbBitmap();
- if (Config.LOGV)
- Log.v(TAG, "thumbBitmap returned " + mBitmap);
+ if(mImage != null) {
+ // don't read in really large bitmaps. max out at 1000.
+ // TODO when saving the resulting bitmap use the decode/crop/encode
+ // api so we don't lose any resolution
+ mBitmap = mImage.thumbBitmap();
+ if (Config.LOGV)
+ Log.v(TAG, "thumbBitmap returned " + mBitmap);
+ }
}
-
+
if (mBitmap == null) {
finish();
return;
@@ -406,7 +409,7 @@ public class CropImage extends Activity {
new Thread(new Runnable() {
public void run() {
final Bitmap b = mImage != null ? mImage.fullSizeBitmap(500) : mBitmap;
- if (Config.LOGV)
+ if (Config.LOGV)
Log.v(TAG, "back from mImage.fullSizeBitmap(500) with bitmap of size " + b.getWidth() + " / " + b.getHeight());
mHandler.post(new Runnable() {
public void run() {
@@ -427,13 +430,13 @@ public class CropImage extends Activity {
Log.e(TAG, "Failed to load bitmap", e);
finish();
}
-
+
findViewById(R.id.discard).setOnClickListener(new android.view.View.OnClickListener() {
public void onClick(View v) {
finish();
}
});
-
+
findViewById(R.id.save).setOnClickListener(new android.view.View.OnClickListener() {
public void onClick(View v) {
// TODO this code needs to change to use the decode/crop/encode single
@@ -461,7 +464,7 @@ public class CropImage extends Activity {
c1.drawBitmap(mBitmap, r, new Rect(0, 0, width, height), null);
if (mCircleCrop) {
- // OK, so what's all this about?
+ // OK, so what's all this about?
// Bitmaps are inherently rectangular but we want to return something
// that's basically a circle. So we fill in the area around the circle
// with alpha. Note the all important PortDuff.Mode.CLEAR.
@@ -532,7 +535,7 @@ public class CropImage extends Activity {
}
if (outputStream != null)
mCroppedImage.compress(mSaveFormat, 75, outputStream);
-
+
} catch (IOException ex) {
if (Config.LOGV)
Log.v(TAG, "got IOException " + ex);
@@ -541,7 +544,7 @@ public class CropImage extends Activity {
try {
outputStream.close();
} catch (IOException ex) {
-
+
}
}
}
@@ -553,13 +556,13 @@ public class CropImage extends Activity {
} else {
Bundle extras = new Bundle();
extras.putString("rect", mCrop.getCropRect().toString());
-
- // here we decide whether to create a new image or
+
+ // here we decide whether to create a new image or
// modify the existing image
if (false) {
/*
// this is the "modify" case
- ImageManager.IGetBoolean_cancelable cancelable =
+ ImageManager.IGetBoolean_cancelable cancelable =
mImage.saveImageContents(mCroppedImage, null, null, null, mImage.getDateTaken(), 0, false);
boolean didSave = cancelable.get();
extras.putString("thumb1uri", mImage.thumbUri().toString());
@@ -571,11 +574,11 @@ public class CropImage extends Activity {
// this is the "new image" case
java.io.File oldPath = new java.io.File(mImage.getDataPath());
java.io.File directory = new java.io.File(oldPath.getParent());
-
+
int x = 0;
String fileName = oldPath.getName();
fileName = fileName.substring(0, fileName.lastIndexOf("."));
-
+
while (true) {
x += 1;
String candidate = directory.toString() + "/" + fileName + "-" + x + ".jpg";
@@ -585,7 +588,7 @@ public class CropImage extends Activity {
if (!exists)
break;
}
-
+
try {
Uri newUri = ImageManager.instance().addImage(
CropImage.this,
@@ -600,10 +603,10 @@ public class CropImage extends Activity {
ImageManager.IAddImage_cancelable cancelable = ImageManager.instance().storeImage(
newUri,
- CropImage.this,
- getContentResolver(),
+ CropImage.this,
+ getContentResolver(),
0, // TODO fix this orientation
- mCroppedImage,
+ mCroppedImage,
null);
cancelable.get();
@@ -611,7 +614,7 @@ public class CropImage extends Activity {
(new Intent()).setAction(newUri.toString())
.putExtras(extras));
} catch (Exception ex) {
- // basically ignore this or put up
+ // basically ignore this or put up
// some ui saying we failed
}
}
@@ -625,7 +628,7 @@ public class CropImage extends Activity {
}
});
}
-
+
@Override
public void onResume() {
super.onResume();
@@ -639,7 +642,7 @@ public class CropImage extends Activity {
Matrix mImageMatrix;
FaceDetector.Face[] mFaces = new FaceDetector.Face[3];
int mNumFaces;
-
+
private void handleFace(FaceDetector.Face f) {
PointF midPoint = new PointF();
@@ -647,58 +650,58 @@ public class CropImage extends Activity {
f.getMidPoint(midPoint);
midPoint.x *= mScale;
midPoint.y *= mScale;
-
+
int midX = (int) midPoint.x;
int midY = (int) midPoint.y;
-
+
HighlightView hv = makeHighlightView();
int width = mBitmap.getWidth();
int height = mBitmap.getHeight();
-
+
Rect imageRect = new Rect(0, 0, width, height);
-
+
RectF faceRect = new RectF(midX, midY, midX, midY);
faceRect.inset(-r, -r);
if (faceRect.left < 0)
faceRect.inset(-faceRect.left, -faceRect.left);
-
+
if (faceRect.top < 0)
faceRect.inset(-faceRect.top, -faceRect.top);
-
+
if (faceRect.right > imageRect.right)
faceRect.inset(faceRect.right - imageRect.right, faceRect.right - imageRect.right);
-
+
if (faceRect.bottom > imageRect.bottom)
faceRect.inset(faceRect.bottom - imageRect.bottom, faceRect.bottom - imageRect.bottom);
hv.setup(mImageMatrix, imageRect, faceRect, mCircleCrop, mAspectX != 0 && mAspectY != 0);
-
+
if (mUnion == null) {
mUnion = new RectF(faceRect);
} else {
mUnion.union(faceRect);
}
- mImageView.add(hv);
+ mImageView.add(hv);
}
-
+
private HighlightView makeHighlightView() {
return new HighlightView(mImageView);
}
-
+
private void makeDefault() {
HighlightView hv = makeHighlightView();
int width = mBitmap.getWidth();
int height = mBitmap.getHeight();
-
+
Rect imageRect = new Rect(0, 0, width, height);
// make the default size about 4/5 of the width or height
int cropWidth = Math.min(width, height) * 4 / 5;
int cropHeight = cropWidth;
-
+
if (mAspectX != 0 && mAspectY != 0) {
if (mAspectX > mAspectY) {
cropHeight = cropWidth * mAspectY / mAspectX;
@@ -711,16 +714,16 @@ public class CropImage extends Activity {
int x = (width - cropWidth) / 2;
int y = (height - cropHeight) / 2;
-
+
RectF cropRect = new RectF(x, y, x + cropWidth, y + cropHeight);
hv.setup(mImageMatrix, imageRect, cropRect, mCircleCrop, mAspectX != 0 && mAspectY != 0);
mImageView.add(hv);
}
-
+
private Bitmap prepareBitmap() {
if (mBitmap == null)
return null;
-
+
// scale the image down for faster face detection
// 256 pixels wide is enough.
if (mBitmap.getWidth() > 256) {
@@ -730,13 +733,13 @@ public class CropImage extends Activity {
matrix.setScale(mScale, mScale);
Bitmap faceBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap
.getWidth(), mBitmap.getHeight(), matrix, true);
- return faceBitmap;
+ return faceBitmap;
}
-
+
public void run() {
mImageMatrix = mImageView.getImageMatrix();
Bitmap faceBitmap = prepareBitmap();
-
+
mScale = 1.0F / mScale;
if (faceBitmap != null && mDoFaceDetection) {
FaceDetector detector = new FaceDetector(faceBitmap.getWidth(),
@@ -745,8 +748,8 @@ public class CropImage extends Activity {
if (Config.LOGV)
Log.v(TAG, "numFaces is " + mNumFaces);
}
- mHandler.post(new Runnable() {
- public void run() {
+ mHandler.post(new Runnable() {
+ public void run() {
mWaitingToPick = mNumFaces > 1;
if (mNumFaces > 0) {
for (int i = 0; i < mNumFaces; i++) {
@@ -767,9 +770,9 @@ public class CropImage extends Activity {
Toast t = Toast.makeText(CropImage.this, R.string.multiface_crop_help, Toast.LENGTH_SHORT);
t.show();
}
- }
+ }
});
-
+
}
};
@@ -780,15 +783,15 @@ public class CropImage extends Activity {
if (mAllImages != null)
mAllImages.deactivate();
}
-
+
private synchronized void closeProgressDialog() {
if (mFaceDetectionDialog != null) {
mFaceDetectionDialog.dismiss();
mFaceDetectionDialog = null;
- }
+ }
if (mSavingProgressDialog != null) {
mSavingProgressDialog.dismiss();
mSavingProgressDialog = null;
- }
+ }
}
}
diff --git a/src/com/android/camera/GalleryPicker.java b/src/com/android/camera/GalleryPicker.java
index df9049f..44f8fc2 100644
--- a/src/com/android/camera/GalleryPicker.java
+++ b/src/com/android/camera/GalleryPicker.java
@@ -62,6 +62,7 @@ import java.util.Map;
public class GalleryPicker extends Activity {
static private final String TAG = "GalleryPicker";
+ private View mNoImagesView;
GridView mGridView;
Drawable mFrameGalleryMask;
Drawable mCellOutline;
@@ -107,6 +108,7 @@ public class GalleryPicker extends Activity {
setContentView(R.layout.gallerypicker);
+ mNoImagesView = findViewById(R.id.no_images);
mGridView = (GridView) findViewById(R.id.albums);
mGridView.setSelector(android.R.color.transparent);
@@ -511,14 +513,21 @@ public class GalleryPicker extends Activity {
});
t.start();
- // If we just have one folder, open it. (Probably never triggered because we always have
- // At least two folders now.)
- if (!scanning && mAdapter.mItems.size() <= 1) {
- android.net.Uri uri = Images.Media.INTERNAL_CONTENT_URI;
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- startActivity(intent);
- finish();
- return;
+ // If we just have zero or one folder, open it. (We shouldn't have just one folder
+ // any more, but we can have zero folders.)
+ mNoImagesView.setVisibility(View.GONE);
+ if (!scanning) {
+ int numItems = mAdapter.mItems.size();
+ if (numItems == 0) {
+ mNoImagesView.setVisibility(View.VISIBLE);
+ } else if (numItems == 1) {
+ // Not sure we can ever get here any more.
+ android.net.Uri uri = Images.Media.INTERNAL_CONTENT_URI;
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ startActivity(intent);
+ finish();
+ return;
+ }
}
}
diff --git a/src/com/android/camera/MenuHelper.java b/src/com/android/camera/MenuHelper.java
index f8e1658..034c441 100644
--- a/src/com/android/camera/MenuHelper.java
+++ b/src/com/android/camera/MenuHelper.java
@@ -25,7 +25,9 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
+import android.os.Environment;
import android.os.Handler;
+import android.os.StatFs;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.util.Config;
@@ -83,6 +85,9 @@ public class MenuHelper {
static public final int MENU_VIDEO_SHARE = 24;
static public final int MENU_VIDEO_TOSS = 27;
+ public static final int NO_STORAGE_ERROR = -1;
+ public static final int CANNOT_STAT_ERROR = -2;
+
public interface MenuItemsResult {
public void gettingReadyToOpen(Menu menu, ImageManager.IImage image);
public void aboutToCall(MenuItem item, ImageManager.IImage image);
@@ -300,20 +305,10 @@ public class MenuHelper {
}
try {
- long durationMs = Long.parseLong(retriever.extractMetadata(
+ int durationMs = Integer.parseInt(retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_DURATION));
- long duration = durationMs / 1000;
- long h = duration / 3600;
- long m = (duration - h * 3600) / 60;
- long s = duration - (h * 3600 + m * 60);
- String durationValue;
- if (h == 0) {
- durationValue = String.format(
- activity.getString(R.string.details_ms), m, s);
- } else {
- durationValue = String.format(
- activity.getString(R.string.details_hms), h, m, s);
- }
+ String durationValue = formatDuration(
+ activity, durationMs);
((TextView)d.findViewById(R.id.details_duration_value))
.setText(durationValue);
} catch (NumberFormatException e) {
@@ -605,5 +600,55 @@ public class MenuHelper {
int keyboard = activity.getResources().getConfiguration().hardKeyboardHidden;
flipItem.setEnabled(keyboard != android.content.res.Configuration.HARDKEYBOARDHIDDEN_NO);
}
+
+ public static String formatDuration(final Activity activity, int durationMs) {
+ int duration = durationMs / 1000;
+ int h = duration / 3600;
+ int m = (duration - h * 3600) / 60;
+ int s = duration - (h * 3600 + m * 60);
+ String durationValue;
+ if (h == 0) {
+ durationValue = String.format(
+ activity.getString(R.string.details_ms), m, s);
+ } else {
+ durationValue = String.format(
+ activity.getString(R.string.details_hms), h, m, s);
+ }
+ return durationValue;
+ }
+
+
+ public static void showStorageToast(Activity activity) {
+ String noStorageText = null;
+ int remaining = calculatePicturesRemaining();
+
+ if (remaining == MenuHelper.NO_STORAGE_ERROR) {
+ noStorageText = activity.getString(R.string.no_storage);
+ } else if (remaining < 1) {
+ noStorageText = activity.getString(R.string.not_enough_space);
+ }
+
+ if (noStorageText != null) {
+ Toast.makeText(activity, noStorageText, 5000).show();
+ }
+ }
+
+ public static int calculatePicturesRemaining() {
+ try {
+ if (!ImageManager.hasStorage()) {
+ return NO_STORAGE_ERROR;
+ } else {
+ String storageDirectory = Environment.getExternalStorageDirectory().toString();
+ StatFs stat = new StatFs(storageDirectory);
+ float remaining = ((float)stat.getAvailableBlocks() * (float)stat.getBlockSize()) / 400000F;
+ return (int)remaining;
+ }
+ } catch (Exception ex) {
+ // if we can't stat the filesystem then we don't know how many
+ // pictures are remaining. it might be zero but just leave it
+ // blank since we really don't know.
+ return CANNOT_STAT_ERROR;
+ }
+ }
}
diff --git a/src/com/android/camera/MovieView.java b/src/com/android/camera/MovieView.java
index 58e80df..b93336c 100644
--- a/src/com/android/camera/MovieView.java
+++ b/src/com/android/camera/MovieView.java
@@ -18,14 +18,22 @@ package com.android.camera;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ContentValues;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
import android.content.pm.ActivityInfo;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
+import android.provider.MediaStore.Video;
import android.view.View;
import android.widget.MediaController;
import android.widget.VideoView;
@@ -41,6 +49,7 @@ public class MovieView extends Activity implements MediaPlayer.OnErrorListener,
private VideoView mVideoView;
private View mProgressView;
private boolean mFinishOnCompletion;
+ private Uri mUri;
public MovieView()
{
}
@@ -63,11 +72,11 @@ public class MovieView extends Activity implements MediaPlayer.OnErrorListener,
}
}
mFinishOnCompletion = intent.getBooleanExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, true);
- Uri uri = intent.getData();
+ mUri = intent.getData();
// For streams that we expect to be slow to start up, show a
// progress spinner until playback starts.
- String scheme = uri.getScheme();
+ String scheme = mUri.getScheme();
if ("http".equalsIgnoreCase(scheme) ||
"rtsp".equalsIgnoreCase(scheme)) {
mHandler.postDelayed(mPlayingChecker, 250);
@@ -77,20 +86,100 @@ public class MovieView extends Activity implements MediaPlayer.OnErrorListener,
mVideoView.setOnErrorListener(this);
mVideoView.setOnCompletionListener(this);
- mVideoView.setVideoURI(uri);
+ mVideoView.setVideoURI(mUri);
mVideoView.setMediaController(new MediaController(this));
mVideoView.requestFocus(); // make the video view handle keys for seeking and pausing
Intent i = new Intent(SERVICECMD);
i.putExtra(CMDNAME, CMDPAUSE);
sendBroadcast(i);
+ {
+ final Integer bookmark = getBookmark();
+ if (bookmark != null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.resume_playing_title);
+ builder.setMessage(String.format(
+ getString(R.string.resume_playing_message),
+ MenuHelper.formatDuration(this, bookmark)));
+ builder.setOnCancelListener(new OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }});
+ builder.setPositiveButton(R.string.resume_playing_resume,
+ new OnClickListener(){
+ public void onClick(DialogInterface dialog, int which) {
+ mVideoView.seekTo(bookmark);
+ mVideoView.start();
+ }});
+ builder.setNegativeButton(R.string.resume_playing_restart, new OnClickListener(){
+ public void onClick(DialogInterface dialog, int which) {
+ mVideoView.start();
+ }});
+ builder.show();
+ } else {
+ mVideoView.start();
+ }
+ }
+ }
- mVideoView.start();
+ private Integer getBookmark() {
+ String scheme = mUri.getScheme();
+ if ("content".equalsIgnoreCase(scheme)) {
+ String[] projection = new String[]{Video.VideoColumns.DURATION,
+ Video.VideoColumns.BOOKMARK};
+ try {
+ Cursor cursor = getContentResolver().query(mUri, projection, null, null, null);
+ if (cursor != null) {
+ try {
+ if ( cursor.moveToFirst() ) {
+ int duration = getCursorInteger(cursor, 0);
+ int bookmark = getCursorInteger(cursor, 1);
+ final int ONE_MINUTE = 60 * 1000;
+ final int TWO_MINUTES = 2 * ONE_MINUTE;
+ final int FIVE_MINUTES = 5 * ONE_MINUTE;
+ if ((bookmark < TWO_MINUTES)
+ || (duration < FIVE_MINUTES)
+ || (bookmark > (duration - ONE_MINUTE))) {
+ return null;
+ }
+
+ return new Integer(bookmark);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ } catch (SQLiteException e) {
+ // ignore
+ }
+ }
+ return null;
+ }
+
+ private int getCursorInteger(Cursor cursor, int index) {
+ try {
+ return cursor.getInt(index);
+ } catch (SQLiteException e) {
+ return 0;
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+
+ }
+
+ private void setBookmark(int bookmark) {
+ String scheme = mUri.getScheme();
+ if ("content".equalsIgnoreCase(scheme)) {
+ ContentValues values = new ContentValues();
+ values.put(Video.VideoColumns.BOOKMARK, Integer.toString(bookmark));
+ getContentResolver().update(mUri, values, null, null);
+ }
}
@Override
public void onPause() {
mHandler.removeCallbacksAndMessages(null);
+ setBookmark(mVideoView.getCurrentPosition());
super.onPause();
}
diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java
index 9da1f49..9bfb860 100644
--- a/src/com/android/camera/VideoCamera.java
+++ b/src/com/android/camera/VideoCamera.java
@@ -67,8 +67,8 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
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 KEEP = 2;
private static final int CLEAR_SCREEN_DELAY = 4;
private static final int UPDATE_RECORD_TIME = 5;
@@ -86,7 +86,6 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
public static final int MENU_SAVE_SELECT_VIDEO = 36;
public static final int MENU_SAVE_NEW_VIDEO = 37;
- Toast mToast;
SharedPreferences mPreferences;
private static final float VIDEO_ASPECT_RATIO = 176.0f / 144.0f;
@@ -98,9 +97,14 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
private MediaRecorder mMediaRecorder;
private boolean mMediaRecorderRecording = false;
- private boolean mNeedToDeletePartialRecording;
private boolean mNeedToRegisterRecording;
private long mRecordingStartTime;
+ // The video file that the hardware camera is about to record into
+ // (or is recording into.)
+ private String mCameraVideoFilename;
+
+ // The video file that has already been recorded, and that is being
+ // examined by the user.
private String mCurrentVideoFilename;
private Uri mCurrentVideoUri;
private ContentValues mCurrentVideoValues;
@@ -123,26 +127,11 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
private Handler mHandler = new MainHandler();
- private void cancelSavingNotification() {
- if (mToast != null) {
- mToast.cancel();
- mToast = null;
- }
- }
-
/** This Handler is used to post message back onto the main thread of the application */
private class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case KEEP: {
- keep();
-
- if (msg.obj != null) {
- mHandler.post((Runnable)msg.obj);
- }
- break;
- }
case CLEAR_SCREEN_DELAY: {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@@ -209,15 +198,12 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
return DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken).toString();
}
- private void postAfterKeep(final Runnable r) {
- Message msg = mHandler.obtainMessage(KEEP);
- msg.obj = r;
- msg.sendToTarget();
- }
-
/** 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);
@@ -248,7 +234,8 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
mPostPictureAlert = findViewById(R.id.post_picture_panel);
int[] ids = new int[]{R.id.play, R.id.share, R.id.discard,
- R.id.capture, R.id.cancel, R.id.accept};
+ R.id.capture, R.id.cancel, R.id.accept, R.id.mode_indicator,
+ R.id.recording_indicator};
for (int id : ids) {
findViewById(id).setOnClickListener(this);
}
@@ -261,6 +248,9 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
@Override
public void onStart() {
+ if (DEBUG_LOG_APP_LIFECYCLE) {
+ Log.v(TAG, "onStart " + this.hashCode());
+ }
super.onStart();
final View hintView = findViewById(R.id.hint_toast);
@@ -304,7 +294,7 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
public void onClick(View v) {
switch (v.getId()) {
case R.id.capture:
- doDiscardCurrentVideo();
+ doStartCaptureMode();
break;
case R.id.accept:
@@ -316,7 +306,7 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
break;
case R.id.discard: {
- doDiscardCurrentVideo();
+ discardCurrentVideoAndStartPreview();
break;
}
@@ -338,10 +328,30 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
doPlayCurrentVideo();
break;
}
+
+ case R.id.mode_indicator:
+ if (mVideoFrame.getVisibility() == View.VISIBLE) {
+ doStartCaptureMode();
+ }
+ startVideoRecording();
+ break;
+
+ case R.id.recording_indicator:
+ stopVideoRecordingAndDisplayDialog();
+ break;
+ }
+ }
+
+ private void doStartCaptureMode() {
+ if (isPickIntent()) {
+ discardCurrentVideoAndStartPreview();
+ } else {
+ hideVideoFrameAndStartPreview();
}
}
private void doPlayCurrentVideo() {
+ Log.e(TAG, "Playing current video: " + mCurrentVideoUri);
Intent intent = new Intent(Intent.ACTION_VIEW, mCurrentVideoUri);
try {
startActivity(intent);
@@ -350,7 +360,7 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
}
}
- private void doDiscardCurrentVideo() {
+ private void discardCurrentVideoAndStartPreview() {
deleteCurrentVideo();
hideVideoFrameAndStartPreview();
}
@@ -369,6 +379,9 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
@Override
public void onResume() {
+ if (DEBUG_LOG_APP_LIFECYCLE) {
+ Log.v(TAG, "onResume " + this.hashCode());
+ }
super.onResume();
mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
@@ -393,18 +406,22 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
@Override
public void onStop() {
- Log.v(TAG, "onStop");
+ if (DEBUG_LOG_APP_LIFECYCLE) {
+ Log.v(TAG, "onStop " + this.hashCode());
+ }
stopVideoRecording();
- keep();
mHandler.removeMessages(CLEAR_SCREEN_DELAY);
super.onStop();
}
@Override
protected void onPause() {
- Log.v(TAG, "onPause");
+ if (DEBUG_LOG_APP_LIFECYCLE) {
+ Log.v(TAG, "onPause " + this.hashCode());
+ }
+ super.onPause();
+
stopVideoRecording();
- keep();
hidePostPictureAlert();
mPausing = true;
@@ -413,8 +430,7 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
unregisterReceiver(mReceiver);
mDidRegister = false;
}
-
- super.onPause();
+ mBlackout.setVisibility(View.VISIBLE);
}
@Override
@@ -481,14 +497,6 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
}
}
- void keep() {
- cancelSavingNotification();
- };
-
- void toss() {
- cancelSavingNotification();
- };
-
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
@@ -500,7 +508,6 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
}
menu.setGroupVisible(MenuHelper.VIDEO_MODE_ITEM, true);
-
return true;
}
@@ -531,11 +538,7 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
R.string.camera_gallery_photos_text).setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
- postAfterKeep(new Runnable() {
- public void run() {
- gotoGallery();
- }
- });
+ gotoGallery();
return true;
}
});
@@ -593,7 +596,6 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
}
mMediaRecorder = new MediaRecorder();
- mNeedToDeletePartialRecording = true;
mNeedToRegisterRecording = false;
if (DEBUG_SUPPRESS_AUDIO_RECORDING) {
@@ -603,9 +605,8 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
}
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- Log.v(TAG, "before setOutputFile");
createVideoPath();
- mMediaRecorder.setOutputFile(mCurrentVideoFilename);
+ mMediaRecorder.setOutputFile(mCameraVideoFilename);
boolean videoQualityHigh = getBooleanPreference(CameraSettings.KEY_VIDEO_QUALITY,
CameraSettings.DEFAULT_VIDEO_QUALITY_VALUE);
@@ -631,12 +632,11 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
if (!DEBUG_SUPPRESS_AUDIO_RECORDING) {
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
}
- Log.v(TAG, "before setPreviewDisplay");
mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
try {
mMediaRecorder.prepare();
} catch (IOException exception) {
- Log.e(TAG, "prepare failed for " + mCurrentVideoFilename);
+ Log.e(TAG, "prepare failed for " + mCameraVideoFilename);
releaseMediaRecorder();
// TODO: add more exception handling logic here
return;
@@ -661,7 +661,7 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
try {
mMediaRecorder.prepare();
} catch (IOException exception) {
- Log.e(TAG, "prepare failed for " + mCurrentVideoFilename);
+ Log.e(TAG, "prepare failed for " + mCameraVideoFilename);
releaseMediaRecorder();
// TODO: add more exception handling logic here
}
@@ -698,25 +698,22 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
values.put(Video.Media.DATE_TAKEN, dateTaken);
values.put(Video.Media.MIME_TYPE, "video/3gpp");
values.put(Video.Media.DATA, filename);
- mCurrentVideoFilename = filename;
+ mCameraVideoFilename = filename;
+ Log.v(TAG, "Current camera video filename: " + mCameraVideoFilename);
mCurrentVideoValues = values;
- mCurrentVideoUri = null;
}
private void registerVideo() {
Uri videoTable = Uri.parse("content://media/external/video/media");
mCurrentVideoUri = mContentResolver.insert(videoTable,
mCurrentVideoValues);
+ Log.v(TAG, "Current video URI: " + mCurrentVideoUri);
mCurrentVideoValues = null;
}
private void deleteCurrentVideo() {
if (mCurrentVideoFilename != null) {
- Log.v(TAG, "Deleting stub video " + mCurrentVideoFilename);
- File f = new File(mCurrentVideoFilename);
- if (! f.delete()) {
- Log.v(TAG, "Could not delete " + mCurrentVideoFilename);
- }
+ deleteVideoFile(mCurrentVideoFilename);
mCurrentVideoFilename = null;
}
if (mCurrentVideoUri != null) {
@@ -725,6 +722,14 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
}
}
+ private void deleteVideoFile(String fileName) {
+ Log.v(TAG, "Deleting video " + fileName);
+ File f = new File(fileName);
+ if (! f.delete()) {
+ Log.v(TAG, "Could not delete " + fileName);
+ }
+ }
+
private void addBaseMenuItems(Menu menu) {
MenuHelper.addSwitchModeMenuItem(menu, this, false);
{
@@ -817,7 +822,9 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
if (mMediaRecorderRecording || mMediaRecorder != null) {
if (mMediaRecorderRecording) {
mMediaRecorder.stop();
- mNeedToDeletePartialRecording = false;
+ mCurrentVideoFilename = mCameraVideoFilename;
+ Log.v(TAG, "Setting current video filename: " + mCurrentVideoFilename);
+ mCameraVideoFilename = null;
mNeedToRegisterRecording = true;
mMediaRecorderRecording = false;
}
@@ -831,9 +838,8 @@ public class VideoCamera extends Activity implements View.OnClickListener, Surfa
registerVideo();
mNeedToRegisterRecording = false;
}
- if (mNeedToDeletePartialRecording){
- deleteCurrentVideo();
- mNeedToDeletePartialRecording = false;
+ if (mCameraVideoFilename != null){
+ deleteVideoFile(mCameraVideoFilename);
}
}