blob: ff2faf790936460cdc11cf4c03124b4966c6d0d3 [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2012 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
19import android.annotation.TargetApi;
20import android.app.Activity;
21import android.app.AlertDialog;
22import android.content.ContentProviderClient;
23import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070024import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080025import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.SharedPreferences.Editor;
28import android.content.res.Configuration;
29import android.graphics.Bitmap;
30import android.graphics.SurfaceTexture;
31import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080032import android.hardware.Camera.Parameters;
33import android.hardware.Camera.PictureCallback;
34import android.hardware.Camera.Size;
Angus Kong0d00a892013-03-26 11:40:40 -070035import android.hardware.Sensor;
36import android.hardware.SensorEvent;
37import android.hardware.SensorEventListener;
38import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080039import android.location.Location;
40import android.media.CameraProfile;
41import android.net.Uri;
42import android.os.Bundle;
43import android.os.ConditionVariable;
44import android.os.Handler;
45import android.os.Looper;
46import android.os.Message;
47import android.os.MessageQueue;
48import android.os.SystemClock;
49import android.provider.MediaStore;
50import android.util.Log;
Michael Kolb8872c232013-01-29 10:33:22 -080051import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080052import android.view.MotionEvent;
53import android.view.OrientationEventListener;
54import android.view.SurfaceHolder;
55import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080056import android.view.WindowManager;
Michael Kolb8872c232013-01-29 10:33:22 -080057
58import com.android.camera.CameraManager.CameraProxy;
Michael Kolbd6954f32013-03-08 20:43:01 -080059import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
Michael Kolb8872c232013-01-29 10:33:22 -080060import com.android.camera.ui.PopupManager;
Michael Kolb8872c232013-01-29 10:33:22 -080061import com.android.camera.ui.RotateTextToast;
John Reck54987e82013-02-15 15:51:30 -080062import com.android.gallery3d.R;
Michael Kolb8872c232013-01-29 10:33:22 -080063import com.android.gallery3d.common.ApiHelper;
Angus Kong0d00a892013-03-26 11:40:40 -070064import com.android.gallery3d.exif.ExifInterface;
65import com.android.gallery3d.exif.ExifTag;
66import com.android.gallery3d.exif.Rational;
Michael Kolb8872c232013-01-29 10:33:22 -080067import com.android.gallery3d.filtershow.CropExtras;
68import com.android.gallery3d.filtershow.FilterShowActivity;
Bobby Georgescu0a7dd572013-03-12 22:45:17 -070069import com.android.gallery3d.util.UsageStatistics;
Michael Kolb8872c232013-01-29 10:33:22 -080070
71import java.io.File;
72import java.io.FileNotFoundException;
73import java.io.FileOutputStream;
74import java.io.IOException;
75import java.io.OutputStream;
76import java.util.ArrayList;
77import java.util.Collections;
78import java.util.Formatter;
79import java.util.List;
80
81public class PhotoModule
82 implements CameraModule,
Michael Kolbd6954f32013-03-08 20:43:01 -080083 PhotoController,
Michael Kolb8872c232013-01-29 10:33:22 -080084 FocusOverlayManager.Listener,
85 CameraPreference.OnPreferenceChangedListener,
Michael Kolb8872c232013-01-29 10:33:22 -080086 ShutterButton.OnShutterButtonListener,
Michael Kolbd6954f32013-03-08 20:43:01 -080087 MediaSaveService.Listener,
Angus Kong0d00a892013-03-26 11:40:40 -070088 OnCountDownFinishedListener,
89 SensorEventListener {
Michael Kolb8872c232013-01-29 10:33:22 -080090
91 private static final String TAG = "CAM_PhotoModule";
92
93 // We number the request code from 1000 to avoid collision with Gallery.
94 private static final int REQUEST_CROP = 1000;
95
96 private static final int SETUP_PREVIEW = 1;
97 private static final int FIRST_TIME_INIT = 2;
98 private static final int CLEAR_SCREEN_DELAY = 3;
99 private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
100 private static final int CHECK_DISPLAY_ROTATION = 5;
101 private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
102 private static final int SWITCH_CAMERA = 7;
103 private static final int SWITCH_CAMERA_START_ANIMATION = 8;
104 private static final int CAMERA_OPEN_DONE = 9;
105 private static final int START_PREVIEW_DONE = 10;
106 private static final int OPEN_CAMERA_FAIL = 11;
107 private static final int CAMERA_DISABLED = 12;
Michael Kolb8872c232013-01-29 10:33:22 -0800108
109 // The subset of parameters we need to update in setCameraParameters().
110 private static final int UPDATE_PARAM_INITIALIZE = 1;
111 private static final int UPDATE_PARAM_ZOOM = 2;
112 private static final int UPDATE_PARAM_PREFERENCE = 4;
113 private static final int UPDATE_PARAM_ALL = -1;
114
115 // This is the timeout to keep the camera in onPause for the first time
116 // after screen on if the activity is started from secure lock screen.
117 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
118
119 // copied from Camera hierarchy
120 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800121 private CameraProxy mCameraDevice;
122 private int mCameraId;
123 private Parameters mParameters;
124 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800125
126 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800127
128 // these are only used by Camera
129
130 // The activity is going to switch to the specified camera id. This is
131 // needed because texture copy is done in GL thread. -1 means camera is not
132 // switching.
133 protected int mPendingSwitchCameraId = -1;
134 private boolean mOpenCameraFail;
135 private boolean mCameraDisabled;
136
137 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
138 // needed to be updated in mUpdateSet.
139 private int mUpdateSet;
140
141 private static final int SCREEN_DELAY = 2 * 60 * 1000;
142
143 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800144
145 private Parameters mInitialParams;
146 private boolean mFocusAreaSupported;
147 private boolean mMeteringAreaSupported;
148 private boolean mAeLockSupported;
149 private boolean mAwbLockSupported;
150 private boolean mContinousFocusSupported;
151
152 // The degrees of the device rotated clockwise from its natural orientation.
153 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
154 private ComboPreferences mPreferences;
155
156 private static final String sTempCropFilename = "crop-temp";
157
158 private ContentProviderClient mMediaProviderClient;
Michael Kolb8872c232013-01-29 10:33:22 -0800159 private boolean mFaceDetectionStarted = false;
160
Michael Kolb8872c232013-01-29 10:33:22 -0800161 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
162 private String mCropValue;
163 private Uri mSaveUri;
164
Angus Kongce5480e2013-01-29 17:43:48 -0800165 // We use a queue to generated names of the images to be used later
166 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800167 private NamedImages mNamedImages;
168
169 private Runnable mDoSnapRunnable = new Runnable() {
170 @Override
171 public void run() {
172 onShutterButtonClick();
173 }
174 };
175
176 private final StringBuilder mBuilder = new StringBuilder();
177 private final Formatter mFormatter = new Formatter(mBuilder);
178 private final Object[] mFormatterArgs = new Object[1];
179
180 /**
181 * An unpublished intent flag requesting to return as soon as capturing
182 * is completed.
183 *
184 * TODO: consider publishing by moving into MediaStore.
185 */
186 private static final String EXTRA_QUICK_CAPTURE =
187 "android.intent.extra.quickCapture";
188
189 // The display rotation in degrees. This is only valid when mCameraState is
190 // not PREVIEW_STOPPED.
191 private int mDisplayRotation;
192 // The value for android.hardware.Camera.setDisplayOrientation.
193 private int mCameraDisplayOrientation;
194 // The value for UI components like indicators.
195 private int mDisplayOrientation;
196 // The value for android.hardware.Camera.Parameters.setRotation.
197 private int mJpegRotation;
198 private boolean mFirstTimeInitialized;
199 private boolean mIsImageCaptureIntent;
200
Michael Kolb8872c232013-01-29 10:33:22 -0800201 private int mCameraState = PREVIEW_STOPPED;
202 private boolean mSnapshotOnIdle = false;
203
204 private ContentResolver mContentResolver;
205
206 private LocationManager mLocationManager;
207
208 private final ShutterCallback mShutterCallback = new ShutterCallback();
209 private final PostViewPictureCallback mPostViewPictureCallback =
210 new PostViewPictureCallback();
211 private final RawPictureCallback mRawPictureCallback =
212 new RawPictureCallback();
213 private final AutoFocusCallback mAutoFocusCallback =
214 new AutoFocusCallback();
215 private final Object mAutoFocusMoveCallback =
216 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
217 ? new AutoFocusMoveCallback()
218 : null;
219
220 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
221
222 private long mFocusStartTime;
223 private long mShutterCallbackTime;
224 private long mPostViewPictureCallbackTime;
225 private long mRawPictureCallbackTime;
226 private long mJpegPictureCallbackTime;
227 private long mOnResumeTime;
228 private byte[] mJpegImageData;
229
230 // These latency time are for the CameraLatency test.
231 public long mAutoFocusTime;
232 public long mShutterLag;
233 public long mShutterToPictureDisplayedTime;
234 public long mPictureDisplayedToJpegCallbackTime;
235 public long mJpegCallbackFinishTime;
236 public long mCaptureStartTime;
237
238 // This handles everything about focus.
239 private FocusOverlayManager mFocusManager;
240
Michael Kolb8872c232013-01-29 10:33:22 -0800241 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800242
243 private final Handler mHandler = new MainHandler();
244 private PreferenceGroup mPreferenceGroup;
245
246 private boolean mQuickCapture;
247
248 CameraStartUpThread mCameraStartUpThread;
249 ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
250
Angus Kong0d00a892013-03-26 11:40:40 -0700251 private SensorManager mSensorManager;
252 private float[] mGData = new float[3];
253 private float[] mMData = new float[3];
254 private float[] mR = new float[16];
255 private int mHeading = -1;
256
Angus Kongce5480e2013-01-29 17:43:48 -0800257 private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
258 new MediaSaveService.OnMediaSavedListener() {
259 @Override
260 public void onMediaSaved(Uri uri) {
261 if (uri != null) {
262 mActivity.addSecureAlbumItemIfNeeded(false, uri);
263 Util.broadcastNewPicture(mActivity, uri);
264 }
265 }
266 };
Michael Kolb8872c232013-01-29 10:33:22 -0800267
268 // The purpose is not to block the main thread in onCreate and onResume.
269 private class CameraStartUpThread extends Thread {
270 private volatile boolean mCancelled;
271
272 public void cancel() {
273 mCancelled = true;
274 interrupt();
275 }
276
277 public boolean isCanceled() {
278 return mCancelled;
279 }
280
281 @Override
282 public void run() {
283 try {
284 // We need to check whether the activity is paused before long
285 // operations to ensure that onPause() can be done ASAP.
286 if (mCancelled) return;
287 mCameraDevice = Util.openCamera(mActivity, mCameraId);
288 mParameters = mCameraDevice.getParameters();
289 // Wait until all the initialization needed by startPreview are
290 // done.
291 mStartPreviewPrerequisiteReady.block();
292
293 initializeCapabilities();
294 if (mFocusManager == null) initializeFocusManager();
295 if (mCancelled) return;
296 setCameraParameters(UPDATE_PARAM_ALL);
297 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
298 if (mCancelled) return;
299 startPreview();
300 mHandler.sendEmptyMessage(START_PREVIEW_DONE);
301 mOnResumeTime = SystemClock.uptimeMillis();
302 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
303 } catch (CameraHardwareException e) {
304 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
305 } catch (CameraDisabledException e) {
306 mHandler.sendEmptyMessage(CAMERA_DISABLED);
307 }
308 }
309 }
310
311 /**
312 * This Handler is used to post message back onto the main thread of the
313 * application
314 */
315 private class MainHandler extends Handler {
316 @Override
317 public void handleMessage(Message msg) {
318 switch (msg.what) {
319 case SETUP_PREVIEW: {
320 setupPreview();
321 break;
322 }
323
324 case CLEAR_SCREEN_DELAY: {
325 mActivity.getWindow().clearFlags(
326 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
327 break;
328 }
329
330 case FIRST_TIME_INIT: {
331 initializeFirstTime();
332 break;
333 }
334
335 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
336 setCameraParametersWhenIdle(0);
337 break;
338 }
339
340 case CHECK_DISPLAY_ROTATION: {
341 // Set the display orientation if display rotation has changed.
342 // Sometimes this happens when the device is held upside
343 // down and camera app is opened. Rotation animation will
344 // take some time and the rotation value we have got may be
345 // wrong. Framework does not have a callback for this now.
346 if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
347 setDisplayOrientation();
348 }
349 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
350 mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
351 }
352 break;
353 }
354
355 case SHOW_TAP_TO_FOCUS_TOAST: {
356 showTapToFocusToast();
357 break;
358 }
359
360 case SWITCH_CAMERA: {
361 switchCamera();
362 break;
363 }
364
365 case SWITCH_CAMERA_START_ANIMATION: {
366 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
367 break;
368 }
369
370 case CAMERA_OPEN_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800371 onCameraOpened();
Michael Kolb8872c232013-01-29 10:33:22 -0800372 break;
373 }
374
375 case START_PREVIEW_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800376 onPreviewStarted();
Michael Kolb8872c232013-01-29 10:33:22 -0800377 break;
378 }
379
380 case OPEN_CAMERA_FAIL: {
381 mCameraStartUpThread = null;
382 mOpenCameraFail = true;
383 Util.showErrorAndFinish(mActivity,
384 R.string.cannot_connect_camera);
385 break;
386 }
387
388 case CAMERA_DISABLED: {
389 mCameraStartUpThread = null;
390 mCameraDisabled = true;
391 Util.showErrorAndFinish(mActivity,
392 R.string.camera_disabled);
393 break;
394 }
Michael Kolb8872c232013-01-29 10:33:22 -0800395 }
396 }
397 }
398
399 @Override
400 public void init(CameraActivity activity, View parent, boolean reuseNail) {
401 mActivity = activity;
Michael Kolbd6954f32013-03-08 20:43:01 -0800402 mUI = new PhotoUI(activity, this, parent);
Michael Kolb8872c232013-01-29 10:33:22 -0800403 mPreferences = new ComboPreferences(mActivity);
404 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
405 mCameraId = getPreferredCameraId(mPreferences);
406
407 mContentResolver = mActivity.getContentResolver();
408
409 // To reduce startup time, open the camera and start the preview in
410 // another thread.
411 mCameraStartUpThread = new CameraStartUpThread();
412 mCameraStartUpThread.start();
413
Michael Kolb8872c232013-01-29 10:33:22 -0800414
415 // Surface texture is from camera screen nail and startPreview needs it.
416 // This must be done before startPreview.
417 mIsImageCaptureIntent = isImageCaptureIntent();
418 if (reuseNail) {
419 mActivity.reuseCameraScreenNail(!mIsImageCaptureIntent);
420 } else {
421 mActivity.createCameraScreenNail(!mIsImageCaptureIntent);
422 }
423
424 mPreferences.setLocalId(mActivity, mCameraId);
425 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
426 // we need to reset exposure for the preview
427 resetExposureCompensation();
428 // Starting the preview needs preferences, camera screen nail, and
429 // focus area indicator.
430 mStartPreviewPrerequisiteReady.open();
431
432 initializeControlByIntent();
433 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Michael Kolbd6954f32013-03-08 20:43:01 -0800434 mLocationManager = new LocationManager(mActivity, mUI);
Angus Kong0d00a892013-03-26 11:40:40 -0700435 mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
436
Michael Kolbd6954f32013-03-08 20:43:01 -0800437 }
438
439 private void initializeControlByIntent() {
440 mUI.initializeControlByIntent();
441 if (mIsImageCaptureIntent) {
442 setupCaptureParams();
443 }
444 }
445
446 private void onPreviewStarted() {
447 mCameraStartUpThread = null;
448 setCameraState(IDLE);
449 if (!ApiHelper.HAS_SURFACE_TEXTURE) {
450 // This may happen if surfaceCreated has arrived.
451 mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
452 }
453 startFaceDetection();
454 locationFirstRun();
Michael Kolb8872c232013-01-29 10:33:22 -0800455 }
456
457 // Prompt the user to pick to record location for the very first run of
458 // camera only
459 private void locationFirstRun() {
460 if (RecordLocationPreference.isSet(mPreferences)) {
461 return;
462 }
463 if (mActivity.isSecureCamera()) return;
464 // Check if the back camera exists
465 int backCameraId = CameraHolder.instance().getBackCameraId();
466 if (backCameraId == -1) {
467 // If there is no back camera, do not show the prompt.
468 return;
469 }
470
471 new AlertDialog.Builder(mActivity)
472 .setTitle(R.string.remember_location_title)
473 .setMessage(R.string.remember_location_prompt)
474 .setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() {
475 @Override
476 public void onClick(DialogInterface dialog, int arg1) {
477 setLocationPreference(RecordLocationPreference.VALUE_ON);
478 }
479 })
480 .setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() {
481 @Override
482 public void onClick(DialogInterface dialog, int arg1) {
483 dialog.cancel();
484 }
485 })
486 .setOnCancelListener(new DialogInterface.OnCancelListener() {
487 @Override
488 public void onCancel(DialogInterface dialog) {
489 setLocationPreference(RecordLocationPreference.VALUE_OFF);
490 }
491 })
492 .show();
493 }
494
495 private void setLocationPreference(String value) {
496 mPreferences.edit()
497 .putString(CameraSettings.KEY_RECORD_LOCATION, value)
498 .apply();
499 // TODO: Fix this to use the actual onSharedPreferencesChanged listener
500 // instead of invoking manually
501 onSharedPreferenceChanged();
502 }
503
Michael Kolbd6954f32013-03-08 20:43:01 -0800504 private void onCameraOpened() {
505 View root = mUI.getRootView();
Michael Kolb8872c232013-01-29 10:33:22 -0800506 // These depend on camera parameters.
Michael Kolbd6954f32013-03-08 20:43:01 -0800507
508 int width = root.getWidth();
509 int height = root.getHeight();
Doris Liu6a0de792013-02-26 10:54:25 -0800510 mFocusManager.setPreviewSize(width, height);
511 // Full-screen screennail
512 if (Util.getDisplayRotation(mActivity) % 180 == 0) {
513 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(width, height);
514 } else {
515 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(height, width);
516 }
517 // Set touch focus listener.
Michael Kolbd6954f32013-03-08 20:43:01 -0800518 mActivity.setSingleTapUpListener(root);
519 openCameraCommon();
Michael Kolb8872c232013-01-29 10:33:22 -0800520 onFullScreenChanged(mActivity.isInCameraApp());
521 }
522
Michael Kolbd6954f32013-03-08 20:43:01 -0800523 private void switchCamera() {
524 if (mPaused) return;
525
526 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
527 mCameraId = mPendingSwitchCameraId;
528 mPendingSwitchCameraId = -1;
529 setCameraId(mCameraId);
530
531 // from onPause
532 closeCamera();
533 mUI.collapseCameraControls();
534 mUI.clearFaces();
535 if (mFocusManager != null) mFocusManager.removeMessages();
536
537 // Restart the camera and initialize the UI. From onCreate.
538 mPreferences.setLocalId(mActivity, mCameraId);
539 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
540 try {
541 mCameraDevice = Util.openCamera(mActivity, mCameraId);
542 mParameters = mCameraDevice.getParameters();
543 } catch (CameraHardwareException e) {
544 Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
545 return;
546 } catch (CameraDisabledException e) {
547 Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
548 return;
Doris Liu09106a42013-03-05 09:54:25 -0800549 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800550 initializeCapabilities();
551 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
552 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
553 mFocusManager.setMirror(mirror);
554 mFocusManager.setParameters(mInitialParams);
555 setupPreview();
556
557 openCameraCommon();
558
559 if (ApiHelper.HAS_SURFACE_TEXTURE) {
560 // Start switch camera animation. Post a message because
561 // onFrameAvailable from the old camera may already exist.
562 mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
Doris Liu48239f42013-03-04 22:19:10 -0800563 }
564 }
565
Michael Kolbd6954f32013-03-08 20:43:01 -0800566 protected void setCameraId(int cameraId) {
567 ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
568 pref.setValue("" + cameraId);
569 }
570
571 // either open a new camera or switch cameras
572 private void openCameraCommon() {
Michael Kolb8872c232013-01-29 10:33:22 -0800573 loadCameraPreferences();
Michael Kolbd6954f32013-03-08 20:43:01 -0800574
575 mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
576 updateSceneMode();
577 showTapToFocusToastIfNeeded();
578
579
Michael Kolb8872c232013-01-29 10:33:22 -0800580 }
581
Michael Kolbd6954f32013-03-08 20:43:01 -0800582 public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight) {
583 Log.d(TAG, "Preview size changed.");
584 if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
585 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(
586 previewWidth, previewHeight);
587 }
Michael Kolb8872c232013-01-29 10:33:22 -0800588
589 private void resetExposureCompensation() {
590 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
591 CameraSettings.EXPOSURE_DEFAULT_VALUE);
592 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
593 Editor editor = mPreferences.edit();
594 editor.putString(CameraSettings.KEY_EXPOSURE, "0");
595 editor.apply();
596 }
597 }
598
599 private void keepMediaProviderInstance() {
600 // We want to keep a reference to MediaProvider in camera's lifecycle.
601 // TODO: Utilize mMediaProviderClient instance to replace
602 // ContentResolver calls.
603 if (mMediaProviderClient == null) {
604 mMediaProviderClient = mContentResolver
605 .acquireContentProviderClient(MediaStore.AUTHORITY);
606 }
607 }
608
609 // Snapshots can only be taken after this is called. It should be called
610 // once only. We could have done these things in onCreate() but we want to
611 // make preview screen appear as soon as possible.
612 private void initializeFirstTime() {
613 if (mFirstTimeInitialized) return;
614
615 // Initialize location service.
616 boolean recordLocation = RecordLocationPreference.get(
617 mPreferences, mContentResolver);
618 mLocationManager.recordLocation(recordLocation);
619
620 keepMediaProviderInstance();
621
Michael Kolbd6954f32013-03-08 20:43:01 -0800622 mUI.initializeFirstTime();
Angus Kong86d36312013-01-31 18:22:44 -0800623 MediaSaveService s = mActivity.getMediaSaveService();
624 // We set the listener only when both service and shutterbutton
625 // are initialized.
626 if (s != null) {
627 s.setListener(this);
628 }
Michael Kolb8872c232013-01-29 10:33:22 -0800629
Michael Kolb8872c232013-01-29 10:33:22 -0800630 mNamedImages = new NamedImages();
631
632 mFirstTimeInitialized = true;
633 addIdleHandler();
634
635 mActivity.updateStorageSpaceAndHint();
636 }
637
Michael Kolbd6954f32013-03-08 20:43:01 -0800638 // If the activity is paused and resumed, this method will be called in
639 // onResume.
640 private void initializeSecondTime() {
641 // Start location update if needed.
642 boolean recordLocation = RecordLocationPreference.get(
643 mPreferences, mContentResolver);
644 mLocationManager.recordLocation(recordLocation);
645 MediaSaveService s = mActivity.getMediaSaveService();
646 if (s != null) {
647 s.setListener(this);
648 }
649 mNamedImages = new NamedImages();
650 mUI.initializeSecondTime(mParameters);
651 keepMediaProviderInstance();
652 }
653
654 @Override
655 public void onSurfaceCreated(SurfaceHolder holder) {
656 // Do not access the camera if camera start up thread is not finished.
657 if (mCameraDevice == null || mCameraStartUpThread != null)
658 return;
659
660 mCameraDevice.setPreviewDisplayAsync(holder);
661 // This happens when onConfigurationChanged arrives, surface has been
662 // destroyed, and there is no onFullScreenChanged.
663 if (mCameraState == PREVIEW_STOPPED) {
664 setupPreview();
665 }
666 }
667
Michael Kolb8872c232013-01-29 10:33:22 -0800668 private void showTapToFocusToastIfNeeded() {
669 // Show the tap to focus toast if this is the first start.
670 if (mFocusAreaSupported &&
671 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
672 // Delay the toast for one second to wait for orientation.
673 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
674 }
675 }
676
677 private void addIdleHandler() {
678 MessageQueue queue = Looper.myQueue();
679 queue.addIdleHandler(new MessageQueue.IdleHandler() {
680 @Override
681 public boolean queueIdle() {
682 Storage.ensureOSXCompatible();
683 return false;
684 }
685 });
686 }
687
Michael Kolb8872c232013-01-29 10:33:22 -0800688 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
689 @Override
690 public void startFaceDetection() {
691 if (!ApiHelper.HAS_FACE_DETECTION) return;
692 if (mFaceDetectionStarted) return;
693 if (mParameters.getMaxNumDetectedFaces() > 0) {
694 mFaceDetectionStarted = true;
Michael Kolb8872c232013-01-29 10:33:22 -0800695 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Michael Kolbd6954f32013-03-08 20:43:01 -0800696 mUI.onStartFaceDetection(mDisplayOrientation,
697 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
698 mCameraDevice.setFaceDetectionListener(mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800699 mCameraDevice.startFaceDetection();
700 }
701 }
702
703 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
704 @Override
705 public void stopFaceDetection() {
706 if (!ApiHelper.HAS_FACE_DETECTION) return;
707 if (!mFaceDetectionStarted) return;
708 if (mParameters.getMaxNumDetectedFaces() > 0) {
709 mFaceDetectionStarted = false;
710 mCameraDevice.setFaceDetectionListener(null);
711 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800712 mUI.clearFaces();
Michael Kolb8872c232013-01-29 10:33:22 -0800713 }
714 }
715
716 @Override
717 public boolean dispatchTouchEvent(MotionEvent m) {
718 if (mCameraState == SWITCHING_CAMERA) return true;
Michael Kolbd6954f32013-03-08 20:43:01 -0800719 return mUI.dispatchTouchEvent(m);
Michael Kolb8872c232013-01-29 10:33:22 -0800720 }
721
722 private final class ShutterCallback
723 implements android.hardware.Camera.ShutterCallback {
724 @Override
725 public void onShutter() {
726 mShutterCallbackTime = System.currentTimeMillis();
727 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
728 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
729 }
730 }
731
732 private final class PostViewPictureCallback implements PictureCallback {
733 @Override
734 public void onPictureTaken(
735 byte [] data, android.hardware.Camera camera) {
736 mPostViewPictureCallbackTime = System.currentTimeMillis();
737 Log.v(TAG, "mShutterToPostViewCallbackTime = "
738 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
739 + "ms");
740 }
741 }
742
743 private final class RawPictureCallback implements PictureCallback {
744 @Override
745 public void onPictureTaken(
746 byte [] rawData, android.hardware.Camera camera) {
747 mRawPictureCallbackTime = System.currentTimeMillis();
748 Log.v(TAG, "mShutterToRawCallbackTime = "
749 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
750 }
751 }
752
753 private final class JpegPictureCallback implements PictureCallback {
754 Location mLocation;
755
756 public JpegPictureCallback(Location loc) {
757 mLocation = loc;
758 }
759
760 @Override
761 public void onPictureTaken(
762 final byte [] jpegData, final android.hardware.Camera camera) {
763 if (mPaused) {
764 return;
765 }
766 if (mSceneMode == Util.SCENE_MODE_HDR) {
767 mActivity.showSwitcher();
768 mActivity.setSwipingEnabled(true);
769 }
770
771 mJpegPictureCallbackTime = System.currentTimeMillis();
772 // If postview callback has arrived, the captured image is displayed
773 // in postview callback. If not, the captured image is displayed in
774 // raw picture callback.
775 if (mPostViewPictureCallbackTime != 0) {
776 mShutterToPictureDisplayedTime =
777 mPostViewPictureCallbackTime - mShutterCallbackTime;
778 mPictureDisplayedToJpegCallbackTime =
779 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
780 } else {
781 mShutterToPictureDisplayedTime =
782 mRawPictureCallbackTime - mShutterCallbackTime;
783 mPictureDisplayedToJpegCallbackTime =
784 mJpegPictureCallbackTime - mRawPictureCallbackTime;
785 }
786 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
787 + mPictureDisplayedToJpegCallbackTime + "ms");
788
789 // Only animate when in full screen capture mode
790 // i.e. If monkey/a user swipes to the gallery during picture taking,
791 // don't show animation
792 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
793 && mActivity.mShowCameraAppView) {
794 // Finish capture animation
795 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSlide();
796 }
797 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
798 if (!mIsImageCaptureIntent) {
799 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
800 setupPreview();
801 } else {
802 // Camera HAL of some devices have a bug. Starting preview
803 // immediately after taking a picture will fail. Wait some
804 // time before starting the preview.
805 mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
806 }
807 }
808
809 if (!mIsImageCaptureIntent) {
810 // Calculate the width and the height of the jpeg.
811 Size s = mParameters.getPictureSize();
Angus Kong0d00a892013-03-26 11:40:40 -0700812 ExifInterface exif = Exif.getExif(jpegData);
813 int orientation = Exif.getOrientation(exif);
Michael Kolb8872c232013-01-29 10:33:22 -0800814 int width, height;
815 if ((mJpegRotation + orientation) % 180 == 0) {
816 width = s.width;
817 height = s.height;
818 } else {
819 width = s.height;
820 height = s.width;
821 }
822 String title = mNamedImages.getTitle();
823 long date = mNamedImages.getDate();
824 if (title == null) {
825 Log.e(TAG, "Unbalanced name/data pair");
826 } else {
827 if (date == -1) date = mCaptureStartTime;
Angus Kong0d00a892013-03-26 11:40:40 -0700828 if (mHeading >= 0) {
829 // heading direction has been updated by the sensor.
830 ExifTag directionRefTag = exif.buildTag(
831 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
832 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
833 ExifTag directionTag = exif.buildTag(
834 ExifInterface.TAG_GPS_IMG_DIRECTION,
835 new Rational(mHeading, 1));
836 exif.setTag(directionRefTag);
837 exif.setTag(directionTag);
838 }
Angus Kong86d36312013-01-31 18:22:44 -0800839 mActivity.getMediaSaveService().addImage(
840 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700841 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800842 }
843 } else {
844 mJpegImageData = jpegData;
845 if (!mQuickCapture) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800846 mUI.showPostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -0800847 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800848 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800849 }
850 }
851
852 // Check this in advance of each shot so we don't add to shutter
853 // latency. It's true that someone else could write to the SD card in
854 // the mean time and fill it, but that could have happened between the
855 // shutter press and saving the JPEG too.
856 mActivity.updateStorageSpaceAndHint();
857
858 long now = System.currentTimeMillis();
859 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
860 Log.v(TAG, "mJpegCallbackFinishTime = "
861 + mJpegCallbackFinishTime + "ms");
862 mJpegPictureCallbackTime = 0;
863 }
864 }
865
866 private final class AutoFocusCallback
867 implements android.hardware.Camera.AutoFocusCallback {
868 @Override
869 public void onAutoFocus(
870 boolean focused, android.hardware.Camera camera) {
871 if (mPaused) return;
872
873 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
874 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
875 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800876 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb8872c232013-01-29 10:33:22 -0800877 }
878 }
879
880 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
881 private final class AutoFocusMoveCallback
882 implements android.hardware.Camera.AutoFocusMoveCallback {
883 @Override
884 public void onAutoFocusMoving(
885 boolean moving, android.hardware.Camera camera) {
886 mFocusManager.onAutoFocusMoving(moving);
887 }
888 }
889
890 private static class NamedImages {
891 private ArrayList<NamedEntity> mQueue;
892 private boolean mStop;
893 private NamedEntity mNamedEntity;
894
895 public NamedImages() {
896 mQueue = new ArrayList<NamedEntity>();
897 }
898
899 public void nameNewImage(ContentResolver resolver, long date) {
900 NamedEntity r = new NamedEntity();
901 r.title = Util.createJpegName(date);
902 r.date = date;
903 mQueue.add(r);
904 }
905
906 public String getTitle() {
907 if (mQueue.isEmpty()) {
908 mNamedEntity = null;
909 return null;
910 }
911 mNamedEntity = mQueue.get(0);
912 mQueue.remove(0);
913
914 return mNamedEntity.title;
915 }
916
917 // Must be called after getTitle().
918 public long getDate() {
919 if (mNamedEntity == null) return -1;
920 return mNamedEntity.date;
921 }
922
923 private static class NamedEntity {
924 String title;
925 long date;
926 }
927 }
928
929 private void setCameraState(int state) {
930 mCameraState = state;
931 switch (state) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800932 case PhotoController.PREVIEW_STOPPED:
933 case PhotoController.SNAPSHOT_IN_PROGRESS:
934 case PhotoController.FOCUSING:
935 case PhotoController.SWITCHING_CAMERA:
936 mUI.enableGestures(false);
937 break;
938 case PhotoController.IDLE:
939 if (mActivity.isInCameraApp()) {
940 mUI.enableGestures(true);
941 }
942 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800943 }
944 }
945
946 private void animateFlash() {
947 // Only animate when in full screen capture mode
948 // i.e. If monkey/a user swipes to the gallery during picture taking,
949 // don't show animation
950 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
951 && mActivity.mShowCameraAppView) {
952 // Start capture animation.
953 ((CameraScreenNail) mActivity.mCameraScreenNail).animateFlash(mDisplayRotation);
954 }
955 }
956
957 @Override
958 public boolean capture() {
959 // If we are already in the middle of taking a snapshot or the image save request
960 // is full then ignore.
961 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Angus Kong86d36312013-01-31 18:22:44 -0800962 || mCameraState == SWITCHING_CAMERA
963 || mActivity.getMediaSaveService().isQueueFull()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800964 return false;
965 }
966 mCaptureStartTime = System.currentTimeMillis();
967 mPostViewPictureCallbackTime = 0;
968 mJpegImageData = null;
969
970 final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR);
971
972 if (animateBefore) {
973 animateFlash();
974 }
975
976 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800977 int orientation;
978 // We need to be consistent with the framework orientation (i.e. the
979 // orientation of the UI.) when the auto-rotate screen setting is on.
980 if (mActivity.isAutoRotateScreen()) {
981 orientation = (360 - mDisplayRotation) % 360;
982 } else {
983 orientation = mOrientation;
984 }
985 mJpegRotation = Util.getJpegRotation(mCameraId, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800986 mParameters.setRotation(mJpegRotation);
987 Location loc = mLocationManager.getCurrentLocation();
988 Util.setGpsParameters(mParameters, loc);
989 mCameraDevice.setParameters(mParameters);
990
991 mCameraDevice.takePicture2(mShutterCallback, mRawPictureCallback,
992 mPostViewPictureCallback, new JpegPictureCallback(loc),
993 mCameraState, mFocusManager.getFocusState());
994
995 if (!animateBefore) {
996 animateFlash();
997 }
998
999 mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime);
1000
1001 mFaceDetectionStarted = false;
1002 setCameraState(SNAPSHOT_IN_PROGRESS);
1003 return true;
1004 }
1005
1006 @Override
1007 public void setFocusParameters() {
1008 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1009 }
1010
1011 private int getPreferredCameraId(ComboPreferences preferences) {
1012 int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
1013 if (intentCameraId != -1) {
1014 // Testing purpose. Launch a specific camera through the intent
1015 // extras.
1016 return intentCameraId;
1017 } else {
1018 return CameraSettings.readPreferredCameraId(preferences);
1019 }
1020 }
1021
Michael Kolb8872c232013-01-29 10:33:22 -08001022 @Override
1023 public void onFullScreenChanged(boolean full) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001024 mUI.onFullScreenChanged(full);
Michael Kolb8872c232013-01-29 10:33:22 -08001025 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1026 if (mActivity.mCameraScreenNail != null) {
1027 ((CameraScreenNail) mActivity.mCameraScreenNail).setFullScreen(full);
1028 }
1029 return;
1030 }
Michael Kolb8872c232013-01-29 10:33:22 -08001031 }
1032
Michael Kolbd6954f32013-03-08 20:43:01 -08001033 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001034 // If scene mode is set, we cannot set flash mode, white balance, and
1035 // focus mode, instead, we read it from driver
1036 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1037 overrideCameraSettings(mParameters.getFlashMode(),
1038 mParameters.getWhiteBalance(), mParameters.getFocusMode());
1039 } else {
1040 overrideCameraSettings(null, null, null);
1041 }
1042 }
1043
1044 private void overrideCameraSettings(final String flashMode,
1045 final String whiteBalance, final String focusMode) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001046 mUI.overrideSettings(
1047 CameraSettings.KEY_FLASH_MODE, flashMode,
1048 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
1049 CameraSettings.KEY_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001050 }
1051
1052 private void loadCameraPreferences() {
1053 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
1054 mCameraId, CameraHolder.instance().getCameraInfo());
1055 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
1056 }
1057
1058 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001059 public void onOrientationChanged(int orientation) {
1060 // We keep the last known orientation. So if the user first orient
1061 // the camera then point the camera to floor or sky, we still have
1062 // the correct orientation.
1063 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
1064 mOrientation = Util.roundOrientation(orientation, mOrientation);
1065
1066 // Show the toast after getting the first orientation changed.
1067 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1068 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1069 showTapToFocusToast();
1070 }
1071 }
1072
1073 @Override
1074 public void onStop() {
1075 if (mMediaProviderClient != null) {
1076 mMediaProviderClient.release();
1077 mMediaProviderClient = null;
1078 }
1079 }
1080
Michael Kolbd6954f32013-03-08 20:43:01 -08001081 @Override
1082 public void onCaptureCancelled() {
1083 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1084 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001085 }
1086
Michael Kolbd6954f32013-03-08 20:43:01 -08001087 @Override
1088 public void onCaptureRetake() {
Michael Kolb8872c232013-01-29 10:33:22 -08001089 if (mPaused)
1090 return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001091 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001092 setupPreview();
1093 }
1094
Michael Kolbd6954f32013-03-08 20:43:01 -08001095 @Override
1096 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001097 if (mPaused) {
1098 return;
1099 }
1100
1101 byte[] data = mJpegImageData;
1102
1103 if (mCropValue == null) {
1104 // First handle the no crop case -- just return the value. If the
1105 // caller specifies a "save uri" then write the data to its
1106 // stream. Otherwise, pass back a scaled down version of the bitmap
1107 // directly in the extras.
1108 if (mSaveUri != null) {
1109 OutputStream outputStream = null;
1110 try {
1111 outputStream = mContentResolver.openOutputStream(mSaveUri);
1112 outputStream.write(data);
1113 outputStream.close();
1114
1115 mActivity.setResultEx(Activity.RESULT_OK);
1116 mActivity.finish();
1117 } catch (IOException ex) {
1118 // ignore exception
1119 } finally {
1120 Util.closeSilently(outputStream);
1121 }
1122 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001123 ExifInterface exif = Exif.getExif(data);
1124 int orientation = Exif.getOrientation(exif);
Michael Kolb8872c232013-01-29 10:33:22 -08001125 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
1126 bitmap = Util.rotate(bitmap, orientation);
1127 mActivity.setResultEx(Activity.RESULT_OK,
1128 new Intent("inline-data").putExtra("data", bitmap));
1129 mActivity.finish();
1130 }
1131 } else {
1132 // Save the image to a temp file and invoke the cropper
1133 Uri tempUri = null;
1134 FileOutputStream tempStream = null;
1135 try {
1136 File path = mActivity.getFileStreamPath(sTempCropFilename);
1137 path.delete();
1138 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1139 tempStream.write(data);
1140 tempStream.close();
1141 tempUri = Uri.fromFile(path);
1142 } catch (FileNotFoundException ex) {
1143 mActivity.setResultEx(Activity.RESULT_CANCELED);
1144 mActivity.finish();
1145 return;
1146 } catch (IOException ex) {
1147 mActivity.setResultEx(Activity.RESULT_CANCELED);
1148 mActivity.finish();
1149 return;
1150 } finally {
1151 Util.closeSilently(tempStream);
1152 }
1153
1154 Bundle newExtras = new Bundle();
1155 if (mCropValue.equals("circle")) {
1156 newExtras.putString("circleCrop", "true");
1157 }
1158 if (mSaveUri != null) {
1159 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1160 } else {
1161 newExtras.putBoolean(CropExtras.KEY_RETURN_DATA, true);
1162 }
1163 if (mActivity.isSecureCamera()) {
1164 newExtras.putBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, true);
1165 }
1166
1167 Intent cropIntent = new Intent(FilterShowActivity.CROP_ACTION);
1168
1169 cropIntent.setData(tempUri);
1170 cropIntent.putExtras(newExtras);
1171
1172 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1173 }
1174 }
1175
Michael Kolb8872c232013-01-29 10:33:22 -08001176 @Override
1177 public void onShutterButtonFocus(boolean pressed) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001178 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001179 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1180 || (mCameraState == PREVIEW_STOPPED)) return;
1181
1182 // Do not do focus if there is not enough storage.
1183 if (pressed && !canTakePicture()) return;
1184
1185 if (pressed) {
1186 if (mSceneMode == Util.SCENE_MODE_HDR) {
1187 mActivity.hideSwitcher();
1188 mActivity.setSwipingEnabled(false);
1189 }
1190 mFocusManager.onShutterDown();
1191 } else {
Doris Liuda50e052013-02-07 14:36:32 -08001192 // for countdown mode, we need to postpone the shutter release
1193 // i.e. lock the focus during countdown.
Michael Kolbd6954f32013-03-08 20:43:01 -08001194 if (!mUI.isCountingDown()) {
Doris Liuda50e052013-02-07 14:36:32 -08001195 mFocusManager.onShutterUp();
1196 }
Michael Kolb8872c232013-01-29 10:33:22 -08001197 }
1198 }
1199
1200 @Override
1201 public void onShutterButtonClick() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001202 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001203 || (mCameraState == SWITCHING_CAMERA)
1204 || (mCameraState == PREVIEW_STOPPED)) return;
1205
1206 // Do not take the picture if there is not enough storage.
1207 if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
1208 Log.i(TAG, "Not enough space or storage not ready. remaining="
1209 + mActivity.getStorageSpace());
1210 return;
1211 }
1212 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1213
1214 // If the user wants to do a snapshot while the previous one is still
1215 // in progress, remember the fact and do it after we finish the previous
1216 // one and re-start the preview. Snapshot in progress also includes the
1217 // state that autofocus is focusing and a picture will be taken when
1218 // focus callback arrives.
1219 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1220 && !mIsImageCaptureIntent) {
1221 mSnapshotOnIdle = true;
1222 return;
1223 }
1224
1225 String timer = mPreferences.getString(
1226 CameraSettings.KEY_TIMER,
1227 mActivity.getString(R.string.pref_camera_timer_default));
1228 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1229 mActivity.getString(R.string.pref_camera_timer_sound_default))
1230 .equals(mActivity.getString(R.string.setting_on_value));
1231
1232 int seconds = Integer.parseInt(timer);
1233 // When shutter button is pressed, check whether the previous countdown is
1234 // finished. If not, cancel the previous countdown and start a new one.
Michael Kolbd6954f32013-03-08 20:43:01 -08001235 if (mUI.isCountingDown()) {
1236 mUI.cancelCountDown();
1237 }
1238 if (seconds > 0) {
1239 mUI.startCountDown(seconds, playSound);
Michael Kolb8872c232013-01-29 10:33:22 -08001240 } else {
1241 mSnapshotOnIdle = false;
1242 mFocusManager.doSnap();
1243 }
1244 }
1245
1246 @Override
1247 public void installIntentFilter() {
1248 }
1249
1250 @Override
1251 public boolean updateStorageHintOnResume() {
1252 return mFirstTimeInitialized;
1253 }
1254
1255 @Override
1256 public void updateCameraAppView() {
1257 }
1258
1259 @Override
1260 public void onResumeBeforeSuper() {
1261 mPaused = false;
1262 }
1263
1264 @Override
1265 public void onResumeAfterSuper() {
1266 if (mOpenCameraFail || mCameraDisabled) return;
1267
1268 mJpegPictureCallbackTime = 0;
1269 mZoomValue = 0;
1270
1271 // Start the preview if it is not started.
1272 if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
1273 resetExposureCompensation();
1274 mCameraStartUpThread = new CameraStartUpThread();
1275 mCameraStartUpThread.start();
1276 }
1277
1278 // If first time initialization is not finished, put it in the
1279 // message queue.
1280 if (!mFirstTimeInitialized) {
1281 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1282 } else {
1283 initializeSecondTime();
1284 }
1285 keepScreenOnAwhile();
1286
1287 // Dismiss open menu if exists.
1288 PopupManager.getInstance(mActivity).notifyShowPopup(null);
Bobby Georgescu0a7dd572013-03-12 22:45:17 -07001289 UsageStatistics.onContentViewChanged(
1290 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
Angus Kong0d00a892013-03-26 11:40:40 -07001291
1292 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1293 if (gsensor != null) {
1294 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1295 }
1296
1297 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1298 if (msensor != null) {
1299 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1300 }
Michael Kolb8872c232013-01-29 10:33:22 -08001301 }
1302
1303 void waitCameraStartUpThread() {
1304 try {
1305 if (mCameraStartUpThread != null) {
1306 mCameraStartUpThread.cancel();
1307 mCameraStartUpThread.join();
1308 mCameraStartUpThread = null;
1309 setCameraState(IDLE);
1310 }
1311 } catch (InterruptedException e) {
1312 // ignore
1313 }
1314 }
1315
1316 @Override
1317 public void onPauseBeforeSuper() {
1318 mPaused = true;
Angus Kong0d00a892013-03-26 11:40:40 -07001319 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1320 if (gsensor != null) {
1321 mSensorManager.unregisterListener(this, gsensor);
1322 }
1323
1324 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1325 if (msensor != null) {
1326 mSensorManager.unregisterListener(this, msensor);
1327 }
Michael Kolb8872c232013-01-29 10:33:22 -08001328 }
1329
1330 @Override
1331 public void onPauseAfterSuper() {
1332 // Wait the camera start up thread to finish.
1333 waitCameraStartUpThread();
1334
1335 // When camera is started from secure lock screen for the first time
1336 // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1337 // To reduce the latency, keep the camera for a short time so it does
1338 // not need to be opened again.
1339 if (mCameraDevice != null && mActivity.isSecureCamera()
1340 && ActivityBase.isFirstStartAfterScreenOn()) {
1341 ActivityBase.resetFirstStartAfterScreenOn();
1342 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1343 }
1344 // Reset the focus first. Camera CTS does not guarantee that
1345 // cancelAutoFocus is allowed after preview stops.
1346 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1347 mCameraDevice.cancelAutoFocus();
1348 }
1349 stopPreview();
Doris Liub8cf2502013-02-22 14:31:17 -08001350 // Release surface texture.
1351 ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture();
Michael Kolb8872c232013-01-29 10:33:22 -08001352
Angus Kongce5480e2013-01-29 17:43:48 -08001353 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001354
1355 if (mLocationManager != null) mLocationManager.recordLocation(false);
1356
1357 // If we are in an image capture intent and has taken
1358 // a picture, we just clear it in onPause.
1359 mJpegImageData = null;
1360
1361 // Remove the messages in the event queue.
1362 mHandler.removeMessages(SETUP_PREVIEW);
1363 mHandler.removeMessages(FIRST_TIME_INIT);
1364 mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
1365 mHandler.removeMessages(SWITCH_CAMERA);
1366 mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
1367 mHandler.removeMessages(CAMERA_OPEN_DONE);
1368 mHandler.removeMessages(START_PREVIEW_DONE);
1369 mHandler.removeMessages(OPEN_CAMERA_FAIL);
1370 mHandler.removeMessages(CAMERA_DISABLED);
1371
Michael Kolbd6954f32013-03-08 20:43:01 -08001372 closeCamera();
1373
1374 resetScreenOn();
1375 mUI.onPause();
1376
Michael Kolb8872c232013-01-29 10:33:22 -08001377 mPendingSwitchCameraId = -1;
1378 if (mFocusManager != null) mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001379 MediaSaveService s = mActivity.getMediaSaveService();
1380 if (s != null) {
1381 s.setListener(null);
1382 }
Michael Kolb8872c232013-01-29 10:33:22 -08001383 }
1384
Michael Kolb8872c232013-01-29 10:33:22 -08001385 /**
1386 * The focus manager is the first UI related element to get initialized,
1387 * and it requires the RenderOverlay, so initialize it here
1388 */
1389 private void initializeFocusManager() {
1390 // Create FocusManager object. startPreview needs it.
Michael Kolb8872c232013-01-29 10:33:22 -08001391 // if mFocusManager not null, reuse it
1392 // otherwise create a new instance
1393 if (mFocusManager != null) {
1394 mFocusManager.removeMessages();
1395 } else {
1396 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1397 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
1398 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1399 R.array.pref_camera_focusmode_default_array);
1400 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
1401 mInitialParams, this, mirror,
Michael Kolbd6954f32013-03-08 20:43:01 -08001402 mActivity.getMainLooper(), mUI);
Michael Kolb8872c232013-01-29 10:33:22 -08001403 }
1404 }
1405
Michael Kolb8872c232013-01-29 10:33:22 -08001406 @Override
1407 public void onConfigurationChanged(Configuration newConfig) {
1408 Log.v(TAG, "onConfigurationChanged");
1409 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001410 }
1411
1412 @Override
1413 public void onActivityResult(
1414 int requestCode, int resultCode, Intent data) {
1415 switch (requestCode) {
1416 case REQUEST_CROP: {
1417 Intent intent = new Intent();
1418 if (data != null) {
1419 Bundle extras = data.getExtras();
1420 if (extras != null) {
1421 intent.putExtras(extras);
1422 }
1423 }
1424 mActivity.setResultEx(resultCode, intent);
1425 mActivity.finish();
1426
1427 File path = mActivity.getFileStreamPath(sTempCropFilename);
1428 path.delete();
1429
1430 break;
1431 }
1432 }
1433 }
1434
1435 private boolean canTakePicture() {
1436 return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
1437 }
1438
1439 @Override
1440 public void autoFocus() {
1441 mFocusStartTime = System.currentTimeMillis();
1442 mCameraDevice.autoFocus(mAutoFocusCallback);
1443 setCameraState(FOCUSING);
1444 }
1445
1446 @Override
1447 public void cancelAutoFocus() {
1448 mCameraDevice.cancelAutoFocus();
1449 setCameraState(IDLE);
1450 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1451 }
1452
1453 // Preview area is touched. Handle touch focus.
1454 @Override
1455 public void onSingleTapUp(View view, int x, int y) {
1456 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1457 || mCameraState == SNAPSHOT_IN_PROGRESS
1458 || mCameraState == SWITCHING_CAMERA
1459 || mCameraState == PREVIEW_STOPPED) {
1460 return;
1461 }
1462
1463 // Do not trigger touch focus if popup window is opened.
Michael Kolbd6954f32013-03-08 20:43:01 -08001464 if (mUI.removeTopLevelPopup()) return;
Michael Kolb8872c232013-01-29 10:33:22 -08001465
1466 // Check if metering area or focus area is supported.
1467 if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1468 mFocusManager.onSingleTapUp(x, y);
1469 }
1470
1471 @Override
1472 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001473 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001474 }
1475
1476 @Override
1477 public boolean onKeyDown(int keyCode, KeyEvent event) {
1478 switch (keyCode) {
1479 case KeyEvent.KEYCODE_VOLUME_UP:
1480 case KeyEvent.KEYCODE_VOLUME_DOWN:
1481 case KeyEvent.KEYCODE_FOCUS:
1482 if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
1483 if (event.getRepeatCount() == 0) {
1484 onShutterButtonFocus(true);
1485 }
1486 return true;
1487 }
1488 return false;
1489 case KeyEvent.KEYCODE_CAMERA:
1490 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1491 onShutterButtonClick();
1492 }
1493 return true;
1494 case KeyEvent.KEYCODE_DPAD_CENTER:
1495 // If we get a dpad center event without any focused view, move
1496 // the focus to the shutter button and press it.
1497 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1498 // Start auto-focus immediately to reduce shutter lag. After
1499 // the shutter button gets the focus, onShutterButtonFocus()
1500 // will be called again but it is fine.
Michael Kolbd6954f32013-03-08 20:43:01 -08001501 if (mUI.removeTopLevelPopup()) return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001502 onShutterButtonFocus(true);
Michael Kolbd6954f32013-03-08 20:43:01 -08001503 mUI.pressShutterButton();
Michael Kolb8872c232013-01-29 10:33:22 -08001504 }
1505 return true;
1506 }
1507 return false;
1508 }
1509
1510 @Override
1511 public boolean onKeyUp(int keyCode, KeyEvent event) {
1512 switch (keyCode) {
1513 case KeyEvent.KEYCODE_VOLUME_UP:
1514 case KeyEvent.KEYCODE_VOLUME_DOWN:
1515 if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
1516 onShutterButtonClick();
1517 return true;
1518 }
1519 return false;
1520 case KeyEvent.KEYCODE_FOCUS:
1521 if (mFirstTimeInitialized) {
1522 onShutterButtonFocus(false);
1523 }
1524 return true;
1525 }
1526 return false;
1527 }
1528
1529 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1530 private void closeCamera() {
1531 if (mCameraDevice != null) {
1532 mCameraDevice.setZoomChangeListener(null);
1533 if(ApiHelper.HAS_FACE_DETECTION) {
1534 mCameraDevice.setFaceDetectionListener(null);
1535 }
1536 mCameraDevice.setErrorCallback(null);
1537 CameraHolder.instance().release();
1538 mFaceDetectionStarted = false;
1539 mCameraDevice = null;
1540 setCameraState(PREVIEW_STOPPED);
1541 mFocusManager.onCameraReleased();
1542 }
1543 }
1544
1545 private void setDisplayOrientation() {
1546 mDisplayRotation = Util.getDisplayRotation(mActivity);
1547 mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
1548 mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -08001549 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001550 if (mFocusManager != null) {
1551 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1552 }
1553 // GLRoot also uses the DisplayRotation, and needs to be told to layout to update
1554 mActivity.getGLRoot().requestLayoutContentPane();
1555 }
1556
1557 // Only called by UI thread.
1558 private void setupPreview() {
1559 mFocusManager.resetTouchFocus();
1560 startPreview();
1561 setCameraState(IDLE);
1562 startFaceDetection();
1563 }
1564
1565 // This can be called by UI Thread or CameraStartUpThread. So this should
1566 // not modify the views.
1567 private void startPreview() {
1568 mCameraDevice.setErrorCallback(mErrorCallback);
1569
1570 // ICS camera frameworks has a bug. Face detection state is not cleared
1571 // after taking a picture. Stop the preview to work around it. The bug
1572 // was fixed in JB.
1573 if (mCameraState != PREVIEW_STOPPED) stopPreview();
1574
1575 setDisplayOrientation();
1576
1577 if (!mSnapshotOnIdle) {
1578 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1579 // resume it because it may have been paused by autoFocus call.
1580 if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
1581 mCameraDevice.cancelAutoFocus();
1582 }
1583 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1584 }
1585 setCameraParameters(UPDATE_PARAM_ALL);
1586
1587 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1588 CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail;
Michael Kolbd6954f32013-03-08 20:43:01 -08001589 if (mUI.getSurfaceTexture() == null) {
Michael Kolb8872c232013-01-29 10:33:22 -08001590 Size size = mParameters.getPreviewSize();
1591 if (mCameraDisplayOrientation % 180 == 0) {
1592 screenNail.setSize(size.width, size.height);
1593 } else {
1594 screenNail.setSize(size.height, size.width);
1595 }
1596 screenNail.enableAspectRatioClamping();
1597 mActivity.notifyScreenNailChanged();
1598 screenNail.acquireSurfaceTexture();
1599 CameraStartUpThread t = mCameraStartUpThread;
1600 if (t != null && t.isCanceled()) {
1601 return; // Exiting, so no need to get the surface texture.
1602 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001603 mUI.setSurfaceTexture(screenNail.getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001604 }
1605 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
Michael Kolbd6954f32013-03-08 20:43:01 -08001606 Object st = mUI.getSurfaceTexture();
1607 if (st != null) {
1608 mCameraDevice.setPreviewTextureAsync((SurfaceTexture) st);
Michael Kolb8872c232013-01-29 10:33:22 -08001609 }
1610 } else {
1611 mCameraDevice.setDisplayOrientation(mDisplayOrientation);
Michael Kolbd6954f32013-03-08 20:43:01 -08001612 mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
Michael Kolb8872c232013-01-29 10:33:22 -08001613 }
1614
1615 Log.v(TAG, "startPreview");
1616 mCameraDevice.startPreviewAsync();
1617
1618 mFocusManager.onPreviewStarted();
1619
1620 if (mSnapshotOnIdle) {
1621 mHandler.post(mDoSnapRunnable);
1622 }
1623 }
1624
Michael Kolbd6954f32013-03-08 20:43:01 -08001625 @Override
1626 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001627 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1628 Log.v(TAG, "stopPreview");
1629 mCameraDevice.stopPreview();
1630 mFaceDetectionStarted = false;
1631 }
1632 setCameraState(PREVIEW_STOPPED);
1633 if (mFocusManager != null) mFocusManager.onPreviewStopped();
1634 }
1635
1636 @SuppressWarnings("deprecation")
1637 private void updateCameraParametersInitialize() {
1638 // Reset preview frame rate to the maximum because it may be lowered by
1639 // video camera application.
1640 List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates();
1641 if (frameRates != null) {
1642 Integer max = Collections.max(frameRates);
1643 mParameters.setPreviewFrameRate(max);
1644 }
1645
1646 mParameters.set(Util.RECORDING_HINT, Util.FALSE);
1647
1648 // Disable video stabilization. Convenience methods not available in API
1649 // level <= 14
1650 String vstabSupported = mParameters.get("video-stabilization-supported");
1651 if ("true".equals(vstabSupported)) {
1652 mParameters.set("video-stabilization", "false");
1653 }
1654 }
1655
1656 private void updateCameraParametersZoom() {
1657 // Set zoom.
1658 if (mParameters.isZoomSupported()) {
1659 mParameters.setZoom(mZoomValue);
1660 }
1661 }
1662
1663 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1664 private void setAutoExposureLockIfSupported() {
1665 if (mAeLockSupported) {
1666 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1667 }
1668 }
1669
1670 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1671 private void setAutoWhiteBalanceLockIfSupported() {
1672 if (mAwbLockSupported) {
1673 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1674 }
1675 }
1676
1677 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1678 private void setFocusAreasIfSupported() {
1679 if (mFocusAreaSupported) {
1680 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1681 }
1682 }
1683
1684 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1685 private void setMeteringAreasIfSupported() {
1686 if (mMeteringAreaSupported) {
1687 // Use the same area for focus and metering.
1688 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1689 }
1690 }
1691
1692 private void updateCameraParametersPreference() {
1693 setAutoExposureLockIfSupported();
1694 setAutoWhiteBalanceLockIfSupported();
1695 setFocusAreasIfSupported();
1696 setMeteringAreasIfSupported();
1697
1698 // Set picture size.
1699 String pictureSize = mPreferences.getString(
1700 CameraSettings.KEY_PICTURE_SIZE, null);
1701 if (pictureSize == null) {
1702 CameraSettings.initialCameraPictureSize(mActivity, mParameters);
1703 } else {
1704 List<Size> supported = mParameters.getSupportedPictureSizes();
1705 CameraSettings.setCameraPictureSize(
1706 pictureSize, supported, mParameters);
1707 }
1708 Size size = mParameters.getPictureSize();
1709
1710 // Set a preview size that is closest to the viewfinder height and has
1711 // the right aspect ratio.
1712 List<Size> sizes = mParameters.getSupportedPreviewSizes();
1713 Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes,
1714 (double) size.width / size.height);
1715 Size original = mParameters.getPreviewSize();
1716 if (!original.equals(optimalSize)) {
1717 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
1718
1719 // Zoom related settings will be changed for different preview
1720 // sizes, so set and read the parameters to get latest values
1721 mCameraDevice.setParameters(mParameters);
1722 mParameters = mCameraDevice.getParameters();
1723 }
1724 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1725
1726 // Since changing scene mode may change supported values, set scene mode
1727 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1728 // separate preference.
1729 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
1730 mActivity.getString(R.string.pref_camera_hdr_default));
1731 if (mActivity.getString(R.string.setting_on_value).equals(hdr)) {
1732 mSceneMode = Util.SCENE_MODE_HDR;
1733 } else {
1734 mSceneMode = mPreferences.getString(
1735 CameraSettings.KEY_SCENE_MODE,
1736 mActivity.getString(R.string.pref_camera_scenemode_default));
1737 }
1738 if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
1739 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1740 mParameters.setSceneMode(mSceneMode);
1741
1742 // Setting scene mode will change the settings of flash mode,
1743 // white balance, and focus mode. Here we read back the
1744 // parameters, so we can know those settings.
1745 mCameraDevice.setParameters(mParameters);
1746 mParameters = mCameraDevice.getParameters();
1747 }
1748 } else {
1749 mSceneMode = mParameters.getSceneMode();
1750 if (mSceneMode == null) {
1751 mSceneMode = Parameters.SCENE_MODE_AUTO;
1752 }
1753 }
1754
1755 // Set JPEG quality.
1756 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1757 CameraProfile.QUALITY_HIGH);
1758 mParameters.setJpegQuality(jpegQuality);
1759
1760 // For the following settings, we need to check if the settings are
1761 // still supported by latest driver, if not, ignore the settings.
1762
1763 // Set exposure compensation
1764 int value = CameraSettings.readExposure(mPreferences);
1765 int max = mParameters.getMaxExposureCompensation();
1766 int min = mParameters.getMinExposureCompensation();
1767 if (value >= min && value <= max) {
1768 mParameters.setExposureCompensation(value);
1769 } else {
1770 Log.w(TAG, "invalid exposure range: " + value);
1771 }
1772
1773 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1774 // Set flash mode.
1775 String flashMode = mPreferences.getString(
1776 CameraSettings.KEY_FLASH_MODE,
1777 mActivity.getString(R.string.pref_camera_flashmode_default));
1778 List<String> supportedFlash = mParameters.getSupportedFlashModes();
1779 if (Util.isSupported(flashMode, supportedFlash)) {
1780 mParameters.setFlashMode(flashMode);
1781 } else {
1782 flashMode = mParameters.getFlashMode();
1783 if (flashMode == null) {
1784 flashMode = mActivity.getString(
1785 R.string.pref_camera_flashmode_no_flash);
1786 }
1787 }
1788
1789 // Set white balance parameter.
1790 String whiteBalance = mPreferences.getString(
1791 CameraSettings.KEY_WHITE_BALANCE,
1792 mActivity.getString(R.string.pref_camera_whitebalance_default));
1793 if (Util.isSupported(whiteBalance,
1794 mParameters.getSupportedWhiteBalance())) {
1795 mParameters.setWhiteBalance(whiteBalance);
1796 } else {
1797 whiteBalance = mParameters.getWhiteBalance();
1798 if (whiteBalance == null) {
1799 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1800 }
1801 }
1802
1803 // Set focus mode.
1804 mFocusManager.overrideFocusMode(null);
1805 mParameters.setFocusMode(mFocusManager.getFocusMode());
1806 } else {
1807 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1808 }
1809
1810 if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1811 updateAutoFocusMoveCallback();
1812 }
1813 }
1814
1815 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1816 private void updateAutoFocusMoveCallback() {
1817 if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
1818 mCameraDevice.setAutoFocusMoveCallback(
1819 (AutoFocusMoveCallback) mAutoFocusMoveCallback);
1820 } else {
1821 mCameraDevice.setAutoFocusMoveCallback(null);
1822 }
1823 }
1824
1825 // We separate the parameters into several subsets, so we can update only
1826 // the subsets actually need updating. The PREFERENCE set needs extra
1827 // locking because the preference can be changed from GLThread as well.
1828 private void setCameraParameters(int updateSet) {
1829 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1830 updateCameraParametersInitialize();
1831 }
1832
1833 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1834 updateCameraParametersZoom();
1835 }
1836
1837 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
1838 updateCameraParametersPreference();
1839 }
1840
1841 mCameraDevice.setParameters(mParameters);
1842 }
1843
1844 // If the Camera is idle, update the parameters immediately, otherwise
1845 // accumulate them in mUpdateSet and update later.
1846 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1847 mUpdateSet |= additionalUpdateSet;
1848 if (mCameraDevice == null) {
1849 // We will update all the parameters when we open the device, so
1850 // we don't need to do anything now.
1851 mUpdateSet = 0;
1852 return;
1853 } else if (isCameraIdle()) {
1854 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001855 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001856 mUpdateSet = 0;
1857 } else {
1858 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1859 mHandler.sendEmptyMessageDelayed(
1860 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1861 }
1862 }
1863 }
1864
Michael Kolbd6954f32013-03-08 20:43:01 -08001865 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001866 return (mCameraState == IDLE) ||
1867 (mCameraState == PREVIEW_STOPPED) ||
1868 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1869 && (mCameraState != SWITCHING_CAMERA));
1870 }
1871
Michael Kolbd6954f32013-03-08 20:43:01 -08001872 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001873 String action = mActivity.getIntent().getAction();
1874 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
1875 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
1876 }
1877
1878 private void setupCaptureParams() {
1879 Bundle myExtras = mActivity.getIntent().getExtras();
1880 if (myExtras != null) {
1881 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1882 mCropValue = myExtras.getString("crop");
1883 }
1884 }
1885
Michael Kolb8872c232013-01-29 10:33:22 -08001886 @Override
1887 public void onSharedPreferenceChanged() {
1888 // ignore the events after "onPause()"
1889 if (mPaused) return;
1890
1891 boolean recordLocation = RecordLocationPreference.get(
1892 mPreferences, mContentResolver);
1893 mLocationManager.recordLocation(recordLocation);
1894
1895 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
Michael Kolbd6954f32013-03-08 20:43:01 -08001896 mUI.updateOnScreenIndicators(mParameters, mPreferences);
Michael Kolb8872c232013-01-29 10:33:22 -08001897 }
1898
1899 @Override
1900 public void onCameraPickerClicked(int cameraId) {
1901 if (mPaused || mPendingSwitchCameraId != -1) return;
1902
1903 mPendingSwitchCameraId = cameraId;
1904 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1905 Log.v(TAG, "Start to copy texture. cameraId=" + cameraId);
1906 // We need to keep a preview frame for the animation before
1907 // releasing the camera. This will trigger onPreviewTextureCopied.
1908 ((CameraScreenNail) mActivity.mCameraScreenNail).copyTexture();
1909 // Disable all camera controls.
1910 setCameraState(SWITCHING_CAMERA);
1911 } else {
1912 switchCamera();
1913 }
1914 }
1915
Michael Kolb8872c232013-01-29 10:33:22 -08001916 // Preview texture has been copied. Now camera can be released and the
1917 // animation can be started.
1918 @Override
1919 public void onPreviewTextureCopied() {
1920 mHandler.sendEmptyMessage(SWITCH_CAMERA);
1921 }
1922
1923 @Override
1924 public void onCaptureTextureCopied() {
1925 }
1926
1927 @Override
1928 public void onUserInteraction() {
1929 if (!mActivity.isFinishing()) keepScreenOnAwhile();
1930 }
1931
1932 private void resetScreenOn() {
1933 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1934 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1935 }
1936
1937 private void keepScreenOnAwhile() {
1938 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1939 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1940 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1941 }
1942
1943 // TODO: Delete this function after old camera code is removed
1944 @Override
1945 public void onRestorePreferencesClicked() {
1946 }
1947
1948 @Override
1949 public void onOverriddenPreferencesClicked() {
1950 if (mPaused) return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001951 mUI.showPreferencesToast();
Michael Kolb8872c232013-01-29 10:33:22 -08001952 }
1953
1954 private void showTapToFocusToast() {
1955 // TODO: Use a toast?
1956 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1957 // Clear the preference.
1958 Editor editor = mPreferences.edit();
1959 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
1960 editor.apply();
1961 }
1962
1963 private void initializeCapabilities() {
1964 mInitialParams = mCameraDevice.getParameters();
1965 mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
1966 mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
1967 mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
1968 mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams);
1969 mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
1970 Util.FOCUS_MODE_CONTINUOUS_PICTURE);
1971 }
1972
Michael Kolb8872c232013-01-29 10:33:22 -08001973 @Override
1974 public void onCountDownFinished() {
1975 mSnapshotOnIdle = false;
1976 mFocusManager.doSnap();
Doris Liuda50e052013-02-07 14:36:32 -08001977 mFocusManager.onShutterUp();
Michael Kolb8872c232013-01-29 10:33:22 -08001978 }
1979
Michael Kolb8872c232013-01-29 10:33:22 -08001980 @Override
1981 public boolean needsSwitcher() {
1982 return !mIsImageCaptureIntent;
1983 }
1984
Doris Liu6a0de792013-02-26 10:54:25 -08001985 @Override
1986 public boolean needsPieMenu() {
1987 return true;
1988 }
1989
Michael Kolb8872c232013-01-29 10:33:22 -08001990 @Override
1991 public void onShowSwitcherPopup() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001992 mUI.onShowSwitcherPopup();
Michael Kolb8872c232013-01-29 10:33:22 -08001993 }
1994
Angus Kongce5480e2013-01-29 17:43:48 -08001995 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001996 public int onZoomChanged(int index) {
1997 // Not useful to change zoom value when the activity is paused.
1998 if (mPaused) return index;
1999 mZoomValue = index;
2000 if (mParameters == null || mCameraDevice == null) return index;
2001 // Set zoom parameters asynchronously
2002 mParameters.setZoom(mZoomValue);
2003 mCameraDevice.setParametersAsync(mParameters);
2004 Parameters p = mCameraDevice.getParameters();
2005 if (p != null) return p.getZoom();
2006 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08002007 }
2008
2009 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002010 public int getCameraState() {
2011 return mCameraState;
2012 }
2013
2014 @Override
2015 public void onQueueStatus(boolean full) {
2016 mUI.enableShutter(!full);
Angus Kongce5480e2013-01-29 17:43:48 -08002017 }
Angus Kong86d36312013-01-31 18:22:44 -08002018
2019 @Override
2020 public void onMediaSaveServiceConnected(MediaSaveService s) {
2021 // We set the listener only when both service and shutterbutton
2022 // are initialized.
Michael Kolbd6954f32013-03-08 20:43:01 -08002023 if (mFirstTimeInitialized) {
2024 s.setListener(this);
2025 }
Angus Kong86d36312013-01-31 18:22:44 -08002026 }
Angus Kong0d00a892013-03-26 11:40:40 -07002027
2028 @Override
2029 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2030 }
2031
2032 @Override
2033 public void onSensorChanged(SensorEvent event) {
2034 int type = event.sensor.getType();
2035 float[] data;
2036 if (type == Sensor.TYPE_ACCELEROMETER) {
2037 data = mGData;
2038 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2039 data = mMData;
2040 } else {
2041 // we should not be here.
2042 return;
2043 }
2044 for (int i = 0; i < 3 ; i++) {
2045 data[i] = event.values[i];
2046 }
2047 float[] orientation = new float[3];
2048 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2049 SensorManager.getOrientation(mR, orientation);
2050 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2051 if (mHeading < 0) {
2052 mHeading += 360;
2053 }
2054 Log.v(TAG, "heading:" + mHeading);
2055 }
Michael Kolb8872c232013-01-29 10:33:22 -08002056}