blob: 4c627c92c76e27fc7f5e4c2828e357485e7a5fd5 [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;
24import android.content.DialogInterface;
25import android.content.Intent;
26import android.content.SharedPreferences.Editor;
27import android.content.res.Configuration;
28import android.graphics.Bitmap;
29import android.graphics.SurfaceTexture;
30import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080031import android.hardware.Camera.Parameters;
32import android.hardware.Camera.PictureCallback;
33import android.hardware.Camera.Size;
34import android.location.Location;
35import android.media.CameraProfile;
36import android.net.Uri;
37import android.os.Bundle;
38import android.os.ConditionVariable;
39import android.os.Handler;
40import android.os.Looper;
41import android.os.Message;
42import android.os.MessageQueue;
43import android.os.SystemClock;
44import android.provider.MediaStore;
45import android.util.Log;
Michael Kolb8872c232013-01-29 10:33:22 -080046import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080047import android.view.MotionEvent;
48import android.view.OrientationEventListener;
49import android.view.SurfaceHolder;
50import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080051import android.view.WindowManager;
Michael Kolb8872c232013-01-29 10:33:22 -080052
53import com.android.camera.CameraManager.CameraProxy;
Michael Kolbd6954f32013-03-08 20:43:01 -080054import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
Michael Kolb8872c232013-01-29 10:33:22 -080055import com.android.camera.ui.PopupManager;
Michael Kolb8872c232013-01-29 10:33:22 -080056import com.android.camera.ui.RotateTextToast;
John Reck54987e82013-02-15 15:51:30 -080057import com.android.gallery3d.R;
Michael Kolb8872c232013-01-29 10:33:22 -080058import com.android.gallery3d.common.ApiHelper;
59import com.android.gallery3d.filtershow.CropExtras;
60import com.android.gallery3d.filtershow.FilterShowActivity;
61
62import java.io.File;
63import java.io.FileNotFoundException;
64import java.io.FileOutputStream;
65import java.io.IOException;
66import java.io.OutputStream;
67import java.util.ArrayList;
68import java.util.Collections;
69import java.util.Formatter;
70import java.util.List;
71
72public class PhotoModule
73 implements CameraModule,
Michael Kolbd6954f32013-03-08 20:43:01 -080074 PhotoController,
Michael Kolb8872c232013-01-29 10:33:22 -080075 FocusOverlayManager.Listener,
76 CameraPreference.OnPreferenceChangedListener,
Michael Kolb8872c232013-01-29 10:33:22 -080077 ShutterButton.OnShutterButtonListener,
Michael Kolbd6954f32013-03-08 20:43:01 -080078 MediaSaveService.Listener,
79 OnCountDownFinishedListener {
Michael Kolb8872c232013-01-29 10:33:22 -080080
81 private static final String TAG = "CAM_PhotoModule";
82
83 // We number the request code from 1000 to avoid collision with Gallery.
84 private static final int REQUEST_CROP = 1000;
85
86 private static final int SETUP_PREVIEW = 1;
87 private static final int FIRST_TIME_INIT = 2;
88 private static final int CLEAR_SCREEN_DELAY = 3;
89 private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
90 private static final int CHECK_DISPLAY_ROTATION = 5;
91 private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
92 private static final int SWITCH_CAMERA = 7;
93 private static final int SWITCH_CAMERA_START_ANIMATION = 8;
94 private static final int CAMERA_OPEN_DONE = 9;
95 private static final int START_PREVIEW_DONE = 10;
96 private static final int OPEN_CAMERA_FAIL = 11;
97 private static final int CAMERA_DISABLED = 12;
Michael Kolb8872c232013-01-29 10:33:22 -080098
99 // The subset of parameters we need to update in setCameraParameters().
100 private static final int UPDATE_PARAM_INITIALIZE = 1;
101 private static final int UPDATE_PARAM_ZOOM = 2;
102 private static final int UPDATE_PARAM_PREFERENCE = 4;
103 private static final int UPDATE_PARAM_ALL = -1;
104
105 // This is the timeout to keep the camera in onPause for the first time
106 // after screen on if the activity is started from secure lock screen.
107 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
108
109 // copied from Camera hierarchy
110 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800111 private CameraProxy mCameraDevice;
112 private int mCameraId;
113 private Parameters mParameters;
114 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800115
116 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800117
118 // these are only used by Camera
119
120 // The activity is going to switch to the specified camera id. This is
121 // needed because texture copy is done in GL thread. -1 means camera is not
122 // switching.
123 protected int mPendingSwitchCameraId = -1;
124 private boolean mOpenCameraFail;
125 private boolean mCameraDisabled;
126
127 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
128 // needed to be updated in mUpdateSet.
129 private int mUpdateSet;
130
131 private static final int SCREEN_DELAY = 2 * 60 * 1000;
132
133 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800134
135 private Parameters mInitialParams;
136 private boolean mFocusAreaSupported;
137 private boolean mMeteringAreaSupported;
138 private boolean mAeLockSupported;
139 private boolean mAwbLockSupported;
140 private boolean mContinousFocusSupported;
141
142 // The degrees of the device rotated clockwise from its natural orientation.
143 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
144 private ComboPreferences mPreferences;
145
146 private static final String sTempCropFilename = "crop-temp";
147
148 private ContentProviderClient mMediaProviderClient;
Michael Kolb8872c232013-01-29 10:33:22 -0800149 private boolean mFaceDetectionStarted = false;
150
Michael Kolb8872c232013-01-29 10:33:22 -0800151 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
152 private String mCropValue;
153 private Uri mSaveUri;
154
Angus Kongce5480e2013-01-29 17:43:48 -0800155 // We use a queue to generated names of the images to be used later
156 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800157 private NamedImages mNamedImages;
158
159 private Runnable mDoSnapRunnable = new Runnable() {
160 @Override
161 public void run() {
162 onShutterButtonClick();
163 }
164 };
165
166 private final StringBuilder mBuilder = new StringBuilder();
167 private final Formatter mFormatter = new Formatter(mBuilder);
168 private final Object[] mFormatterArgs = new Object[1];
169
170 /**
171 * An unpublished intent flag requesting to return as soon as capturing
172 * is completed.
173 *
174 * TODO: consider publishing by moving into MediaStore.
175 */
176 private static final String EXTRA_QUICK_CAPTURE =
177 "android.intent.extra.quickCapture";
178
179 // The display rotation in degrees. This is only valid when mCameraState is
180 // not PREVIEW_STOPPED.
181 private int mDisplayRotation;
182 // The value for android.hardware.Camera.setDisplayOrientation.
183 private int mCameraDisplayOrientation;
184 // The value for UI components like indicators.
185 private int mDisplayOrientation;
186 // The value for android.hardware.Camera.Parameters.setRotation.
187 private int mJpegRotation;
188 private boolean mFirstTimeInitialized;
189 private boolean mIsImageCaptureIntent;
190
Michael Kolb8872c232013-01-29 10:33:22 -0800191 private int mCameraState = PREVIEW_STOPPED;
192 private boolean mSnapshotOnIdle = false;
193
194 private ContentResolver mContentResolver;
195
196 private LocationManager mLocationManager;
197
198 private final ShutterCallback mShutterCallback = new ShutterCallback();
199 private final PostViewPictureCallback mPostViewPictureCallback =
200 new PostViewPictureCallback();
201 private final RawPictureCallback mRawPictureCallback =
202 new RawPictureCallback();
203 private final AutoFocusCallback mAutoFocusCallback =
204 new AutoFocusCallback();
205 private final Object mAutoFocusMoveCallback =
206 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
207 ? new AutoFocusMoveCallback()
208 : null;
209
210 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
211
212 private long mFocusStartTime;
213 private long mShutterCallbackTime;
214 private long mPostViewPictureCallbackTime;
215 private long mRawPictureCallbackTime;
216 private long mJpegPictureCallbackTime;
217 private long mOnResumeTime;
218 private byte[] mJpegImageData;
219
220 // These latency time are for the CameraLatency test.
221 public long mAutoFocusTime;
222 public long mShutterLag;
223 public long mShutterToPictureDisplayedTime;
224 public long mPictureDisplayedToJpegCallbackTime;
225 public long mJpegCallbackFinishTime;
226 public long mCaptureStartTime;
227
228 // This handles everything about focus.
229 private FocusOverlayManager mFocusManager;
230
Michael Kolb8872c232013-01-29 10:33:22 -0800231 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800232
233 private final Handler mHandler = new MainHandler();
234 private PreferenceGroup mPreferenceGroup;
235
236 private boolean mQuickCapture;
237
238 CameraStartUpThread mCameraStartUpThread;
239 ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
240
Angus Kongce5480e2013-01-29 17:43:48 -0800241 private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
242 new MediaSaveService.OnMediaSavedListener() {
243 @Override
244 public void onMediaSaved(Uri uri) {
245 if (uri != null) {
246 mActivity.addSecureAlbumItemIfNeeded(false, uri);
247 Util.broadcastNewPicture(mActivity, uri);
248 }
249 }
250 };
Michael Kolb8872c232013-01-29 10:33:22 -0800251
252 // The purpose is not to block the main thread in onCreate and onResume.
253 private class CameraStartUpThread extends Thread {
254 private volatile boolean mCancelled;
255
256 public void cancel() {
257 mCancelled = true;
258 interrupt();
259 }
260
261 public boolean isCanceled() {
262 return mCancelled;
263 }
264
265 @Override
266 public void run() {
267 try {
268 // We need to check whether the activity is paused before long
269 // operations to ensure that onPause() can be done ASAP.
270 if (mCancelled) return;
271 mCameraDevice = Util.openCamera(mActivity, mCameraId);
272 mParameters = mCameraDevice.getParameters();
273 // Wait until all the initialization needed by startPreview are
274 // done.
275 mStartPreviewPrerequisiteReady.block();
276
277 initializeCapabilities();
278 if (mFocusManager == null) initializeFocusManager();
279 if (mCancelled) return;
280 setCameraParameters(UPDATE_PARAM_ALL);
281 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
282 if (mCancelled) return;
283 startPreview();
284 mHandler.sendEmptyMessage(START_PREVIEW_DONE);
285 mOnResumeTime = SystemClock.uptimeMillis();
286 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
287 } catch (CameraHardwareException e) {
288 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
289 } catch (CameraDisabledException e) {
290 mHandler.sendEmptyMessage(CAMERA_DISABLED);
291 }
292 }
293 }
294
295 /**
296 * This Handler is used to post message back onto the main thread of the
297 * application
298 */
299 private class MainHandler extends Handler {
300 @Override
301 public void handleMessage(Message msg) {
302 switch (msg.what) {
303 case SETUP_PREVIEW: {
304 setupPreview();
305 break;
306 }
307
308 case CLEAR_SCREEN_DELAY: {
309 mActivity.getWindow().clearFlags(
310 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
311 break;
312 }
313
314 case FIRST_TIME_INIT: {
315 initializeFirstTime();
316 break;
317 }
318
319 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
320 setCameraParametersWhenIdle(0);
321 break;
322 }
323
324 case CHECK_DISPLAY_ROTATION: {
325 // Set the display orientation if display rotation has changed.
326 // Sometimes this happens when the device is held upside
327 // down and camera app is opened. Rotation animation will
328 // take some time and the rotation value we have got may be
329 // wrong. Framework does not have a callback for this now.
330 if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
331 setDisplayOrientation();
332 }
333 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
334 mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
335 }
336 break;
337 }
338
339 case SHOW_TAP_TO_FOCUS_TOAST: {
340 showTapToFocusToast();
341 break;
342 }
343
344 case SWITCH_CAMERA: {
345 switchCamera();
346 break;
347 }
348
349 case SWITCH_CAMERA_START_ANIMATION: {
350 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
351 break;
352 }
353
354 case CAMERA_OPEN_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800355 onCameraOpened();
Michael Kolb8872c232013-01-29 10:33:22 -0800356 break;
357 }
358
359 case START_PREVIEW_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800360 onPreviewStarted();
Michael Kolb8872c232013-01-29 10:33:22 -0800361 break;
362 }
363
364 case OPEN_CAMERA_FAIL: {
365 mCameraStartUpThread = null;
366 mOpenCameraFail = true;
367 Util.showErrorAndFinish(mActivity,
368 R.string.cannot_connect_camera);
369 break;
370 }
371
372 case CAMERA_DISABLED: {
373 mCameraStartUpThread = null;
374 mCameraDisabled = true;
375 Util.showErrorAndFinish(mActivity,
376 R.string.camera_disabled);
377 break;
378 }
Michael Kolb8872c232013-01-29 10:33:22 -0800379 }
380 }
381 }
382
383 @Override
384 public void init(CameraActivity activity, View parent, boolean reuseNail) {
385 mActivity = activity;
Michael Kolbd6954f32013-03-08 20:43:01 -0800386 mUI = new PhotoUI(activity, this, parent);
Michael Kolb8872c232013-01-29 10:33:22 -0800387 mPreferences = new ComboPreferences(mActivity);
388 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
389 mCameraId = getPreferredCameraId(mPreferences);
390
391 mContentResolver = mActivity.getContentResolver();
392
393 // To reduce startup time, open the camera and start the preview in
394 // another thread.
395 mCameraStartUpThread = new CameraStartUpThread();
396 mCameraStartUpThread.start();
397
Michael Kolb8872c232013-01-29 10:33:22 -0800398
399 // Surface texture is from camera screen nail and startPreview needs it.
400 // This must be done before startPreview.
401 mIsImageCaptureIntent = isImageCaptureIntent();
402 if (reuseNail) {
403 mActivity.reuseCameraScreenNail(!mIsImageCaptureIntent);
404 } else {
405 mActivity.createCameraScreenNail(!mIsImageCaptureIntent);
406 }
407
408 mPreferences.setLocalId(mActivity, mCameraId);
409 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
410 // we need to reset exposure for the preview
411 resetExposureCompensation();
412 // Starting the preview needs preferences, camera screen nail, and
413 // focus area indicator.
414 mStartPreviewPrerequisiteReady.open();
415
416 initializeControlByIntent();
417 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Michael Kolbd6954f32013-03-08 20:43:01 -0800418 mLocationManager = new LocationManager(mActivity, mUI);
419 }
420
421 private void initializeControlByIntent() {
422 mUI.initializeControlByIntent();
423 if (mIsImageCaptureIntent) {
424 setupCaptureParams();
425 }
426 }
427
428 private void onPreviewStarted() {
429 mCameraStartUpThread = null;
430 setCameraState(IDLE);
431 if (!ApiHelper.HAS_SURFACE_TEXTURE) {
432 // This may happen if surfaceCreated has arrived.
433 mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
434 }
435 startFaceDetection();
436 locationFirstRun();
Michael Kolb8872c232013-01-29 10:33:22 -0800437 }
438
439 // Prompt the user to pick to record location for the very first run of
440 // camera only
441 private void locationFirstRun() {
442 if (RecordLocationPreference.isSet(mPreferences)) {
443 return;
444 }
445 if (mActivity.isSecureCamera()) return;
446 // Check if the back camera exists
447 int backCameraId = CameraHolder.instance().getBackCameraId();
448 if (backCameraId == -1) {
449 // If there is no back camera, do not show the prompt.
450 return;
451 }
452
453 new AlertDialog.Builder(mActivity)
454 .setTitle(R.string.remember_location_title)
455 .setMessage(R.string.remember_location_prompt)
456 .setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() {
457 @Override
458 public void onClick(DialogInterface dialog, int arg1) {
459 setLocationPreference(RecordLocationPreference.VALUE_ON);
460 }
461 })
462 .setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() {
463 @Override
464 public void onClick(DialogInterface dialog, int arg1) {
465 dialog.cancel();
466 }
467 })
468 .setOnCancelListener(new DialogInterface.OnCancelListener() {
469 @Override
470 public void onCancel(DialogInterface dialog) {
471 setLocationPreference(RecordLocationPreference.VALUE_OFF);
472 }
473 })
474 .show();
475 }
476
477 private void setLocationPreference(String value) {
478 mPreferences.edit()
479 .putString(CameraSettings.KEY_RECORD_LOCATION, value)
480 .apply();
481 // TODO: Fix this to use the actual onSharedPreferencesChanged listener
482 // instead of invoking manually
483 onSharedPreferenceChanged();
484 }
485
Michael Kolbd6954f32013-03-08 20:43:01 -0800486 private void onCameraOpened() {
487 View root = mUI.getRootView();
Michael Kolb8872c232013-01-29 10:33:22 -0800488 // These depend on camera parameters.
Michael Kolbd6954f32013-03-08 20:43:01 -0800489
490 int width = root.getWidth();
491 int height = root.getHeight();
Doris Liu6a0de792013-02-26 10:54:25 -0800492 mFocusManager.setPreviewSize(width, height);
493 // Full-screen screennail
494 if (Util.getDisplayRotation(mActivity) % 180 == 0) {
495 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(width, height);
496 } else {
497 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(height, width);
498 }
499 // Set touch focus listener.
Michael Kolbd6954f32013-03-08 20:43:01 -0800500 mActivity.setSingleTapUpListener(root);
501 openCameraCommon();
Michael Kolb8872c232013-01-29 10:33:22 -0800502 onFullScreenChanged(mActivity.isInCameraApp());
503 }
504
Michael Kolbd6954f32013-03-08 20:43:01 -0800505 private void switchCamera() {
506 if (mPaused) return;
507
508 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
509 mCameraId = mPendingSwitchCameraId;
510 mPendingSwitchCameraId = -1;
511 setCameraId(mCameraId);
512
513 // from onPause
514 closeCamera();
515 mUI.collapseCameraControls();
516 mUI.clearFaces();
517 if (mFocusManager != null) mFocusManager.removeMessages();
518
519 // Restart the camera and initialize the UI. From onCreate.
520 mPreferences.setLocalId(mActivity, mCameraId);
521 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
522 try {
523 mCameraDevice = Util.openCamera(mActivity, mCameraId);
524 mParameters = mCameraDevice.getParameters();
525 } catch (CameraHardwareException e) {
526 Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
527 return;
528 } catch (CameraDisabledException e) {
529 Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
530 return;
Doris Liu09106a42013-03-05 09:54:25 -0800531 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800532 initializeCapabilities();
533 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
534 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
535 mFocusManager.setMirror(mirror);
536 mFocusManager.setParameters(mInitialParams);
537 setupPreview();
538
539 openCameraCommon();
540
541 if (ApiHelper.HAS_SURFACE_TEXTURE) {
542 // Start switch camera animation. Post a message because
543 // onFrameAvailable from the old camera may already exist.
544 mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
Doris Liu48239f42013-03-04 22:19:10 -0800545 }
546 }
547
Michael Kolbd6954f32013-03-08 20:43:01 -0800548 protected void setCameraId(int cameraId) {
549 ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
550 pref.setValue("" + cameraId);
551 }
552
553 // either open a new camera or switch cameras
554 private void openCameraCommon() {
Michael Kolb8872c232013-01-29 10:33:22 -0800555 loadCameraPreferences();
Michael Kolbd6954f32013-03-08 20:43:01 -0800556
557 mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
558 updateSceneMode();
559 showTapToFocusToastIfNeeded();
560
561
Michael Kolb8872c232013-01-29 10:33:22 -0800562 }
563
Michael Kolbd6954f32013-03-08 20:43:01 -0800564 public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight) {
565 Log.d(TAG, "Preview size changed.");
566 if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
567 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(
568 previewWidth, previewHeight);
569 }
Michael Kolb8872c232013-01-29 10:33:22 -0800570
571 private void resetExposureCompensation() {
572 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
573 CameraSettings.EXPOSURE_DEFAULT_VALUE);
574 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
575 Editor editor = mPreferences.edit();
576 editor.putString(CameraSettings.KEY_EXPOSURE, "0");
577 editor.apply();
578 }
579 }
580
581 private void keepMediaProviderInstance() {
582 // We want to keep a reference to MediaProvider in camera's lifecycle.
583 // TODO: Utilize mMediaProviderClient instance to replace
584 // ContentResolver calls.
585 if (mMediaProviderClient == null) {
586 mMediaProviderClient = mContentResolver
587 .acquireContentProviderClient(MediaStore.AUTHORITY);
588 }
589 }
590
591 // Snapshots can only be taken after this is called. It should be called
592 // once only. We could have done these things in onCreate() but we want to
593 // make preview screen appear as soon as possible.
594 private void initializeFirstTime() {
595 if (mFirstTimeInitialized) return;
596
597 // Initialize location service.
598 boolean recordLocation = RecordLocationPreference.get(
599 mPreferences, mContentResolver);
600 mLocationManager.recordLocation(recordLocation);
601
602 keepMediaProviderInstance();
603
Michael Kolbd6954f32013-03-08 20:43:01 -0800604 mUI.initializeFirstTime();
Angus Kong86d36312013-01-31 18:22:44 -0800605 MediaSaveService s = mActivity.getMediaSaveService();
606 // We set the listener only when both service and shutterbutton
607 // are initialized.
608 if (s != null) {
609 s.setListener(this);
610 }
Michael Kolb8872c232013-01-29 10:33:22 -0800611
Michael Kolb8872c232013-01-29 10:33:22 -0800612 mNamedImages = new NamedImages();
613
614 mFirstTimeInitialized = true;
615 addIdleHandler();
616
617 mActivity.updateStorageSpaceAndHint();
618 }
619
Michael Kolbd6954f32013-03-08 20:43:01 -0800620 // If the activity is paused and resumed, this method will be called in
621 // onResume.
622 private void initializeSecondTime() {
623 // Start location update if needed.
624 boolean recordLocation = RecordLocationPreference.get(
625 mPreferences, mContentResolver);
626 mLocationManager.recordLocation(recordLocation);
627 MediaSaveService s = mActivity.getMediaSaveService();
628 if (s != null) {
629 s.setListener(this);
630 }
631 mNamedImages = new NamedImages();
632 mUI.initializeSecondTime(mParameters);
633 keepMediaProviderInstance();
634 }
635
636 @Override
637 public void onSurfaceCreated(SurfaceHolder holder) {
638 // Do not access the camera if camera start up thread is not finished.
639 if (mCameraDevice == null || mCameraStartUpThread != null)
640 return;
641
642 mCameraDevice.setPreviewDisplayAsync(holder);
643 // This happens when onConfigurationChanged arrives, surface has been
644 // destroyed, and there is no onFullScreenChanged.
645 if (mCameraState == PREVIEW_STOPPED) {
646 setupPreview();
647 }
648 }
649
Michael Kolb8872c232013-01-29 10:33:22 -0800650 private void showTapToFocusToastIfNeeded() {
651 // Show the tap to focus toast if this is the first start.
652 if (mFocusAreaSupported &&
653 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
654 // Delay the toast for one second to wait for orientation.
655 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
656 }
657 }
658
659 private void addIdleHandler() {
660 MessageQueue queue = Looper.myQueue();
661 queue.addIdleHandler(new MessageQueue.IdleHandler() {
662 @Override
663 public boolean queueIdle() {
664 Storage.ensureOSXCompatible();
665 return false;
666 }
667 });
668 }
669
Michael Kolb8872c232013-01-29 10:33:22 -0800670 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
671 @Override
672 public void startFaceDetection() {
673 if (!ApiHelper.HAS_FACE_DETECTION) return;
674 if (mFaceDetectionStarted) return;
675 if (mParameters.getMaxNumDetectedFaces() > 0) {
676 mFaceDetectionStarted = true;
Michael Kolb8872c232013-01-29 10:33:22 -0800677 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Michael Kolbd6954f32013-03-08 20:43:01 -0800678 mUI.onStartFaceDetection(mDisplayOrientation,
679 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
680 mCameraDevice.setFaceDetectionListener(mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800681 mCameraDevice.startFaceDetection();
682 }
683 }
684
685 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
686 @Override
687 public void stopFaceDetection() {
688 if (!ApiHelper.HAS_FACE_DETECTION) return;
689 if (!mFaceDetectionStarted) return;
690 if (mParameters.getMaxNumDetectedFaces() > 0) {
691 mFaceDetectionStarted = false;
692 mCameraDevice.setFaceDetectionListener(null);
693 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800694 mUI.clearFaces();
Michael Kolb8872c232013-01-29 10:33:22 -0800695 }
696 }
697
698 @Override
699 public boolean dispatchTouchEvent(MotionEvent m) {
700 if (mCameraState == SWITCHING_CAMERA) return true;
Michael Kolbd6954f32013-03-08 20:43:01 -0800701 return mUI.dispatchTouchEvent(m);
Michael Kolb8872c232013-01-29 10:33:22 -0800702 }
703
704 private final class ShutterCallback
705 implements android.hardware.Camera.ShutterCallback {
706 @Override
707 public void onShutter() {
708 mShutterCallbackTime = System.currentTimeMillis();
709 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
710 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
711 }
712 }
713
714 private final class PostViewPictureCallback implements PictureCallback {
715 @Override
716 public void onPictureTaken(
717 byte [] data, android.hardware.Camera camera) {
718 mPostViewPictureCallbackTime = System.currentTimeMillis();
719 Log.v(TAG, "mShutterToPostViewCallbackTime = "
720 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
721 + "ms");
722 }
723 }
724
725 private final class RawPictureCallback implements PictureCallback {
726 @Override
727 public void onPictureTaken(
728 byte [] rawData, android.hardware.Camera camera) {
729 mRawPictureCallbackTime = System.currentTimeMillis();
730 Log.v(TAG, "mShutterToRawCallbackTime = "
731 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
732 }
733 }
734
735 private final class JpegPictureCallback implements PictureCallback {
736 Location mLocation;
737
738 public JpegPictureCallback(Location loc) {
739 mLocation = loc;
740 }
741
742 @Override
743 public void onPictureTaken(
744 final byte [] jpegData, final android.hardware.Camera camera) {
745 if (mPaused) {
746 return;
747 }
748 if (mSceneMode == Util.SCENE_MODE_HDR) {
749 mActivity.showSwitcher();
750 mActivity.setSwipingEnabled(true);
751 }
752
753 mJpegPictureCallbackTime = System.currentTimeMillis();
754 // If postview callback has arrived, the captured image is displayed
755 // in postview callback. If not, the captured image is displayed in
756 // raw picture callback.
757 if (mPostViewPictureCallbackTime != 0) {
758 mShutterToPictureDisplayedTime =
759 mPostViewPictureCallbackTime - mShutterCallbackTime;
760 mPictureDisplayedToJpegCallbackTime =
761 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
762 } else {
763 mShutterToPictureDisplayedTime =
764 mRawPictureCallbackTime - mShutterCallbackTime;
765 mPictureDisplayedToJpegCallbackTime =
766 mJpegPictureCallbackTime - mRawPictureCallbackTime;
767 }
768 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
769 + mPictureDisplayedToJpegCallbackTime + "ms");
770
771 // Only animate when in full screen capture mode
772 // i.e. If monkey/a user swipes to the gallery during picture taking,
773 // don't show animation
774 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
775 && mActivity.mShowCameraAppView) {
776 // Finish capture animation
777 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSlide();
778 }
779 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
780 if (!mIsImageCaptureIntent) {
781 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
782 setupPreview();
783 } else {
784 // Camera HAL of some devices have a bug. Starting preview
785 // immediately after taking a picture will fail. Wait some
786 // time before starting the preview.
787 mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
788 }
789 }
790
791 if (!mIsImageCaptureIntent) {
792 // Calculate the width and the height of the jpeg.
793 Size s = mParameters.getPictureSize();
794 int orientation = Exif.getOrientation(jpegData);
795 int width, height;
796 if ((mJpegRotation + orientation) % 180 == 0) {
797 width = s.width;
798 height = s.height;
799 } else {
800 width = s.height;
801 height = s.width;
802 }
803 String title = mNamedImages.getTitle();
804 long date = mNamedImages.getDate();
805 if (title == null) {
806 Log.e(TAG, "Unbalanced name/data pair");
807 } else {
808 if (date == -1) date = mCaptureStartTime;
Angus Kong86d36312013-01-31 18:22:44 -0800809 mActivity.getMediaSaveService().addImage(
810 jpegData, title, date, mLocation, width, height,
Angus Kongce5480e2013-01-29 17:43:48 -0800811 orientation, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800812 }
813 } else {
814 mJpegImageData = jpegData;
815 if (!mQuickCapture) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800816 mUI.showPostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -0800817 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800818 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800819 }
820 }
821
822 // Check this in advance of each shot so we don't add to shutter
823 // latency. It's true that someone else could write to the SD card in
824 // the mean time and fill it, but that could have happened between the
825 // shutter press and saving the JPEG too.
826 mActivity.updateStorageSpaceAndHint();
827
828 long now = System.currentTimeMillis();
829 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
830 Log.v(TAG, "mJpegCallbackFinishTime = "
831 + mJpegCallbackFinishTime + "ms");
832 mJpegPictureCallbackTime = 0;
833 }
834 }
835
836 private final class AutoFocusCallback
837 implements android.hardware.Camera.AutoFocusCallback {
838 @Override
839 public void onAutoFocus(
840 boolean focused, android.hardware.Camera camera) {
841 if (mPaused) return;
842
843 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
844 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
845 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800846 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb8872c232013-01-29 10:33:22 -0800847 }
848 }
849
850 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
851 private final class AutoFocusMoveCallback
852 implements android.hardware.Camera.AutoFocusMoveCallback {
853 @Override
854 public void onAutoFocusMoving(
855 boolean moving, android.hardware.Camera camera) {
856 mFocusManager.onAutoFocusMoving(moving);
857 }
858 }
859
860 private static class NamedImages {
861 private ArrayList<NamedEntity> mQueue;
862 private boolean mStop;
863 private NamedEntity mNamedEntity;
864
865 public NamedImages() {
866 mQueue = new ArrayList<NamedEntity>();
867 }
868
869 public void nameNewImage(ContentResolver resolver, long date) {
870 NamedEntity r = new NamedEntity();
871 r.title = Util.createJpegName(date);
872 r.date = date;
873 mQueue.add(r);
874 }
875
876 public String getTitle() {
877 if (mQueue.isEmpty()) {
878 mNamedEntity = null;
879 return null;
880 }
881 mNamedEntity = mQueue.get(0);
882 mQueue.remove(0);
883
884 return mNamedEntity.title;
885 }
886
887 // Must be called after getTitle().
888 public long getDate() {
889 if (mNamedEntity == null) return -1;
890 return mNamedEntity.date;
891 }
892
893 private static class NamedEntity {
894 String title;
895 long date;
896 }
897 }
898
899 private void setCameraState(int state) {
900 mCameraState = state;
901 switch (state) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800902 case PhotoController.PREVIEW_STOPPED:
903 case PhotoController.SNAPSHOT_IN_PROGRESS:
904 case PhotoController.FOCUSING:
905 case PhotoController.SWITCHING_CAMERA:
906 mUI.enableGestures(false);
907 break;
908 case PhotoController.IDLE:
909 if (mActivity.isInCameraApp()) {
910 mUI.enableGestures(true);
911 }
912 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800913 }
914 }
915
916 private void animateFlash() {
917 // Only animate when in full screen capture mode
918 // i.e. If monkey/a user swipes to the gallery during picture taking,
919 // don't show animation
920 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
921 && mActivity.mShowCameraAppView) {
922 // Start capture animation.
923 ((CameraScreenNail) mActivity.mCameraScreenNail).animateFlash(mDisplayRotation);
924 }
925 }
926
927 @Override
928 public boolean capture() {
929 // If we are already in the middle of taking a snapshot or the image save request
930 // is full then ignore.
931 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Angus Kong86d36312013-01-31 18:22:44 -0800932 || mCameraState == SWITCHING_CAMERA
933 || mActivity.getMediaSaveService().isQueueFull()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800934 return false;
935 }
936 mCaptureStartTime = System.currentTimeMillis();
937 mPostViewPictureCallbackTime = 0;
938 mJpegImageData = null;
939
940 final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR);
941
942 if (animateBefore) {
943 animateFlash();
944 }
945
946 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800947 int orientation;
948 // We need to be consistent with the framework orientation (i.e. the
949 // orientation of the UI.) when the auto-rotate screen setting is on.
950 if (mActivity.isAutoRotateScreen()) {
951 orientation = (360 - mDisplayRotation) % 360;
952 } else {
953 orientation = mOrientation;
954 }
955 mJpegRotation = Util.getJpegRotation(mCameraId, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800956 mParameters.setRotation(mJpegRotation);
957 Location loc = mLocationManager.getCurrentLocation();
958 Util.setGpsParameters(mParameters, loc);
959 mCameraDevice.setParameters(mParameters);
960
961 mCameraDevice.takePicture2(mShutterCallback, mRawPictureCallback,
962 mPostViewPictureCallback, new JpegPictureCallback(loc),
963 mCameraState, mFocusManager.getFocusState());
964
965 if (!animateBefore) {
966 animateFlash();
967 }
968
969 mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime);
970
971 mFaceDetectionStarted = false;
972 setCameraState(SNAPSHOT_IN_PROGRESS);
973 return true;
974 }
975
976 @Override
977 public void setFocusParameters() {
978 setCameraParameters(UPDATE_PARAM_PREFERENCE);
979 }
980
981 private int getPreferredCameraId(ComboPreferences preferences) {
982 int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
983 if (intentCameraId != -1) {
984 // Testing purpose. Launch a specific camera through the intent
985 // extras.
986 return intentCameraId;
987 } else {
988 return CameraSettings.readPreferredCameraId(preferences);
989 }
990 }
991
Michael Kolb8872c232013-01-29 10:33:22 -0800992 @Override
993 public void onFullScreenChanged(boolean full) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800994 mUI.onFullScreenChanged(full);
Michael Kolb8872c232013-01-29 10:33:22 -0800995 if (ApiHelper.HAS_SURFACE_TEXTURE) {
996 if (mActivity.mCameraScreenNail != null) {
997 ((CameraScreenNail) mActivity.mCameraScreenNail).setFullScreen(full);
998 }
999 return;
1000 }
Michael Kolb8872c232013-01-29 10:33:22 -08001001 }
1002
Michael Kolbd6954f32013-03-08 20:43:01 -08001003 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001004 // If scene mode is set, we cannot set flash mode, white balance, and
1005 // focus mode, instead, we read it from driver
1006 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1007 overrideCameraSettings(mParameters.getFlashMode(),
1008 mParameters.getWhiteBalance(), mParameters.getFocusMode());
1009 } else {
1010 overrideCameraSettings(null, null, null);
1011 }
1012 }
1013
1014 private void overrideCameraSettings(final String flashMode,
1015 final String whiteBalance, final String focusMode) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001016 mUI.overrideSettings(
1017 CameraSettings.KEY_FLASH_MODE, flashMode,
1018 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
1019 CameraSettings.KEY_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001020 }
1021
1022 private void loadCameraPreferences() {
1023 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
1024 mCameraId, CameraHolder.instance().getCameraInfo());
1025 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
1026 }
1027
1028 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001029 public void onOrientationChanged(int orientation) {
1030 // We keep the last known orientation. So if the user first orient
1031 // the camera then point the camera to floor or sky, we still have
1032 // the correct orientation.
1033 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
1034 mOrientation = Util.roundOrientation(orientation, mOrientation);
1035
1036 // Show the toast after getting the first orientation changed.
1037 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1038 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1039 showTapToFocusToast();
1040 }
1041 }
1042
1043 @Override
1044 public void onStop() {
1045 if (mMediaProviderClient != null) {
1046 mMediaProviderClient.release();
1047 mMediaProviderClient = null;
1048 }
1049 }
1050
Michael Kolbd6954f32013-03-08 20:43:01 -08001051 @Override
1052 public void onCaptureCancelled() {
1053 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1054 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001055 }
1056
Michael Kolbd6954f32013-03-08 20:43:01 -08001057 @Override
1058 public void onCaptureRetake() {
Michael Kolb8872c232013-01-29 10:33:22 -08001059 if (mPaused)
1060 return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001061 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001062 setupPreview();
1063 }
1064
Michael Kolbd6954f32013-03-08 20:43:01 -08001065 @Override
1066 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001067 if (mPaused) {
1068 return;
1069 }
1070
1071 byte[] data = mJpegImageData;
1072
1073 if (mCropValue == null) {
1074 // First handle the no crop case -- just return the value. If the
1075 // caller specifies a "save uri" then write the data to its
1076 // stream. Otherwise, pass back a scaled down version of the bitmap
1077 // directly in the extras.
1078 if (mSaveUri != null) {
1079 OutputStream outputStream = null;
1080 try {
1081 outputStream = mContentResolver.openOutputStream(mSaveUri);
1082 outputStream.write(data);
1083 outputStream.close();
1084
1085 mActivity.setResultEx(Activity.RESULT_OK);
1086 mActivity.finish();
1087 } catch (IOException ex) {
1088 // ignore exception
1089 } finally {
1090 Util.closeSilently(outputStream);
1091 }
1092 } else {
1093 int orientation = Exif.getOrientation(data);
1094 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
1095 bitmap = Util.rotate(bitmap, orientation);
1096 mActivity.setResultEx(Activity.RESULT_OK,
1097 new Intent("inline-data").putExtra("data", bitmap));
1098 mActivity.finish();
1099 }
1100 } else {
1101 // Save the image to a temp file and invoke the cropper
1102 Uri tempUri = null;
1103 FileOutputStream tempStream = null;
1104 try {
1105 File path = mActivity.getFileStreamPath(sTempCropFilename);
1106 path.delete();
1107 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1108 tempStream.write(data);
1109 tempStream.close();
1110 tempUri = Uri.fromFile(path);
1111 } catch (FileNotFoundException ex) {
1112 mActivity.setResultEx(Activity.RESULT_CANCELED);
1113 mActivity.finish();
1114 return;
1115 } catch (IOException ex) {
1116 mActivity.setResultEx(Activity.RESULT_CANCELED);
1117 mActivity.finish();
1118 return;
1119 } finally {
1120 Util.closeSilently(tempStream);
1121 }
1122
1123 Bundle newExtras = new Bundle();
1124 if (mCropValue.equals("circle")) {
1125 newExtras.putString("circleCrop", "true");
1126 }
1127 if (mSaveUri != null) {
1128 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1129 } else {
1130 newExtras.putBoolean(CropExtras.KEY_RETURN_DATA, true);
1131 }
1132 if (mActivity.isSecureCamera()) {
1133 newExtras.putBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, true);
1134 }
1135
1136 Intent cropIntent = new Intent(FilterShowActivity.CROP_ACTION);
1137
1138 cropIntent.setData(tempUri);
1139 cropIntent.putExtras(newExtras);
1140
1141 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1142 }
1143 }
1144
Michael Kolb8872c232013-01-29 10:33:22 -08001145 @Override
1146 public void onShutterButtonFocus(boolean pressed) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001147 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001148 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1149 || (mCameraState == PREVIEW_STOPPED)) return;
1150
1151 // Do not do focus if there is not enough storage.
1152 if (pressed && !canTakePicture()) return;
1153
1154 if (pressed) {
1155 if (mSceneMode == Util.SCENE_MODE_HDR) {
1156 mActivity.hideSwitcher();
1157 mActivity.setSwipingEnabled(false);
1158 }
1159 mFocusManager.onShutterDown();
1160 } else {
Doris Liuda50e052013-02-07 14:36:32 -08001161 // for countdown mode, we need to postpone the shutter release
1162 // i.e. lock the focus during countdown.
Michael Kolbd6954f32013-03-08 20:43:01 -08001163 if (!mUI.isCountingDown()) {
Doris Liuda50e052013-02-07 14:36:32 -08001164 mFocusManager.onShutterUp();
1165 }
Michael Kolb8872c232013-01-29 10:33:22 -08001166 }
1167 }
1168
1169 @Override
1170 public void onShutterButtonClick() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001171 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001172 || (mCameraState == SWITCHING_CAMERA)
1173 || (mCameraState == PREVIEW_STOPPED)) return;
1174
1175 // Do not take the picture if there is not enough storage.
1176 if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
1177 Log.i(TAG, "Not enough space or storage not ready. remaining="
1178 + mActivity.getStorageSpace());
1179 return;
1180 }
1181 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1182
1183 // If the user wants to do a snapshot while the previous one is still
1184 // in progress, remember the fact and do it after we finish the previous
1185 // one and re-start the preview. Snapshot in progress also includes the
1186 // state that autofocus is focusing and a picture will be taken when
1187 // focus callback arrives.
1188 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1189 && !mIsImageCaptureIntent) {
1190 mSnapshotOnIdle = true;
1191 return;
1192 }
1193
1194 String timer = mPreferences.getString(
1195 CameraSettings.KEY_TIMER,
1196 mActivity.getString(R.string.pref_camera_timer_default));
1197 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1198 mActivity.getString(R.string.pref_camera_timer_sound_default))
1199 .equals(mActivity.getString(R.string.setting_on_value));
1200
1201 int seconds = Integer.parseInt(timer);
1202 // When shutter button is pressed, check whether the previous countdown is
1203 // finished. If not, cancel the previous countdown and start a new one.
Michael Kolbd6954f32013-03-08 20:43:01 -08001204 if (mUI.isCountingDown()) {
1205 mUI.cancelCountDown();
1206 }
1207 if (seconds > 0) {
1208 mUI.startCountDown(seconds, playSound);
Michael Kolb8872c232013-01-29 10:33:22 -08001209 } else {
1210 mSnapshotOnIdle = false;
1211 mFocusManager.doSnap();
1212 }
1213 }
1214
1215 @Override
1216 public void installIntentFilter() {
1217 }
1218
1219 @Override
1220 public boolean updateStorageHintOnResume() {
1221 return mFirstTimeInitialized;
1222 }
1223
1224 @Override
1225 public void updateCameraAppView() {
1226 }
1227
1228 @Override
1229 public void onResumeBeforeSuper() {
1230 mPaused = false;
1231 }
1232
1233 @Override
1234 public void onResumeAfterSuper() {
1235 if (mOpenCameraFail || mCameraDisabled) return;
1236
1237 mJpegPictureCallbackTime = 0;
1238 mZoomValue = 0;
1239
1240 // Start the preview if it is not started.
1241 if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
1242 resetExposureCompensation();
1243 mCameraStartUpThread = new CameraStartUpThread();
1244 mCameraStartUpThread.start();
1245 }
1246
1247 // If first time initialization is not finished, put it in the
1248 // message queue.
1249 if (!mFirstTimeInitialized) {
1250 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1251 } else {
1252 initializeSecondTime();
1253 }
1254 keepScreenOnAwhile();
1255
1256 // Dismiss open menu if exists.
1257 PopupManager.getInstance(mActivity).notifyShowPopup(null);
1258 }
1259
1260 void waitCameraStartUpThread() {
1261 try {
1262 if (mCameraStartUpThread != null) {
1263 mCameraStartUpThread.cancel();
1264 mCameraStartUpThread.join();
1265 mCameraStartUpThread = null;
1266 setCameraState(IDLE);
1267 }
1268 } catch (InterruptedException e) {
1269 // ignore
1270 }
1271 }
1272
1273 @Override
1274 public void onPauseBeforeSuper() {
1275 mPaused = true;
1276 }
1277
1278 @Override
1279 public void onPauseAfterSuper() {
1280 // Wait the camera start up thread to finish.
1281 waitCameraStartUpThread();
1282
1283 // When camera is started from secure lock screen for the first time
1284 // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1285 // To reduce the latency, keep the camera for a short time so it does
1286 // not need to be opened again.
1287 if (mCameraDevice != null && mActivity.isSecureCamera()
1288 && ActivityBase.isFirstStartAfterScreenOn()) {
1289 ActivityBase.resetFirstStartAfterScreenOn();
1290 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1291 }
1292 // Reset the focus first. Camera CTS does not guarantee that
1293 // cancelAutoFocus is allowed after preview stops.
1294 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1295 mCameraDevice.cancelAutoFocus();
1296 }
1297 stopPreview();
Doris Liub8cf2502013-02-22 14:31:17 -08001298 // Release surface texture.
1299 ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture();
Michael Kolb8872c232013-01-29 10:33:22 -08001300
Angus Kongce5480e2013-01-29 17:43:48 -08001301 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001302
1303 if (mLocationManager != null) mLocationManager.recordLocation(false);
1304
1305 // If we are in an image capture intent and has taken
1306 // a picture, we just clear it in onPause.
1307 mJpegImageData = null;
1308
1309 // Remove the messages in the event queue.
1310 mHandler.removeMessages(SETUP_PREVIEW);
1311 mHandler.removeMessages(FIRST_TIME_INIT);
1312 mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
1313 mHandler.removeMessages(SWITCH_CAMERA);
1314 mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
1315 mHandler.removeMessages(CAMERA_OPEN_DONE);
1316 mHandler.removeMessages(START_PREVIEW_DONE);
1317 mHandler.removeMessages(OPEN_CAMERA_FAIL);
1318 mHandler.removeMessages(CAMERA_DISABLED);
1319
Michael Kolbd6954f32013-03-08 20:43:01 -08001320 closeCamera();
1321
1322 resetScreenOn();
1323 mUI.onPause();
1324
Michael Kolb8872c232013-01-29 10:33:22 -08001325 mPendingSwitchCameraId = -1;
1326 if (mFocusManager != null) mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001327 MediaSaveService s = mActivity.getMediaSaveService();
1328 if (s != null) {
1329 s.setListener(null);
1330 }
Michael Kolb8872c232013-01-29 10:33:22 -08001331 }
1332
Michael Kolb8872c232013-01-29 10:33:22 -08001333 /**
1334 * The focus manager is the first UI related element to get initialized,
1335 * and it requires the RenderOverlay, so initialize it here
1336 */
1337 private void initializeFocusManager() {
1338 // Create FocusManager object. startPreview needs it.
Michael Kolb8872c232013-01-29 10:33:22 -08001339 // if mFocusManager not null, reuse it
1340 // otherwise create a new instance
1341 if (mFocusManager != null) {
1342 mFocusManager.removeMessages();
1343 } else {
1344 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1345 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
1346 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1347 R.array.pref_camera_focusmode_default_array);
1348 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
1349 mInitialParams, this, mirror,
Michael Kolbd6954f32013-03-08 20:43:01 -08001350 mActivity.getMainLooper(), mUI);
Michael Kolb8872c232013-01-29 10:33:22 -08001351 }
1352 }
1353
Michael Kolb8872c232013-01-29 10:33:22 -08001354 @Override
1355 public void onConfigurationChanged(Configuration newConfig) {
1356 Log.v(TAG, "onConfigurationChanged");
1357 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001358 }
1359
1360 @Override
1361 public void onActivityResult(
1362 int requestCode, int resultCode, Intent data) {
1363 switch (requestCode) {
1364 case REQUEST_CROP: {
1365 Intent intent = new Intent();
1366 if (data != null) {
1367 Bundle extras = data.getExtras();
1368 if (extras != null) {
1369 intent.putExtras(extras);
1370 }
1371 }
1372 mActivity.setResultEx(resultCode, intent);
1373 mActivity.finish();
1374
1375 File path = mActivity.getFileStreamPath(sTempCropFilename);
1376 path.delete();
1377
1378 break;
1379 }
1380 }
1381 }
1382
1383 private boolean canTakePicture() {
1384 return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
1385 }
1386
1387 @Override
1388 public void autoFocus() {
1389 mFocusStartTime = System.currentTimeMillis();
1390 mCameraDevice.autoFocus(mAutoFocusCallback);
1391 setCameraState(FOCUSING);
1392 }
1393
1394 @Override
1395 public void cancelAutoFocus() {
1396 mCameraDevice.cancelAutoFocus();
1397 setCameraState(IDLE);
1398 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1399 }
1400
1401 // Preview area is touched. Handle touch focus.
1402 @Override
1403 public void onSingleTapUp(View view, int x, int y) {
1404 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1405 || mCameraState == SNAPSHOT_IN_PROGRESS
1406 || mCameraState == SWITCHING_CAMERA
1407 || mCameraState == PREVIEW_STOPPED) {
1408 return;
1409 }
1410
1411 // Do not trigger touch focus if popup window is opened.
Michael Kolbd6954f32013-03-08 20:43:01 -08001412 if (mUI.removeTopLevelPopup()) return;
Michael Kolb8872c232013-01-29 10:33:22 -08001413
1414 // Check if metering area or focus area is supported.
1415 if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1416 mFocusManager.onSingleTapUp(x, y);
1417 }
1418
1419 @Override
1420 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001421 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001422 }
1423
1424 @Override
1425 public boolean onKeyDown(int keyCode, KeyEvent event) {
1426 switch (keyCode) {
1427 case KeyEvent.KEYCODE_VOLUME_UP:
1428 case KeyEvent.KEYCODE_VOLUME_DOWN:
1429 case KeyEvent.KEYCODE_FOCUS:
1430 if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
1431 if (event.getRepeatCount() == 0) {
1432 onShutterButtonFocus(true);
1433 }
1434 return true;
1435 }
1436 return false;
1437 case KeyEvent.KEYCODE_CAMERA:
1438 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1439 onShutterButtonClick();
1440 }
1441 return true;
1442 case KeyEvent.KEYCODE_DPAD_CENTER:
1443 // If we get a dpad center event without any focused view, move
1444 // the focus to the shutter button and press it.
1445 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1446 // Start auto-focus immediately to reduce shutter lag. After
1447 // the shutter button gets the focus, onShutterButtonFocus()
1448 // will be called again but it is fine.
Michael Kolbd6954f32013-03-08 20:43:01 -08001449 if (mUI.removeTopLevelPopup()) return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001450 onShutterButtonFocus(true);
Michael Kolbd6954f32013-03-08 20:43:01 -08001451 mUI.pressShutterButton();
Michael Kolb8872c232013-01-29 10:33:22 -08001452 }
1453 return true;
1454 }
1455 return false;
1456 }
1457
1458 @Override
1459 public boolean onKeyUp(int keyCode, KeyEvent event) {
1460 switch (keyCode) {
1461 case KeyEvent.KEYCODE_VOLUME_UP:
1462 case KeyEvent.KEYCODE_VOLUME_DOWN:
1463 if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
1464 onShutterButtonClick();
1465 return true;
1466 }
1467 return false;
1468 case KeyEvent.KEYCODE_FOCUS:
1469 if (mFirstTimeInitialized) {
1470 onShutterButtonFocus(false);
1471 }
1472 return true;
1473 }
1474 return false;
1475 }
1476
1477 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1478 private void closeCamera() {
1479 if (mCameraDevice != null) {
1480 mCameraDevice.setZoomChangeListener(null);
1481 if(ApiHelper.HAS_FACE_DETECTION) {
1482 mCameraDevice.setFaceDetectionListener(null);
1483 }
1484 mCameraDevice.setErrorCallback(null);
1485 CameraHolder.instance().release();
1486 mFaceDetectionStarted = false;
1487 mCameraDevice = null;
1488 setCameraState(PREVIEW_STOPPED);
1489 mFocusManager.onCameraReleased();
1490 }
1491 }
1492
1493 private void setDisplayOrientation() {
1494 mDisplayRotation = Util.getDisplayRotation(mActivity);
1495 mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
1496 mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -08001497 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001498 if (mFocusManager != null) {
1499 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1500 }
1501 // GLRoot also uses the DisplayRotation, and needs to be told to layout to update
1502 mActivity.getGLRoot().requestLayoutContentPane();
1503 }
1504
1505 // Only called by UI thread.
1506 private void setupPreview() {
1507 mFocusManager.resetTouchFocus();
1508 startPreview();
1509 setCameraState(IDLE);
1510 startFaceDetection();
1511 }
1512
1513 // This can be called by UI Thread or CameraStartUpThread. So this should
1514 // not modify the views.
1515 private void startPreview() {
1516 mCameraDevice.setErrorCallback(mErrorCallback);
1517
1518 // ICS camera frameworks has a bug. Face detection state is not cleared
1519 // after taking a picture. Stop the preview to work around it. The bug
1520 // was fixed in JB.
1521 if (mCameraState != PREVIEW_STOPPED) stopPreview();
1522
1523 setDisplayOrientation();
1524
1525 if (!mSnapshotOnIdle) {
1526 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1527 // resume it because it may have been paused by autoFocus call.
1528 if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
1529 mCameraDevice.cancelAutoFocus();
1530 }
1531 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1532 }
1533 setCameraParameters(UPDATE_PARAM_ALL);
1534
1535 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1536 CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail;
Michael Kolbd6954f32013-03-08 20:43:01 -08001537 if (mUI.getSurfaceTexture() == null) {
Michael Kolb8872c232013-01-29 10:33:22 -08001538 Size size = mParameters.getPreviewSize();
1539 if (mCameraDisplayOrientation % 180 == 0) {
1540 screenNail.setSize(size.width, size.height);
1541 } else {
1542 screenNail.setSize(size.height, size.width);
1543 }
1544 screenNail.enableAspectRatioClamping();
1545 mActivity.notifyScreenNailChanged();
1546 screenNail.acquireSurfaceTexture();
1547 CameraStartUpThread t = mCameraStartUpThread;
1548 if (t != null && t.isCanceled()) {
1549 return; // Exiting, so no need to get the surface texture.
1550 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001551 mUI.setSurfaceTexture(screenNail.getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001552 }
1553 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
Michael Kolbd6954f32013-03-08 20:43:01 -08001554 Object st = mUI.getSurfaceTexture();
1555 if (st != null) {
1556 mCameraDevice.setPreviewTextureAsync((SurfaceTexture) st);
Michael Kolb8872c232013-01-29 10:33:22 -08001557 }
1558 } else {
1559 mCameraDevice.setDisplayOrientation(mDisplayOrientation);
Michael Kolbd6954f32013-03-08 20:43:01 -08001560 mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
Michael Kolb8872c232013-01-29 10:33:22 -08001561 }
1562
1563 Log.v(TAG, "startPreview");
1564 mCameraDevice.startPreviewAsync();
1565
1566 mFocusManager.onPreviewStarted();
1567
1568 if (mSnapshotOnIdle) {
1569 mHandler.post(mDoSnapRunnable);
1570 }
1571 }
1572
Michael Kolbd6954f32013-03-08 20:43:01 -08001573 @Override
1574 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001575 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1576 Log.v(TAG, "stopPreview");
1577 mCameraDevice.stopPreview();
1578 mFaceDetectionStarted = false;
1579 }
1580 setCameraState(PREVIEW_STOPPED);
1581 if (mFocusManager != null) mFocusManager.onPreviewStopped();
1582 }
1583
1584 @SuppressWarnings("deprecation")
1585 private void updateCameraParametersInitialize() {
1586 // Reset preview frame rate to the maximum because it may be lowered by
1587 // video camera application.
1588 List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates();
1589 if (frameRates != null) {
1590 Integer max = Collections.max(frameRates);
1591 mParameters.setPreviewFrameRate(max);
1592 }
1593
1594 mParameters.set(Util.RECORDING_HINT, Util.FALSE);
1595
1596 // Disable video stabilization. Convenience methods not available in API
1597 // level <= 14
1598 String vstabSupported = mParameters.get("video-stabilization-supported");
1599 if ("true".equals(vstabSupported)) {
1600 mParameters.set("video-stabilization", "false");
1601 }
1602 }
1603
1604 private void updateCameraParametersZoom() {
1605 // Set zoom.
1606 if (mParameters.isZoomSupported()) {
1607 mParameters.setZoom(mZoomValue);
1608 }
1609 }
1610
1611 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1612 private void setAutoExposureLockIfSupported() {
1613 if (mAeLockSupported) {
1614 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1615 }
1616 }
1617
1618 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1619 private void setAutoWhiteBalanceLockIfSupported() {
1620 if (mAwbLockSupported) {
1621 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1622 }
1623 }
1624
1625 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1626 private void setFocusAreasIfSupported() {
1627 if (mFocusAreaSupported) {
1628 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1629 }
1630 }
1631
1632 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1633 private void setMeteringAreasIfSupported() {
1634 if (mMeteringAreaSupported) {
1635 // Use the same area for focus and metering.
1636 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1637 }
1638 }
1639
1640 private void updateCameraParametersPreference() {
1641 setAutoExposureLockIfSupported();
1642 setAutoWhiteBalanceLockIfSupported();
1643 setFocusAreasIfSupported();
1644 setMeteringAreasIfSupported();
1645
1646 // Set picture size.
1647 String pictureSize = mPreferences.getString(
1648 CameraSettings.KEY_PICTURE_SIZE, null);
1649 if (pictureSize == null) {
1650 CameraSettings.initialCameraPictureSize(mActivity, mParameters);
1651 } else {
1652 List<Size> supported = mParameters.getSupportedPictureSizes();
1653 CameraSettings.setCameraPictureSize(
1654 pictureSize, supported, mParameters);
1655 }
1656 Size size = mParameters.getPictureSize();
1657
1658 // Set a preview size that is closest to the viewfinder height and has
1659 // the right aspect ratio.
1660 List<Size> sizes = mParameters.getSupportedPreviewSizes();
1661 Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes,
1662 (double) size.width / size.height);
1663 Size original = mParameters.getPreviewSize();
1664 if (!original.equals(optimalSize)) {
1665 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
1666
1667 // Zoom related settings will be changed for different preview
1668 // sizes, so set and read the parameters to get latest values
1669 mCameraDevice.setParameters(mParameters);
1670 mParameters = mCameraDevice.getParameters();
1671 }
1672 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1673
1674 // Since changing scene mode may change supported values, set scene mode
1675 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1676 // separate preference.
1677 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
1678 mActivity.getString(R.string.pref_camera_hdr_default));
1679 if (mActivity.getString(R.string.setting_on_value).equals(hdr)) {
1680 mSceneMode = Util.SCENE_MODE_HDR;
1681 } else {
1682 mSceneMode = mPreferences.getString(
1683 CameraSettings.KEY_SCENE_MODE,
1684 mActivity.getString(R.string.pref_camera_scenemode_default));
1685 }
1686 if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
1687 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1688 mParameters.setSceneMode(mSceneMode);
1689
1690 // Setting scene mode will change the settings of flash mode,
1691 // white balance, and focus mode. Here we read back the
1692 // parameters, so we can know those settings.
1693 mCameraDevice.setParameters(mParameters);
1694 mParameters = mCameraDevice.getParameters();
1695 }
1696 } else {
1697 mSceneMode = mParameters.getSceneMode();
1698 if (mSceneMode == null) {
1699 mSceneMode = Parameters.SCENE_MODE_AUTO;
1700 }
1701 }
1702
1703 // Set JPEG quality.
1704 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1705 CameraProfile.QUALITY_HIGH);
1706 mParameters.setJpegQuality(jpegQuality);
1707
1708 // For the following settings, we need to check if the settings are
1709 // still supported by latest driver, if not, ignore the settings.
1710
1711 // Set exposure compensation
1712 int value = CameraSettings.readExposure(mPreferences);
1713 int max = mParameters.getMaxExposureCompensation();
1714 int min = mParameters.getMinExposureCompensation();
1715 if (value >= min && value <= max) {
1716 mParameters.setExposureCompensation(value);
1717 } else {
1718 Log.w(TAG, "invalid exposure range: " + value);
1719 }
1720
1721 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1722 // Set flash mode.
1723 String flashMode = mPreferences.getString(
1724 CameraSettings.KEY_FLASH_MODE,
1725 mActivity.getString(R.string.pref_camera_flashmode_default));
1726 List<String> supportedFlash = mParameters.getSupportedFlashModes();
1727 if (Util.isSupported(flashMode, supportedFlash)) {
1728 mParameters.setFlashMode(flashMode);
1729 } else {
1730 flashMode = mParameters.getFlashMode();
1731 if (flashMode == null) {
1732 flashMode = mActivity.getString(
1733 R.string.pref_camera_flashmode_no_flash);
1734 }
1735 }
1736
1737 // Set white balance parameter.
1738 String whiteBalance = mPreferences.getString(
1739 CameraSettings.KEY_WHITE_BALANCE,
1740 mActivity.getString(R.string.pref_camera_whitebalance_default));
1741 if (Util.isSupported(whiteBalance,
1742 mParameters.getSupportedWhiteBalance())) {
1743 mParameters.setWhiteBalance(whiteBalance);
1744 } else {
1745 whiteBalance = mParameters.getWhiteBalance();
1746 if (whiteBalance == null) {
1747 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1748 }
1749 }
1750
1751 // Set focus mode.
1752 mFocusManager.overrideFocusMode(null);
1753 mParameters.setFocusMode(mFocusManager.getFocusMode());
1754 } else {
1755 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1756 }
1757
1758 if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1759 updateAutoFocusMoveCallback();
1760 }
1761 }
1762
1763 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1764 private void updateAutoFocusMoveCallback() {
1765 if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
1766 mCameraDevice.setAutoFocusMoveCallback(
1767 (AutoFocusMoveCallback) mAutoFocusMoveCallback);
1768 } else {
1769 mCameraDevice.setAutoFocusMoveCallback(null);
1770 }
1771 }
1772
1773 // We separate the parameters into several subsets, so we can update only
1774 // the subsets actually need updating. The PREFERENCE set needs extra
1775 // locking because the preference can be changed from GLThread as well.
1776 private void setCameraParameters(int updateSet) {
1777 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1778 updateCameraParametersInitialize();
1779 }
1780
1781 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1782 updateCameraParametersZoom();
1783 }
1784
1785 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
1786 updateCameraParametersPreference();
1787 }
1788
1789 mCameraDevice.setParameters(mParameters);
1790 }
1791
1792 // If the Camera is idle, update the parameters immediately, otherwise
1793 // accumulate them in mUpdateSet and update later.
1794 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1795 mUpdateSet |= additionalUpdateSet;
1796 if (mCameraDevice == null) {
1797 // We will update all the parameters when we open the device, so
1798 // we don't need to do anything now.
1799 mUpdateSet = 0;
1800 return;
1801 } else if (isCameraIdle()) {
1802 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001803 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001804 mUpdateSet = 0;
1805 } else {
1806 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1807 mHandler.sendEmptyMessageDelayed(
1808 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1809 }
1810 }
1811 }
1812
Michael Kolbd6954f32013-03-08 20:43:01 -08001813 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001814 return (mCameraState == IDLE) ||
1815 (mCameraState == PREVIEW_STOPPED) ||
1816 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1817 && (mCameraState != SWITCHING_CAMERA));
1818 }
1819
Michael Kolbd6954f32013-03-08 20:43:01 -08001820 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001821 String action = mActivity.getIntent().getAction();
1822 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
1823 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
1824 }
1825
1826 private void setupCaptureParams() {
1827 Bundle myExtras = mActivity.getIntent().getExtras();
1828 if (myExtras != null) {
1829 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1830 mCropValue = myExtras.getString("crop");
1831 }
1832 }
1833
Michael Kolb8872c232013-01-29 10:33:22 -08001834 @Override
1835 public void onSharedPreferenceChanged() {
1836 // ignore the events after "onPause()"
1837 if (mPaused) return;
1838
1839 boolean recordLocation = RecordLocationPreference.get(
1840 mPreferences, mContentResolver);
1841 mLocationManager.recordLocation(recordLocation);
1842
1843 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
Michael Kolbd6954f32013-03-08 20:43:01 -08001844 mUI.updateOnScreenIndicators(mParameters, mPreferences);
Michael Kolb8872c232013-01-29 10:33:22 -08001845 }
1846
1847 @Override
1848 public void onCameraPickerClicked(int cameraId) {
1849 if (mPaused || mPendingSwitchCameraId != -1) return;
1850
1851 mPendingSwitchCameraId = cameraId;
1852 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1853 Log.v(TAG, "Start to copy texture. cameraId=" + cameraId);
1854 // We need to keep a preview frame for the animation before
1855 // releasing the camera. This will trigger onPreviewTextureCopied.
1856 ((CameraScreenNail) mActivity.mCameraScreenNail).copyTexture();
1857 // Disable all camera controls.
1858 setCameraState(SWITCHING_CAMERA);
1859 } else {
1860 switchCamera();
1861 }
1862 }
1863
Michael Kolb8872c232013-01-29 10:33:22 -08001864 // Preview texture has been copied. Now camera can be released and the
1865 // animation can be started.
1866 @Override
1867 public void onPreviewTextureCopied() {
1868 mHandler.sendEmptyMessage(SWITCH_CAMERA);
1869 }
1870
1871 @Override
1872 public void onCaptureTextureCopied() {
1873 }
1874
1875 @Override
1876 public void onUserInteraction() {
1877 if (!mActivity.isFinishing()) keepScreenOnAwhile();
1878 }
1879
1880 private void resetScreenOn() {
1881 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1882 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1883 }
1884
1885 private void keepScreenOnAwhile() {
1886 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1887 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1888 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1889 }
1890
1891 // TODO: Delete this function after old camera code is removed
1892 @Override
1893 public void onRestorePreferencesClicked() {
1894 }
1895
1896 @Override
1897 public void onOverriddenPreferencesClicked() {
1898 if (mPaused) return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001899 mUI.showPreferencesToast();
Michael Kolb8872c232013-01-29 10:33:22 -08001900 }
1901
1902 private void showTapToFocusToast() {
1903 // TODO: Use a toast?
1904 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1905 // Clear the preference.
1906 Editor editor = mPreferences.edit();
1907 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
1908 editor.apply();
1909 }
1910
1911 private void initializeCapabilities() {
1912 mInitialParams = mCameraDevice.getParameters();
1913 mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
1914 mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
1915 mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
1916 mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams);
1917 mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
1918 Util.FOCUS_MODE_CONTINUOUS_PICTURE);
1919 }
1920
Michael Kolb8872c232013-01-29 10:33:22 -08001921 @Override
1922 public void onCountDownFinished() {
1923 mSnapshotOnIdle = false;
1924 mFocusManager.doSnap();
Doris Liuda50e052013-02-07 14:36:32 -08001925 mFocusManager.onShutterUp();
Michael Kolb8872c232013-01-29 10:33:22 -08001926 }
1927
Michael Kolb8872c232013-01-29 10:33:22 -08001928 @Override
1929 public boolean needsSwitcher() {
1930 return !mIsImageCaptureIntent;
1931 }
1932
Doris Liu6a0de792013-02-26 10:54:25 -08001933 @Override
1934 public boolean needsPieMenu() {
1935 return true;
1936 }
1937
Michael Kolb8872c232013-01-29 10:33:22 -08001938 @Override
1939 public void onShowSwitcherPopup() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001940 mUI.onShowSwitcherPopup();
Michael Kolb8872c232013-01-29 10:33:22 -08001941 }
1942
Angus Kongce5480e2013-01-29 17:43:48 -08001943 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001944 public int onZoomChanged(int index) {
1945 // Not useful to change zoom value when the activity is paused.
1946 if (mPaused) return index;
1947 mZoomValue = index;
1948 if (mParameters == null || mCameraDevice == null) return index;
1949 // Set zoom parameters asynchronously
1950 mParameters.setZoom(mZoomValue);
1951 mCameraDevice.setParametersAsync(mParameters);
1952 Parameters p = mCameraDevice.getParameters();
1953 if (p != null) return p.getZoom();
1954 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08001955 }
1956
1957 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001958 public int getCameraState() {
1959 return mCameraState;
1960 }
1961
1962 @Override
1963 public void onQueueStatus(boolean full) {
1964 mUI.enableShutter(!full);
Angus Kongce5480e2013-01-29 17:43:48 -08001965 }
Angus Kong86d36312013-01-31 18:22:44 -08001966
1967 @Override
1968 public void onMediaSaveServiceConnected(MediaSaveService s) {
1969 // We set the listener only when both service and shutterbutton
1970 // are initialized.
Michael Kolbd6954f32013-03-08 20:43:01 -08001971 if (mFirstTimeInitialized) {
1972 s.setListener(this);
1973 }
Angus Kong86d36312013-01-31 18:22:44 -08001974 }
Michael Kolb8872c232013-01-29 10:33:22 -08001975}