blob: b79e7d1fe7790b0df6703a3c3b5c5a2511b87381 [file] [log] [blame]
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
The Android Open Source Project0eaec582008-12-17 18:05:54 -080019import java.io.File;
20import java.io.FileNotFoundException;
21import java.io.FileOutputStream;
22import java.io.IOException;
23import java.io.OutputStream;
24import java.util.ArrayList;
25
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070026import android.app.Activity;
27import android.app.ProgressDialog;
The Android Open Source Project0eaec582008-12-17 18:05:54 -080028import android.content.ActivityNotFoundException;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070029import android.content.BroadcastReceiver;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.SharedPreferences;
35import android.content.res.AssetFileDescriptor;
36import android.content.res.Resources;
37import android.graphics.Bitmap;
38import android.graphics.BitmapFactory;
39import android.graphics.Matrix;
40import android.graphics.drawable.ColorDrawable;
41import android.hardware.Camera.PictureCallback;
42import android.hardware.Camera.Size;
43import android.location.Location;
44import android.location.LocationManager;
45import android.location.LocationProvider;
46import android.media.AudioManager;
47import android.media.MediaPlayer;
48import android.media.ToneGenerator;
49import android.net.Uri;
50import android.os.Bundle;
51import android.os.Debug;
52import android.os.Environment;
53import android.os.Handler;
54import android.os.Message;
55import android.os.StatFs;
56import android.os.SystemClock;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070057import android.preference.PreferenceManager;
58import android.provider.MediaStore;
59import android.provider.MediaStore.Images;
The Android Open Source Project0eaec582008-12-17 18:05:54 -080060import android.text.format.DateFormat;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070061import android.util.Config;
62import android.util.Log;
63import android.view.KeyEvent;
64import android.view.Menu;
65import android.view.MenuItem;
66import android.view.OrientationListener;
67import android.view.SurfaceHolder;
68import android.view.SurfaceView;
69import android.view.View;
70import android.view.ViewGroup;
71import android.view.Window;
72import android.view.WindowManager;
73import android.view.MenuItem.OnMenuItemClickListener;
74import android.view.animation.Animation;
75import android.view.animation.AnimationUtils;
76import android.widget.ImageView;
77import android.widget.Toast;
78
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070079public class Camera extends Activity implements View.OnClickListener, SurfaceHolder.Callback {
The Android Open Source Project0eaec582008-12-17 18:05:54 -080080
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070081 private static final String TAG = "camera";
The Android Open Source Project0eaec582008-12-17 18:05:54 -080082
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070083 private static final boolean DEBUG = false;
The Android Open Source Project0eaec582008-12-17 18:05:54 -080084
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070085 private static final int CROP_MSG = 1;
86 private static final int KEEP = 2;
87 private static final int RESTART_PREVIEW = 3;
88 private static final int CLEAR_SCREEN_DELAY = 4;
The Android Open Source Project0eaec582008-12-17 18:05:54 -080089
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070090 private static final int SCREEN_DELAY = 2 * 60 * 1000;
The Android Open Source Project0eaec582008-12-17 18:05:54 -080091 private static final int POST_PICTURE_ALERT_TIMEOUT = 6 * 1000;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070092 private static final int FOCUS_BEEP_VOLUME = 100;
93
94 private static final int NO_STORAGE_ERROR = -1;
95 private static final int CANNOT_STAT_ERROR = -2;
The Android Open Source Project0eaec582008-12-17 18:05:54 -080096
The Android Open Source Project1d4c7502008-10-21 07:00:00 -070097 public static final int MENU_SWITCH_TO_VIDEO = 0;
The Android Open Source Project0eaec582008-12-17 18:05:54 -080098 public static final int MENU_SWITCH_TO_CAMERA = 1;
99 public static final int MENU_FLASH_SETTING = 2;
100 public static final int MENU_FLASH_AUTO = 3;
101 public static final int MENU_FLASH_ON = 4;
102 public static final int MENU_FLASH_OFF = 5;
103 public static final int MENU_SETTINGS = 6;
104 public static final int MENU_GALLERY_PHOTOS = 7;
105 public static final int MENU_GALLERY_VIDEOS = 8;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700106 public static final int MENU_SAVE_SELECT_PHOTOS = 30;
107 public static final int MENU_SAVE_NEW_PHOTO = 31;
108 public static final int MENU_SAVE_SELECTVIDEO = 32;
109 public static final int MENU_SAVE_TAKE_NEW_VIDEO = 33;
110 public static final int MENU_SAVE_GALLERY_PHOTO = 34;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800111 public static final int MENU_SAVE_GALLERY_VIDEO_PHOTO = 35;
112 public static final int MENU_SAVE_CAMERA_DONE = 36;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700113 public static final int MENU_SAVE_CAMERA_VIDEO_DONE = 37;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800114
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700115 Toast mToast;
116 OrientationListener mOrientationListener;
117 int mLastOrientation = OrientationListener.ORIENTATION_UNKNOWN;
118 SharedPreferences mPreferences;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800119
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700120 static final int IDLE = 1;
121 static final int SNAPSHOT_IN_PROGRESS = 2;
122 static final int SNAPSHOT_COMPLETED = 3;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800123
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700124 int mStatus = IDLE;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800125 static final String sTempCropFilename = "crop-temp";
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700126
127 android.hardware.Camera mCameraDevice;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800128 VideoPreview mSurfaceView;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700129 SurfaceHolder mSurfaceHolder = null;
130 ImageView mBlackout = null;
131
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800132 int mOriginalViewFinderWidth, mOriginalViewFinderHeight;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700133 int mViewFinderWidth, mViewFinderHeight;
134 boolean mPreviewing = false;
135
136 MediaPlayer mClickSound;
137
138 Capturer mCaptureObject;
139 ImageCapture mImageCapture = null;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800140
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700141 boolean mPausing = false;
142
143 boolean mIsFocusing = false;
144 boolean mIsFocused = false;
145 boolean mIsFocusButtonPressed = false;
146 boolean mCaptureOnFocus = false;
147
148 static ContentResolver mContentResolver;
149 boolean mDidRegister = false;
150
151 int mCurrentZoomIndex = 0;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700152
153 ArrayList<MenuItem> mGalleryItems = new ArrayList<MenuItem>();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800154
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700155 boolean mMenuSelectionMade;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800156
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700157 View mPostPictureAlert;
158 LocationManager mLocationManager = null;
159
160 private Animation mFocusBlinkAnimation;
161 private View mFocusIndicator;
162 private ToneGenerator mFocusToneGenerator;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800163
164
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700165 private ShutterCallback mShutterCallback = new ShutterCallback();
166 private RawPictureCallback mRawPictureCallback = new RawPictureCallback();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700167 private AutoFocusCallback mAutoFocusCallback = new AutoFocusCallback();
168 private long mShutterPressTime;
169 private int mPicturesRemaining;
170
171 private boolean mKeepAndRestartPreview;
172
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800173 private Handler mHandler = new MainHandler();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700174 private ProgressDialog mSavingProgress;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800175
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700176 interface Capturer {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800177 Uri getLastCaptureUri();
178 void onSnap();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700179 void dismissFreezeFrame(boolean keep);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800180 void cancelSave();
181 void cancelAutoDismiss();
182 void setDone(boolean wait);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700183 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800184
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700185 private void cancelSavingNotification() {
186 if (mToast != null) {
187 mToast.cancel();
188 mToast = null;
189 }
190 }
191
192 /** This Handler is used to post message back onto the main thread of the application */
193 private class MainHandler extends Handler {
194 @Override
195 public void handleMessage(Message msg) {
196 switch (msg.what) {
197 case KEEP: {
198 keep();
199 if (mSavingProgress != null) {
200 mSavingProgress.cancel();
201 mSavingProgress = null;
202 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800203
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700204 mKeepAndRestartPreview = true;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800205
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700206 if (msg.obj != null) {
207 mHandler.post((Runnable)msg.obj);
208 }
209 break;
210 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800211
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700212 case RESTART_PREVIEW: {
213 if (mStatus == SNAPSHOT_IN_PROGRESS) {
214 // We are still in the processing of taking the picture, wait.
215 // This is is strange. Why are we polling?
216 // TODO remove polling
217 mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, 100);
218 } else if (mStatus == SNAPSHOT_COMPLETED){
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800219 hidePostPictureAlert();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700220 mCaptureObject.dismissFreezeFrame(true);
221 }
222 break;
223 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800224
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700225 case CLEAR_SCREEN_DELAY: {
226 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
227 break;
228 }
229 }
230 }
231 };
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800232
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700233 LocationListener [] mLocationListeners = new LocationListener[] {
234 new LocationListener(LocationManager.GPS_PROVIDER),
235 new LocationListener(LocationManager.NETWORK_PROVIDER)
236 };
237
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800238
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700239 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
240 @Override
241 public void onReceive(Context context, Intent intent) {
242 String action = intent.getAction();
243 if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
244 // SD card available
245 // TODO put up a "please wait" message
246 // TODO also listen for the media scanner finished message
247 showStorageToast();
248 } else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
249 // SD card unavailable
250 showStorageToast();
251 } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) {
252 Toast.makeText(Camera.this, getResources().getString(R.string.wait), 5000);
253 } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
254 showStorageToast();
255 }
256 }
257 };
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800258
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700259 private class LocationListener implements android.location.LocationListener {
260 Location mLastLocation;
261 boolean mValid = false;
262 String mProvider;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800263
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700264 public LocationListener(String provider) {
265 mProvider = provider;
266 mLastLocation = new Location(mProvider);
267 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800268
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700269 public void onLocationChanged(Location newLocation) {
270 if (newLocation.getLatitude() == 0.0 && newLocation.getLongitude() == 0.0) {
271 // Hack to filter out 0.0,0.0 locations
272 return;
273 }
274 mLastLocation.set(newLocation);
275 mValid = true;
276 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800277
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700278 public void onProviderEnabled(String provider) {
279 }
280
281 public void onProviderDisabled(String provider) {
282 mValid = false;
283 }
284
285 public void onStatusChanged(String provider, int status, Bundle extras) {
286 if (status == LocationProvider.OUT_OF_SERVICE) {
287 mValid = false;
288 }
289 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800290
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700291 public Location current() {
292 return mValid ? mLastLocation : null;
293 }
294 };
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800295
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700296 private long mRawPictureCallbackTime;
297
298 private boolean mImageSavingItem = false;
299
300 private final class ShutterCallback implements android.hardware.Camera.ShutterCallback {
301 public void onShutter() {
302 if (DEBUG) {
303 long now = System.currentTimeMillis();
304 Log.v(TAG, "********** Total shutter lag " + (now - mShutterPressTime) + " ms");
305 }
306 if (mClickSound != null) {
307 mClickSound.seekTo(0);
308 mClickSound.start();
309 }
310 }
311 };
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800312
313 private final class RawPictureCallback implements PictureCallback {
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700314 public void onPictureTaken(byte [] rawData, android.hardware.Camera camera) {
315 if (Config.LOGV)
316 Log.v(TAG, "got RawPictureCallback...");
317 mRawPictureCallbackTime = System.currentTimeMillis();
318 mBlackout.setVisibility(View.INVISIBLE);
319 if (!isPickIntent() && mPreferences.getBoolean("pref_camera_postpicturemenu_key", true)) {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800320 showPostPictureAlert();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700321 }
322 }
323 };
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800324
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700325 private final class JpegPictureCallback implements PictureCallback {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800326 Location mLocation;
327
328 public JpegPictureCallback(Location loc) {
329 mLocation = loc;
330 }
331
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700332 public void onPictureTaken(byte [] jpegData, android.hardware.Camera camera) {
333 if (Config.LOGV)
334 Log.v(TAG, "got JpegPictureCallback...");
335
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800336 mImageCapture.storeImage(jpegData, camera, mLocation);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700337
338 mStatus = SNAPSHOT_COMPLETED;
339
340 if (!mPreferences.getBoolean("pref_camera_postpicturemenu_key", true)) {
341 if (mKeepAndRestartPreview) {
342 long delay = 1500 - (System.currentTimeMillis() - mRawPictureCallbackTime);
343 mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, Math.max(delay, 0));
344 }
345 return;
346 }
347
348 if (mKeepAndRestartPreview) {
349 mKeepAndRestartPreview = false;
350 mPostPictureAlert.setVisibility(View.INVISIBLE);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800351
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700352 // Post this message so that we can finish processing the request. This also
353 // prevents the preview from showing up before mPostPictureAlert is dismissed.
354 mHandler.sendEmptyMessage(RESTART_PREVIEW);
355 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800356
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700357 }
358 };
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800359
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700360 private final class AutoFocusCallback implements android.hardware.Camera.AutoFocusCallback {
361 public void onAutoFocus(boolean focused, android.hardware.Camera camera) {
362 mIsFocusing = false;
363 mIsFocused = focused;
364 if (focused) {
365 if (mCaptureOnFocus && mCaptureObject != null) {
366 // No need to play the AF sound if we're about to play the shutter sound
367 mCaptureObject.onSnap();
368 clearFocus();
369 } else {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800370 ToneGenerator tg = mFocusToneGenerator;
371 if (tg != null)
372 tg.startTone(ToneGenerator.TONE_PROP_BEEP2);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700373 }
374 mCaptureOnFocus = false;
375 }
376 updateFocusIndicator();
377 }
378 };
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800379
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700380 private class ImageCapture implements Capturer {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800381
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700382 private boolean mCancel = false;
383 private boolean mCapturing = false;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800384
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700385 private Uri mLastContentUri;
386 private ImageManager.IAddImage_cancelable mAddImageCancelable;
387
388 Bitmap mCaptureOnlyBitmap;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800389
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700390 /** These member variables are used for various debug timings */
391 private long mThreadTimeStart;
392 private long mThreadTimeEnd;
393 private long mWallTimeStart;
394 private long mWallTimeEnd;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800395
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700396
397 public ImageCapture() {
398 }
399
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800400 /**
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700401 * This method sets whether or not we are capturing a picture. This method must be called
402 * with the ImageCapture.this lock held.
403 */
404 public void setCapturingLocked(boolean capturing) {
405 mCapturing = capturing;
406 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800407
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700408 /*
409 * Tell the ImageCapture thread to exit when possible.
410 */
411 public void setDone(boolean wait) {
412 }
413
414 /*
415 * Tell the image capture thread to not "dismiss" the current
416 * capture when the current image is stored, etc.
417 */
418 public void cancelAutoDismiss() {
419 }
420
421 public void dismissFreezeFrame(boolean keep) {
422 if (keep) {
423 cancelSavingNotification();
424 } else {
425 Toast.makeText(Camera.this, R.string.camera_tossing, Toast.LENGTH_SHORT).show();
426 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800427
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700428 if (mStatus == SNAPSHOT_IN_PROGRESS) {
429 // If we are still in the process of taking a picture, then just post a message.
430 mHandler.sendEmptyMessage(RESTART_PREVIEW);
431 } else {
432 restartPreview();
433 }
434 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800435
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700436 private void startTiming() {
437 mWallTimeStart = SystemClock.elapsedRealtime();
438 mThreadTimeStart = Debug.threadCpuTimeNanos();
439 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800440
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700441 private void stopTiming() {
442 mThreadTimeEnd = Debug.threadCpuTimeNanos();
443 mWallTimeEnd = SystemClock.elapsedRealtime();
444 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800445
446 private void storeImage(byte[] data, Location loc) {
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700447 try {
448 if (DEBUG) {
449 startTiming();
450 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800451 long dateTaken = System.currentTimeMillis();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700452 mLastContentUri = ImageManager.instance().addImage(
453 Camera.this,
454 mContentResolver,
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800455 createName(dateTaken),
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700456 "",
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800457 dateTaken,
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700458 // location for the database goes here
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800459 loc,
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700460 0, // the dsp will use the right orientation so don't "double set it"
461 ImageManager.CAMERA_IMAGE_BUCKET_NAME,
462 null);
463
464 if (mLastContentUri == null) {
465 // this means we got an error
466 mCancel = true;
467 }
468 if (!mCancel) {
469 mAddImageCancelable = ImageManager.instance().storeImage(mLastContentUri,
470 Camera.this, mContentResolver, 0, null, data);
471 mAddImageCancelable.get();
472 mAddImageCancelable = null;
473 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800474
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700475 if (DEBUG) {
476 stopTiming();
477 Log.d(TAG, "Storing image took " + (mWallTimeEnd - mWallTimeStart) + " ms. " +
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800478 "Thread time was " + ((mThreadTimeEnd - mThreadTimeStart) / 1000000) +
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700479 " ms.");
480 }
481 } catch (Exception ex) {
482 Log.e(TAG, "Exception while compressing image.", ex);
483 }
484 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800485
486 public void storeImage(byte[] data, android.hardware.Camera camera, Location loc) {
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700487 boolean captureOnly = isPickIntent();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800488
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700489 if (!captureOnly) {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800490 storeImage(data, loc);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700491 sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", mLastContentUri));
492 } else {
493 BitmapFactory.Options options = new BitmapFactory.Options();
494 options.inSampleSize = 4;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800495
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700496 if (DEBUG) {
497 startTiming();
498 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800499
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700500 mCaptureOnlyBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800501
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700502 if (DEBUG) {
503 stopTiming();
504 Log.d(TAG, "Decoded mCaptureOnly bitmap (" + mCaptureOnlyBitmap.getWidth() +
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800505 "x" + mCaptureOnlyBitmap.getHeight() + " ) in " +
506 (mWallTimeEnd - mWallTimeStart) + " ms. Thread time was " +
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700507 ((mThreadTimeEnd - mThreadTimeStart) / 1000000) + " ms.");
508 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800509
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700510 openOptionsMenu();
511 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800512
513
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700514 mCapturing = false;
515 if (mPausing) {
516 closeCamera();
517 }
518 }
519
520 /*
521 * Tells the image capture thread to abort the capture of the
522 * current image.
523 */
524 public void cancelSave() {
525 if (!mCapturing) {
526 return;
527 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800528
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700529 mCancel = true;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800530
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700531 if (mAddImageCancelable != null) {
532 mAddImageCancelable.cancel();
533 }
534 dismissFreezeFrame(false);
535 }
536
537 /*
538 * Initiate the capture of an image.
539 */
540 public void initiate(boolean captureOnly) {
541 if (mCameraDevice == null) {
542 return;
543 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800544
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700545 mCancel = false;
546 mCapturing = true;
547
548 capture(captureOnly);
549 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800550
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700551 public Uri getLastCaptureUri() {
552 return mLastContentUri;
553 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800554
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700555 public Bitmap getLastBitmap() {
556 return mCaptureOnlyBitmap;
557 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800558
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700559 private void capture(boolean captureOnly) {
560 mPreviewing = false;
561 mCaptureOnlyBitmap = null;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800562
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700563 final int latchedOrientation = ImageManager.roundOrientation(mLastOrientation + 90);
564
565 Boolean recordLocation = mPreferences.getBoolean("pref_camera_recordlocation_key", false);
566 Location loc = recordLocation ? getCurrentLocation() : null;
567 android.hardware.Camera.Parameters parameters = mCameraDevice.getParameters();
568 // Quality 75 has visible artifacts, and quality 90 looks great but the files begin to
569 // get large. 85 is a good compromise between the two.
570 parameters.set("jpeg-quality", 85);
571 parameters.set("rotation", latchedOrientation);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800572
573 parameters.remove("gps-latitude");
574 parameters.remove("gps-longitude");
575 parameters.remove("gps-altitude");
576 parameters.remove("gps-timestamp");
577
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700578 if (loc != null) {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800579 double lat = loc.getLatitude();
580 double lon = loc.getLongitude();
581
582 if (lat != 0D && lon != 0d) {
583 parameters.set("gps-latitude", String.valueOf(lat));
584 parameters.set("gps-longitude", String.valueOf(lon));
585 if (loc.hasAltitude())
586 parameters.set("gps-altitude", String.valueOf(loc.getAltitude()));
587 if (loc.getTime() != 0)
588 parameters.set("gps-timestamp", String.valueOf(loc.getTime()));
589 } else {
590 loc = null;
591 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700592 }
593
594 Size pictureSize = parameters.getPictureSize();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700595
596 // resize the SurfaceView to the aspect-ratio of the still image
597 // and so that we can see the full image that was taken
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800598 mSurfaceView.setAspectRatio(pictureSize.width, pictureSize.height);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700599
600 mCameraDevice.setParameters(parameters);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800601
602 mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback, new JpegPictureCallback(loc));
603
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700604 mBlackout.setVisibility(View.VISIBLE);
605 // Comment this out for now until we can decode the preview frame. This currently
606 // just animates black-on-black because the surface flinger blacks out the surface
607 // when the camera driver detaches the buffers.
608 if (false) {
609 Animation a = new android.view.animation.TranslateAnimation(mBlackout.getWidth(), 0 , 0, 0);
610 a.setDuration(450);
611 a.startNow();
612 mBlackout.setAnimation(a);
613 }
614 }
615
616 public void onSnap() {
617 // If we are already in the middle of taking a snapshot then we should just save
618 // the image after we have returned from the camera service.
619 if (mStatus == SNAPSHOT_IN_PROGRESS || mStatus == SNAPSHOT_COMPLETED) {
620 mKeepAndRestartPreview = true;
621 mHandler.sendEmptyMessage(RESTART_PREVIEW);
622 mPostPictureAlert.setVisibility(View.INVISIBLE);
623 return;
624 }
625
626 // Don't check the filesystem here, we can't afford the latency. Instead, check the
627 // cached value which was calculated when the preview was restarted.
628 if (DEBUG) mShutterPressTime = System.currentTimeMillis();
629 if (mPicturesRemaining < 1) {
630 showStorageToast();
631 return;
632 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800633
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700634 mStatus = SNAPSHOT_IN_PROGRESS;
635
636 mKeepAndRestartPreview = !mPreferences.getBoolean("pref_camera_postpicturemenu_key", true);
637
638 boolean getContentAction = isPickIntent();
639 if (getContentAction) {
640 mImageCapture.initiate(true);
641 } else {
642 mImageCapture.initiate(false);
643 }
644 }
645 }
646
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800647
648 static private String createName(long dateTaken) {
649 return DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken).toString();
650 }
651
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700652 static public Matrix GetDisplayMatrix(Bitmap b, ImageView v) {
653 Matrix m = new Matrix();
654 float bw = (float)b.getWidth();
655 float bh = (float)b.getHeight();
656 float vw = (float)v.getWidth();
657 float vh = (float)v.getHeight();
658 float scale, x, y;
659 if (bw*vh > vw*bh) {
660 scale = vh / bh;
661 x = (vw - scale*bw)*0.5F;
662 y = 0;
663 } else {
664 scale = vw / bw;
665 x = 0;
666 y = (vh - scale*bh)*0.5F;
667 }
668 m.setScale(scale, scale, 0.5F, 0.5F);
669 m.postTranslate(x, y);
670 return m;
671 }
672
673 private void postAfterKeep(final Runnable r) {
674 Resources res = getResources();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800675
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700676 if (mSavingProgress != null) {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800677 mSavingProgress = ProgressDialog.show(this, res.getString(R.string.savingImage),
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700678 res.getString(R.string.wait));
679 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800680
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700681 Message msg = mHandler.obtainMessage(KEEP);
682 msg.obj = r;
683 msg.sendToTarget();
684 }
685
686 /** Called with the activity is first created. */
687 @Override
688 public void onCreate(Bundle icicle) {
689 super.onCreate(icicle);
690
691 mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
692
693 mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
694 mContentResolver = getContentResolver();
695
696 //setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
697 requestWindowFeature(Window.FEATURE_PROGRESS);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800698
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700699 Window win = getWindow();
700 win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
701 setContentView(R.layout.camera);
702
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800703 mSurfaceView = (VideoPreview) findViewById(R.id.camera_preview);
704
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700705 // don't set mSurfaceHolder here. We have it set ONLY within
706 // surfaceCreated / surfaceDestroyed, other parts of the code
707 // assume that when it is set, the surface is also set.
708 SurfaceHolder holder = mSurfaceView.getHolder();
709 holder.addCallback(this);
710 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800711
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700712 mBlackout = (ImageView) findViewById(R.id.blackout);
713 mBlackout.setBackgroundDrawable(new ColorDrawable(0xFF000000));
714
715 mPostPictureAlert = findViewById(R.id.post_picture_panel);
716 View b;
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800717
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700718 b = findViewById(R.id.discard);
719 b.setOnClickListener(this);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800720
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700721 b = findViewById(R.id.share);
722 b.setOnClickListener(this);
723
724 b = findViewById(R.id.setas);
725 b.setOnClickListener(this);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800726
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700727 try {
728 mClickSound = new MediaPlayer();
729 AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.camera_click);
730
731 mClickSound.setDataSource(afd.getFileDescriptor(),
732 afd.getStartOffset(),
733 afd.getLength());
734
735 if (mClickSound != null) {
736 mClickSound.setAudioStreamType(AudioManager.STREAM_SYSTEM);
737 mClickSound.prepare();
738 }
739 } catch (Exception ex) {
740 Log.w(TAG, "Couldn't create click sound", ex);
741 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800742
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700743 mOrientationListener = new OrientationListener(this) {
744 public void onOrientationChanged(int orientation) {
745 mLastOrientation = orientation;
746 }
747 };
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800748
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700749 mFocusIndicator = findViewById(R.id.focus_indicator);
750 mFocusBlinkAnimation = AnimationUtils.loadAnimation(this, R.anim.auto_focus_blink);
751 mFocusBlinkAnimation.setRepeatCount(Animation.INFINITE);
752 mFocusBlinkAnimation.setRepeatMode(Animation.REVERSE);
753 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800754
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700755 @Override
756 public void onStart() {
757 super.onStart();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800758
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700759 final View hintView = findViewById(R.id.hint_toast);
760 if (hintView != null)
761 hintView.setVisibility(View.GONE);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800762
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700763 Thread t = new Thread(new Runnable() {
764 public void run() {
765 final boolean storageOK = calculatePicturesRemaining() > 0;
766 if (hintView == null)
767 return;
768
769 if (storageOK) {
770 mHandler.post(new Runnable() {
771 public void run() {
772 hintView.setVisibility(View.VISIBLE);
773 }
774 });
775 mHandler.postDelayed(new Runnable() {
776 public void run() {
777 Animation a = new android.view.animation.AlphaAnimation(1F, 0F);
778 a.setDuration(500);
779 a.startNow();
780 hintView.setAnimation(a);
781 hintView.setVisibility(View.GONE);
782 }
783 }, 3000);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800784 } else {
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700785 mHandler.post(new Runnable() {
786 public void run() {
787 hintView.setVisibility(View.GONE);
788 showStorageToast();
789 }
790 });
791 }
792 }
793 });
794 t.start();
795 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800796
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700797 public void onClick(View v) {
798 switch (v.getId()) {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800799 /*
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700800 case R.id.save: {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800801 mPostPictureAlert.setVisibility(View.GONE);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700802 postAfterKeep(null);
803 break;
804 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800805 */
806
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700807 case R.id.discard: {
808 if (mCaptureObject != null) {
809 mCaptureObject.cancelSave();
810 Uri uri = mCaptureObject.getLastCaptureUri();
811 if (uri != null) {
812 mContentResolver.delete(uri, null, null);
813 }
814 mCaptureObject.dismissFreezeFrame(true);
815 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800816 mPostPictureAlert.setVisibility(View.GONE);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700817 break;
818 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800819
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700820 case R.id.share: {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800821 mPostPictureAlert.setVisibility(View.GONE);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700822 postAfterKeep(new Runnable() {
823 public void run() {
824 Uri u = mCaptureObject.getLastCaptureUri();
825 Intent intent = new Intent();
826 intent.setAction(Intent.ACTION_SEND);
827 intent.setType("image/jpeg");
828 intent.putExtra(Intent.EXTRA_STREAM, u);
829 try {
830 startActivity(Intent.createChooser(intent, getText(R.string.sendImage)));
831 } catch (android.content.ActivityNotFoundException ex) {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800832 Toast.makeText(Camera.this, R.string.no_way_to_share_image, Toast.LENGTH_SHORT).show();
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700833 }
834 }
835 });
836 break;
837 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800838
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700839 case R.id.setas: {
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800840 mPostPictureAlert.setVisibility(View.GONE);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700841 postAfterKeep(new Runnable() {
842 public void run() {
843 Uri u = mCaptureObject.getLastCaptureUri();
844 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA, u);
845 try {
846 startActivity(Intent.createChooser(intent, getText(R.string.setImage)));
847 } catch (android.content.ActivityNotFoundException ex) {
848 Toast.makeText(Camera.this, R.string.no_way_to_share_video, Toast.LENGTH_SHORT).show();
849 }
850 }
851 });
852 break;
853 }
854 }
855 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700856
857 private void showStorageToast() {
858 String noStorageText = null;
859 int remaining = calculatePicturesRemaining();
860
861 if (remaining == NO_STORAGE_ERROR) {
862 noStorageText = getString(R.string.no_storage);
863 } else if (remaining < 1) {
864 noStorageText = getString(R.string.not_enough_space);
865 }
866
867 if (noStorageText != null) {
868 Toast.makeText(this, noStorageText, 5000).show();
869 }
870 }
871
872 @Override
873 public void onResume() {
874 super.onResume();
875 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800876
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700877 mPausing = false;
878 mOrientationListener.enable();
879
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700880 // install an intent filter to receive SD card related events.
881 IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
882 intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
883 intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
884 intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
885 intentFilter.addDataScheme("file");
886 registerReceiver(mReceiver, intentFilter);
887 mDidRegister = true;
888
889 mImageCapture = new ImageCapture();
890
891 restartPreview();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800892
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700893 if (mPreferences.getBoolean("pref_camera_recordlocation_key", false))
894 startReceivingLocationUpdates();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800895
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700896 updateFocusIndicator();
897
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800898 try {
899 mFocusToneGenerator = new ToneGenerator(AudioManager.STREAM_SYSTEM, FOCUS_BEEP_VOLUME);
900 } catch (RuntimeException e) {
901 Log.w(TAG, "Exception caught while creating local tone generator: " + e);
902 mFocusToneGenerator = null;
903 }
904
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700905 mBlackout.setVisibility(View.INVISIBLE);
906 }
907
908 private ImageManager.DataLocation dataLocation() {
909 return ImageManager.DataLocation.EXTERNAL;
910 }
911
912 @Override
913 public void onStop() {
914 keep();
915 stopPreview();
916 closeCamera();
917 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
918 super.onStop();
919 }
920
921 @Override
922 protected void onPause() {
923 keep();
924 mPostPictureAlert.setVisibility(View.INVISIBLE);
925
926 mPausing = true;
927 mOrientationListener.disable();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800928
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700929 stopPreview();
930
931 if (!mImageCapture.mCapturing) {
932 closeCamera();
933 }
934 if (mDidRegister) {
935 unregisterReceiver(mReceiver);
936 mDidRegister = false;
937 }
938 stopReceivingLocationUpdates();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800939
940 if (mFocusToneGenerator != null) {
941 mFocusToneGenerator.release();
942 mFocusToneGenerator = null;
943 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700944
945 super.onPause();
946 }
947
948 @Override
949 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
950 switch (requestCode) {
951 case CROP_MSG: {
952 Intent intent = new Intent();
953 if (data != null) {
954 Bundle extras = data.getExtras();
955 if (extras != null) {
956 intent.putExtras(extras);
957 }
958 }
959 setResult(resultCode, intent);
960 finish();
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800961
962 File path = getFileStreamPath(sTempCropFilename);
963 path.delete();
964
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700965 break;
966 }
967 }
968 }
969
970 private void autoFocus() {
971 updateFocusIndicator();
972 if (!mIsFocusing) {
973 if (mCameraDevice != null) {
974 mIsFocusing = true;
975 mIsFocused = false;
976 mCameraDevice.autoFocus(mAutoFocusCallback);
977 }
978 }
979 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800980
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700981 private void clearFocus() {
982 mIsFocusing = false;
983 mIsFocused = false;
984 mIsFocusButtonPressed = false;
985 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800986
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700987 private void updateFocusIndicator() {
988 mHandler.post(new Runnable() {
989 public void run() {
990 if (mIsFocusing || !mIsFocusButtonPressed) {
991 mFocusIndicator.setVisibility(View.GONE);
992 mFocusIndicator.clearAnimation();
993 } else {
994 if (mIsFocused) {
995 mFocusIndicator.setVisibility(View.VISIBLE);
996 mFocusIndicator.clearAnimation();
997 } else {
998 mFocusIndicator.setVisibility(View.VISIBLE);
999 mFocusIndicator.startAnimation(mFocusBlinkAnimation);
1000 }
1001 }
1002 }
1003 });
1004 }
1005
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001006
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001007 @Override
1008 public boolean onKeyDown(int keyCode, KeyEvent event) {
1009 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1010 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001011
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001012 switch (keyCode) {
1013 case KeyEvent.KEYCODE_BACK:
1014 if (mStatus == SNAPSHOT_IN_PROGRESS || mStatus == SNAPSHOT_COMPLETED) {
1015 if (mPostPictureAlert.getVisibility() == View.VISIBLE) {
1016 keep();
1017 mPostPictureAlert.setVisibility(View.INVISIBLE);
1018 restartPreview();
1019 }
1020 // ignore backs while we're taking a picture
1021 return true;
1022 }
1023 break;
1024 case KeyEvent.KEYCODE_FOCUS:
1025 mIsFocusButtonPressed = true;
1026 if (event.getRepeatCount() == 0) {
1027 if (mPreviewing) {
1028 autoFocus();
1029 } else if (mCaptureObject != null) {
1030 // Save and restart preview
1031 mCaptureObject.onSnap();
1032 }
1033 }
1034 return true;
1035 case KeyEvent.KEYCODE_CAMERA:
1036 case KeyEvent.KEYCODE_DPAD_CENTER:
1037 if (event.getRepeatCount() == 0) {
1038 // The camera operates in focus-priority mode, meaning that we take a picture
1039 // when focusing completes, and only if it completes successfully. If the user
1040 // has half-pressed the shutter and already locked focus, we can take the photo
1041 // right away, otherwise we need to start AF.
1042 if (mIsFocused || !mPreviewing) {
1043 // doesn't get set until the idler runs
1044 if (mCaptureObject != null) {
1045 mCaptureObject.onSnap();
1046 }
1047 clearFocus();
1048 updateFocusIndicator();
1049 } else {
1050 // Half pressing the shutter (i.e. the focus button event) will already have
1051 // requested AF for us, so just request capture on focus here. If AF has
1052 // already failed, we don't want to trigger it again.
1053 mCaptureOnFocus = true;
1054 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && !mIsFocusButtonPressed) {
1055 // But we do need to start AF for DPAD_CENTER
1056 autoFocus();
1057 }
1058 }
1059 }
1060 return true;
1061 case KeyEvent.KEYCODE_MENU:
1062 mPostPictureAlert.setVisibility(View.INVISIBLE);
1063 break;
1064 }
1065
1066 return super.onKeyDown(keyCode, event);
1067 }
1068
1069 @Override
1070 public boolean onKeyUp(int keyCode, KeyEvent event) {
1071 switch (keyCode) {
1072 case KeyEvent.KEYCODE_FOCUS:
1073 clearFocus();
1074 updateFocusIndicator();
1075 return true;
1076 }
1077 return super.onKeyUp(keyCode, event);
1078 }
1079
1080 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001081 // if we're creating the surface, start the preview as well.
1082 boolean preview = holder.isCreating();
1083 setViewFinder(w, h, preview);
1084 mCaptureObject = mImageCapture;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001085 }
1086
1087 public void surfaceCreated(SurfaceHolder holder) {
1088 mSurfaceHolder = holder;
1089 }
1090
1091 public void surfaceDestroyed(SurfaceHolder holder) {
1092 stopPreview();
1093 mSurfaceHolder = null;
1094 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001095
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001096 private void closeCamera() {
1097 if (mCameraDevice != null) {
1098 mCameraDevice.release();
1099 mCameraDevice = null;
1100 mPreviewing = false;
1101 }
1102 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001103
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001104 private boolean ensureCameraDevice() {
1105 if (mCameraDevice == null) {
1106 mCameraDevice = android.hardware.Camera.open();
1107 }
1108 return mCameraDevice != null;
1109 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001110
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001111 private void restartPreview() {
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001112 VideoPreview surfaceView = mSurfaceView;
1113 if (surfaceView == null ||
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001114 surfaceView.getWidth() == 0 || surfaceView.getHeight() == 0) {
1115 return;
1116 }
1117 // make sure the surfaceview fills the whole screen when previewing
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001118 surfaceView.setAspectRatio(VideoPreview.DONT_CARE);
1119 setViewFinder(mOriginalViewFinderWidth, mOriginalViewFinderHeight, true);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001120 mStatus = IDLE;
1121
1122 // Calculate this in advance of each shot so we don't add to shutter latency. It's true that
1123 // someone else could write to the SD card in the mean time and fill it, but that could have
1124 // happened between the shutter press and saving the JPEG too.
1125 // TODO: The best longterm solution is to write a reserve file of maximum JPEG size, always
1126 // let the user take a picture, and delete that file if needed to save the new photo.
1127 calculatePicturesRemaining();
1128 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001129
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001130 private void setViewFinder(int w, int h, boolean startPreview) {
1131 if (mPausing)
1132 return;
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001133
1134 if (mPreviewing &&
1135 w == mViewFinderWidth &&
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001136 h == mViewFinderHeight) {
1137 return;
1138 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001139
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001140 if (!ensureCameraDevice())
1141 return;
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001142
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001143 if (mSurfaceHolder == null)
1144 return;
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001145
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001146 if (isFinishing())
1147 return;
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001148
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001149 if (mPausing)
1150 return;
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001151
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001152 // remember view finder size
1153 mViewFinderWidth = w;
1154 mViewFinderHeight = h;
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001155 if (mOriginalViewFinderHeight == 0) {
1156 mOriginalViewFinderWidth = w;
1157 mOriginalViewFinderHeight = h;
1158 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001159
1160 if (startPreview == false)
1161 return;
1162
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001163 /*
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001164 * start the preview if we're asked to...
1165 */
1166
1167 // we want to start the preview and we're previewing already,
1168 // stop the preview first (this will blank the screen).
1169 if (mPreviewing)
1170 stopPreview();
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001171
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001172 // this blanks the screen if the surface changed, no-op otherwise
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001173 try {
1174 mCameraDevice.setPreviewDisplay(mSurfaceHolder);
1175 } catch (IOException exception) {
1176 mCameraDevice.release();
1177 mCameraDevice = null;
1178 // TODO: add more exception handling logic here
1179 return;
1180 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001181
1182 // request the preview size, the hardware may not honor it,
1183 // if we depended on it we would have to query the size again
1184 android.hardware.Camera.Parameters p = mCameraDevice.getParameters();
1185 p.setPreviewSize(w, h);
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001186 try {
1187 mCameraDevice.setParameters(p);
1188 } catch (IllegalArgumentException e) {
1189 // Ignore this error, it happens in the simulator.
1190 }
1191
1192
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001193 final long wallTimeStart = SystemClock.elapsedRealtime();
1194 final long threadTimeStart = Debug.threadCpuTimeNanos();
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001195
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001196 final Object watchDogSync = new Object();
1197 Thread watchDog = new Thread(new Runnable() {
1198 public void run() {
1199 int next_warning = 1;
1200 while (true) {
1201 try {
1202 synchronized (watchDogSync) {
1203 watchDogSync.wait(1000);
1204 }
1205 } catch (InterruptedException ex) {
1206 //
1207 }
1208 if (mPreviewing)
1209 break;
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001210
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001211 int delay = (int) (SystemClock.elapsedRealtime() - wallTimeStart) / 1000;
1212 if (delay >= next_warning) {
1213 if (delay < 120) {
1214 Log.e(TAG, "preview hasn't started yet in " + delay + " seconds");
1215 } else {
1216 Log.e(TAG, "preview hasn't started yet in " + (delay / 60) + " minutes");
1217 }
1218 if (next_warning < 60) {
1219 next_warning <<= 1;
1220 if (next_warning == 16) {
1221 next_warning = 15;
1222 }
1223 } else {
1224 next_warning += 60;
1225 }
1226 }
1227 }
1228 }
1229 });
1230
1231 watchDog.start();
1232
1233 if (Config.LOGV)
1234 Log.v(TAG, "calling mCameraDevice.startPreview");
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001235 try {
1236 mCameraDevice.startPreview();
1237 } catch (Throwable e) {
1238 // TODO: change Throwable to IOException once android.hardware.Camera.startPreview
1239 // properly declares that it throws IOException.
1240 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001241 mPreviewing = true;
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001242
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001243 synchronized (watchDogSync) {
1244 watchDogSync.notify();
1245 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001246
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001247 long threadTimeEnd = Debug.threadCpuTimeNanos();
1248 long wallTimeEnd = SystemClock.elapsedRealtime();
1249 if ((wallTimeEnd - wallTimeStart) > 3000) {
1250 Log.w(TAG, "startPreview() to " + (wallTimeEnd - wallTimeStart) + " ms. Thread time was"
1251 + (threadTimeEnd - threadTimeStart) / 1000000 + " ms.");
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001252 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001253 }
1254
1255 private void stopPreview() {
1256 if (mCameraDevice != null && mPreviewing) {
1257 mCameraDevice.stopPreview();
1258 }
1259 mPreviewing = false;
1260 }
1261
1262 void gotoGallery() {
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001263 Uri target = Images.Media.INTERNAL_CONTENT_URI;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001264 Intent intent = new Intent(Intent.ACTION_VIEW, target);
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001265 try {
1266 startActivity(intent);
1267 } catch (ActivityNotFoundException e) {
1268 Log.e(TAG, "Could not start gallery activity", e);
1269 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001270 }
1271
1272 void keep() {
1273 cancelSavingNotification();
1274 if (mCaptureObject != null) {
1275 mCaptureObject.dismissFreezeFrame(true);
1276 }
1277 };
1278
1279 void toss() {
1280 cancelSavingNotification();
1281 if (mCaptureObject != null) {
1282 mCaptureObject.cancelSave();
1283 }
1284 };
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001285
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001286 private ImageManager.IImage getImageForURI(Uri uri) {
1287 ImageManager.IImageList list = ImageManager.instance().allImages(
1288 this,
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001289 mContentResolver,
1290 dataLocation(),
1291 ImageManager.INCLUDE_IMAGES,
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001292 ImageManager.SORT_ASCENDING);
1293 ImageManager.IImage image = list.getImageForUri(uri);
1294 list.deactivate();
1295 return image;
1296 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001297
1298
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001299 private void startReceivingLocationUpdates() {
1300 if (mLocationManager != null) {
1301 try {
1302 mLocationManager.requestLocationUpdates(
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001303 LocationManager.NETWORK_PROVIDER,
1304 1000,
1305 0F,
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001306 mLocationListeners[1]);
1307 } catch (java.lang.SecurityException ex) {
1308 // ok
1309 } catch (IllegalArgumentException ex) {
1310 if (Config.LOGD) {
1311 Log.d(TAG, "provider does not exist " + ex.getMessage());
1312 }
1313 }
1314 try {
1315 mLocationManager.requestLocationUpdates(
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001316 LocationManager.GPS_PROVIDER,
1317 1000,
1318 0F,
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001319 mLocationListeners[0]);
1320 } catch (java.lang.SecurityException ex) {
1321 // ok
1322 } catch (IllegalArgumentException ex) {
1323 if (Config.LOGD) {
1324 Log.d(TAG, "provider does not exist " + ex.getMessage());
1325 }
1326 }
1327 }
1328 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001329
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001330 private void stopReceivingLocationUpdates() {
1331 if (mLocationManager != null) {
1332 for (int i = 0; i < mLocationListeners.length; i++) {
1333 try {
1334 mLocationManager.removeUpdates(mLocationListeners[i]);
1335 } catch (Exception ex) {
1336 // ok
1337 }
1338 }
1339 }
1340 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001341
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001342 private Location getCurrentLocation() {
1343 Location l = null;
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001344
1345 // go in best to worst order
1346 for (int i = 0; i < mLocationListeners.length; i++) {
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001347 l = mLocationListeners[i].current();
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001348 if (l != null)
1349 break;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001350 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001351
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001352 return l;
1353 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001354
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001355 @Override
1356 public void onOptionsMenuClosed(Menu menu) {
1357 super.onOptionsMenuClosed(menu);
1358 if (mImageSavingItem && !mMenuSelectionMade) {
1359 // save the image if we presented the "advanced" menu
1360 // which happens if "menu" is pressed while in
1361 // SNAPSHOT_IN_PROGRESS or SNAPSHOT_COMPLETED modes
1362 keep();
1363 mHandler.sendEmptyMessage(RESTART_PREVIEW);
1364 }
1365 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001366
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001367 @Override
1368 public boolean onMenuOpened(int featureId, Menu menu) {
1369 if (featureId == Window.FEATURE_OPTIONS_PANEL) {
1370 if (mStatus == SNAPSHOT_IN_PROGRESS) {
1371 mKeepAndRestartPreview = false;
1372 mHandler.removeMessages(RESTART_PREVIEW);
1373 mMenuSelectionMade = false;
1374 }
1375 }
1376 return super.onMenuOpened(featureId, menu);
1377 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001378
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001379 @Override
1380 public boolean onPrepareOptionsMenu(Menu menu) {
1381 super.onPrepareOptionsMenu(menu);
1382
1383 mMenuSelectionMade = false;
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001384
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001385 for (int i = 1; i <= MenuHelper.MENU_ITEM_MAX; i++) {
1386 if (i != MenuHelper.GENERIC_ITEM) {
1387 menu.setGroupVisible(i, false);
1388 }
1389 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001390
1391 if (mStatus == SNAPSHOT_IN_PROGRESS || mStatus == SNAPSHOT_COMPLETED) {
1392 menu.setGroupVisible(MenuHelper.IMAGE_SAVING_ITEM, true);
1393 mImageSavingItem = true;
1394 } else {
1395 menu.setGroupVisible(MenuHelper.IMAGE_MODE_ITEM, true);
1396 mImageSavingItem = false;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001397 }
1398
1399 if (mCaptureObject != null)
1400 mCaptureObject.cancelAutoDismiss();
1401
1402 return true;
1403 }
1404
1405 private boolean isPickIntent() {
1406 String action = getIntent().getAction();
1407 return (Intent.ACTION_PICK.equals(action) || MediaStore.ACTION_IMAGE_CAPTURE.equals(action));
1408 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001409
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001410 @Override
1411 public boolean onCreateOptionsMenu(Menu menu) {
1412 super.onCreateOptionsMenu(menu);
1413
1414 if (isPickIntent()) {
1415 menu.add(MenuHelper.IMAGE_SAVING_ITEM, MENU_SAVE_SELECT_PHOTOS , 0, R.string.camera_selectphoto).setOnMenuItemClickListener(new OnMenuItemClickListener() {
1416 public boolean onMenuItemClick(MenuItem item) {
1417 Bitmap bitmap = mImageCapture.getLastBitmap();
1418 mCaptureObject.setDone(true);
1419
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001420 String cropValue = null;
1421 Uri saveUri = null;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001422
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001423 Bundle myExtras = getIntent().getExtras();
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001424 if (myExtras != null) {
1425 saveUri = (Uri) myExtras.getParcelable("output");
1426 cropValue = myExtras.getString("crop");
1427 }
1428
1429
1430 if (cropValue == null) {
1431 /*
1432 * First handle the no crop case -- just return the value. If the caller
1433 * specifies a "save uri" then write the data to it's stream. Otherwise,
1434 * pass back a scaled down version of the bitmap directly in the extras.
1435 */
1436 if (saveUri != null) {
1437 OutputStream outputStream = null;
1438 try {
1439 outputStream = mContentResolver.openOutputStream(saveUri);
1440 bitmap.compress(Bitmap.CompressFormat.JPEG, 75, outputStream);
1441 outputStream.close();
1442
1443 setResult(RESULT_OK);
1444 finish();
1445 } catch (IOException ex) {
1446 //
1447 } finally {
1448 if (outputStream != null) {
1449 try {
1450 outputStream.close();
1451 } catch (IOException ex) {
1452
1453 }
1454 }
1455 }
1456 } else {
1457 float scale = .5F;
1458 Matrix m = new Matrix();
1459 m.setScale(scale, scale);
1460
1461 bitmap = Bitmap.createBitmap(bitmap, 0, 0,
1462 bitmap.getWidth(),
1463 bitmap.getHeight(),
1464 m, true);
1465
1466 setResult(RESULT_OK, new Intent("inline-data").putExtra("data", bitmap));
1467 finish();
1468 }
1469 }
1470 else {
1471 /*
1472 * Save the image to a temp file and invoke the cropper
1473 */
1474 Uri tempUri = null;
1475 FileOutputStream tempStream = null;
1476 try {
1477 File path = getFileStreamPath(sTempCropFilename);
1478 path.delete();
1479 tempStream = openFileOutput(sTempCropFilename, 0);
1480 bitmap.compress(Bitmap.CompressFormat.JPEG, 75, tempStream);
1481 tempStream.close();
1482 tempUri = Uri.fromFile(path);
1483 } catch (FileNotFoundException ex) {
1484 setResult(Activity.RESULT_CANCELED);
1485 finish();
1486 return true;
1487 } catch (IOException ex) {
1488 setResult(Activity.RESULT_CANCELED);
1489 finish();
1490 return true;
1491 } finally {
1492 if (tempStream != null) {
1493 try {
1494 tempStream.close();
1495 } catch (IOException ex) {
1496
1497 }
1498 }
1499 }
1500
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001501 Bundle newExtras = new Bundle();
1502 if (cropValue.equals("circle"))
1503 newExtras.putString("circleCrop", "true");
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001504 if (saveUri != null)
1505 newExtras.putParcelable("output", saveUri);
1506 else
1507 newExtras.putBoolean("return-data", true);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001508
1509 Intent cropIntent = new Intent();
1510 cropIntent.setClass(Camera.this, CropImage.class);
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001511 cropIntent.setData(tempUri);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001512 cropIntent.putExtras(newExtras);
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001513
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001514 startActivityForResult(cropIntent, CROP_MSG);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001515 }
1516 return true;
1517 }
1518 });
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001519
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001520 menu.add(MenuHelper.IMAGE_SAVING_ITEM, MENU_SAVE_NEW_PHOTO, 0, R.string.camera_takenewphoto).setOnMenuItemClickListener(new OnMenuItemClickListener() {
1521 public boolean onMenuItemClick(MenuItem item) {
1522 keep();
1523 return true;
1524 }
1525 });
1526 menu.add(MenuHelper.VIDEO_SAVING_ITEM, MENU_SAVE_TAKE_NEW_VIDEO, 0, R.string.camera_takenewvideo).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
1527 public boolean onMenuItemClick(MenuItem item) {
1528 toss();
1529 return true;
1530 }
1531 });
1532 } else {
1533 addBaseMenuItems(menu);
1534 MenuHelper.addImageMenuItems(
1535 menu,
1536 MenuHelper.INCLUDE_ALL & ~MenuHelper.INCLUDE_ROTATE_MENU,
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001537 true,
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001538 Camera.this,
1539 mHandler,
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001540
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001541 // Handler for deletion
1542 new Runnable() {
1543 public void run() {
1544 if (mCaptureObject != null) {
1545 mCaptureObject.cancelSave();
1546 Uri uri = mCaptureObject.getLastCaptureUri();
1547 if (uri != null) {
1548 mContentResolver.delete(uri, null, null);
1549 }
1550 }
1551 }
1552 },
1553 new MenuHelper.MenuInvoker() {
1554 public void run(final MenuHelper.MenuCallback cb) {
1555 mMenuSelectionMade = true;
1556 postAfterKeep(new Runnable() {
1557 public void run() {
1558 cb.run(mSelectedImageGetter.getCurrentImageUri(), mSelectedImageGetter.getCurrentImage());
1559 if (mCaptureObject != null)
1560 mCaptureObject.dismissFreezeFrame(true);
1561 }
1562 });
1563 }
1564 });
1565
1566 MenuItem gallery = menu.add(MenuHelper.IMAGE_SAVING_ITEM, MENU_SAVE_GALLERY_PHOTO, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
1567 public boolean onMenuItemClick(MenuItem item) {
1568 postAfterKeep(new Runnable() {
1569 public void run() {
1570 gotoGallery();
1571 }
1572 });
1573 return true;
1574 }
1575 });
1576 gallery.setIcon(android.R.drawable.ic_menu_gallery);
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001577 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001578 return true;
1579 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001580
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001581 SelectedImageGetter mSelectedImageGetter =
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001582 new SelectedImageGetter() {
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001583 public ImageManager.IImage getCurrentImage() {
1584 return getImageForURI(getCurrentImageUri());
1585 }
1586 public Uri getCurrentImageUri() {
1587 keep();
1588 return mCaptureObject.getLastCaptureUri();
1589 }
1590 };
1591
1592 private int calculatePicturesRemaining() {
1593 try {
1594 if (!ImageManager.instance().hasStorage()) {
1595 mPicturesRemaining = NO_STORAGE_ERROR;
1596 } else {
1597 String storageDirectory = Environment.getExternalStorageDirectory().toString();
1598 StatFs stat = new StatFs(storageDirectory);
1599 float remaining = ((float)stat.getAvailableBlocks() * (float)stat.getBlockSize()) / 400000F;
1600 mPicturesRemaining = (int)remaining;
1601 }
1602 } catch (Exception ex) {
1603 // if we can't stat the filesystem then we don't know how many
1604 // pictures are remaining. it might be zero but just leave it
1605 // blank since we really don't know.
1606 mPicturesRemaining = CANNOT_STAT_ERROR;
1607 }
1608 return mPicturesRemaining;
1609 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001610
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001611 private void addBaseMenuItems(Menu menu) {
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001612 MenuHelper.addSwitchModeMenuItem(menu, this, true);
1613 {
1614 MenuItem gallery = menu.add(MenuHelper.IMAGE_MODE_ITEM, MENU_GALLERY_PHOTOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() {
1615 public boolean onMenuItemClick(MenuItem item) {
1616 gotoGallery();
1617 return true;
1618 }
1619 });
1620 gallery.setIcon(android.R.drawable.ic_menu_gallery);
1621 mGalleryItems.add(gallery);
1622 }
1623 {
1624 MenuItem gallery = menu.add(MenuHelper.VIDEO_MODE_ITEM, MENU_GALLERY_VIDEOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() {
1625 public boolean onMenuItemClick(MenuItem item) {
1626 gotoGallery();
1627 return true;
1628 }
1629 });
1630 gallery.setIcon(android.R.drawable.ic_menu_gallery);
1631 mGalleryItems.add(gallery);
1632 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001633
1634 MenuItem item = menu.add(MenuHelper.GENERIC_ITEM, MENU_SETTINGS, 0, R.string.settings).setOnMenuItemClickListener(new OnMenuItemClickListener() {
1635 public boolean onMenuItemClick(MenuItem item) {
1636 Intent intent = new Intent();
1637 intent.setClass(Camera.this, CameraSettings.class);
1638 startActivity(intent);
1639 return true;
1640 }
1641 });
1642 item.setIcon(android.R.drawable.ic_menu_preferences);
1643 }
The Android Open Source Project0eaec582008-12-17 18:05:54 -08001644
1645 private void showPostPictureAlert() {
1646 mPostPictureAlert.setVisibility(View.VISIBLE);
1647 mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, POST_PICTURE_ALERT_TIMEOUT);
1648 }
1649
1650 private void hidePostPictureAlert() {
1651 cancelRestartPreviewTimeout();
1652 mPostPictureAlert.setVisibility(View.INVISIBLE);
1653 }
1654
1655 private void cancelRestartPreviewTimeout() {
1656 mHandler.removeMessages(RESTART_PREVIEW);
1657 }
1658
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001659}
1660