blob: 5b352719fdb7cef7e71b0335fcc6cea39c56a168 [file] [log] [blame]
Michael Kolb25668cd2013-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 Kongdd2e5112013-03-26 11:40:40 -070024import android.content.Context;
Michael Kolb25668cd2013-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 Kolb25668cd2013-01-29 10:33:22 -080032import android.hardware.Camera.Parameters;
33import android.hardware.Camera.PictureCallback;
34import android.hardware.Camera.Size;
Angus Kongdd2e5112013-03-26 11:40:40 -070035import android.hardware.Sensor;
36import android.hardware.SensorEvent;
37import android.hardware.SensorEventListener;
38import android.hardware.SensorManager;
Michael Kolb25668cd2013-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 Kolb25668cd2013-01-29 10:33:22 -080051import android.view.KeyEvent;
Michael Kolb25668cd2013-01-29 10:33:22 -080052import android.view.MotionEvent;
53import android.view.OrientationEventListener;
54import android.view.SurfaceHolder;
55import android.view.View;
Michael Kolb25668cd2013-01-29 10:33:22 -080056import android.view.WindowManager;
Michael Kolb25668cd2013-01-29 10:33:22 -080057
58import com.android.camera.CameraManager.CameraProxy;
Michael Kolbb28942d2013-03-08 20:43:01 -080059import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
Michael Kolb25668cd2013-01-29 10:33:22 -080060import com.android.camera.ui.PopupManager;
Michael Kolb25668cd2013-01-29 10:33:22 -080061import com.android.camera.ui.RotateTextToast;
John Recka3dc2c02013-02-15 15:51:30 -080062import com.android.gallery3d.R;
Michael Kolb25668cd2013-01-29 10:33:22 -080063import com.android.gallery3d.common.ApiHelper;
Angus Kongdd2e5112013-03-26 11:40:40 -070064import com.android.gallery3d.exif.ExifInterface;
65import com.android.gallery3d.exif.ExifTag;
66import com.android.gallery3d.exif.Rational;
Michael Kolb25668cd2013-01-29 10:33:22 -080067import com.android.gallery3d.filtershow.FilterShowActivity;
Ruben Brunk6fe165b2013-04-04 17:20:42 -070068import com.android.gallery3d.filtershow.crop.CropExtras;
Bobby Georgescuf37648b2013-03-12 22:45:17 -070069import com.android.gallery3d.util.UsageStatistics;
Michael Kolb25668cd2013-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 Kolbb28942d2013-03-08 20:43:01 -080083 PhotoController,
Michael Kolb25668cd2013-01-29 10:33:22 -080084 FocusOverlayManager.Listener,
85 CameraPreference.OnPreferenceChangedListener,
Michael Kolb25668cd2013-01-29 10:33:22 -080086 ShutterButton.OnShutterButtonListener,
Michael Kolbb28942d2013-03-08 20:43:01 -080087 MediaSaveService.Listener,
Angus Kongdd2e5112013-03-26 11:40:40 -070088 OnCountDownFinishedListener,
89 SensorEventListener {
Michael Kolb25668cd2013-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 Kolbc43e5b62013-04-30 15:34:19 -0700108 private static final int CAPTURE_ANIMATION_DONE = 13;
Michael Kolb25668cd2013-01-29 10:33:22 -0800109
110 // The subset of parameters we need to update in setCameraParameters().
111 private static final int UPDATE_PARAM_INITIALIZE = 1;
112 private static final int UPDATE_PARAM_ZOOM = 2;
113 private static final int UPDATE_PARAM_PREFERENCE = 4;
114 private static final int UPDATE_PARAM_ALL = -1;
115
116 // This is the timeout to keep the camera in onPause for the first time
117 // after screen on if the activity is started from secure lock screen.
118 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
119
120 // copied from Camera hierarchy
121 private CameraActivity mActivity;
Michael Kolb25668cd2013-01-29 10:33:22 -0800122 private CameraProxy mCameraDevice;
123 private int mCameraId;
124 private Parameters mParameters;
125 private boolean mPaused;
Michael Kolbb28942d2013-03-08 20:43:01 -0800126
127 private PhotoUI mUI;
Michael Kolb25668cd2013-01-29 10:33:22 -0800128
129 // these are only used by Camera
130
131 // The activity is going to switch to the specified camera id. This is
132 // needed because texture copy is done in GL thread. -1 means camera is not
133 // switching.
134 protected int mPendingSwitchCameraId = -1;
135 private boolean mOpenCameraFail;
136 private boolean mCameraDisabled;
137
138 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
139 // needed to be updated in mUpdateSet.
140 private int mUpdateSet;
141
142 private static final int SCREEN_DELAY = 2 * 60 * 1000;
143
144 private int mZoomValue; // The current zoom value.
Michael Kolb25668cd2013-01-29 10:33:22 -0800145
146 private Parameters mInitialParams;
147 private boolean mFocusAreaSupported;
148 private boolean mMeteringAreaSupported;
149 private boolean mAeLockSupported;
150 private boolean mAwbLockSupported;
151 private boolean mContinousFocusSupported;
152
153 // The degrees of the device rotated clockwise from its natural orientation.
154 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
155 private ComboPreferences mPreferences;
156
157 private static final String sTempCropFilename = "crop-temp";
158
159 private ContentProviderClient mMediaProviderClient;
Michael Kolb25668cd2013-01-29 10:33:22 -0800160 private boolean mFaceDetectionStarted = false;
161
Michael Kolb25668cd2013-01-29 10:33:22 -0800162 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
163 private String mCropValue;
164 private Uri mSaveUri;
165
Angus Kongc0993982013-01-29 17:43:48 -0800166 // We use a queue to generated names of the images to be used later
167 // when the image is ready to be saved.
Michael Kolb25668cd2013-01-29 10:33:22 -0800168 private NamedImages mNamedImages;
169
170 private Runnable mDoSnapRunnable = new Runnable() {
171 @Override
172 public void run() {
173 onShutterButtonClick();
174 }
175 };
176
Angus Kong1d752532013-03-25 23:11:43 -0700177 private Runnable mFlashRunnable = new Runnable() {
178 @Override
179 public void run() {
180 animateFlash();
181 }
182 };
183
Michael Kolb25668cd2013-01-29 10:33:22 -0800184 private final StringBuilder mBuilder = new StringBuilder();
185 private final Formatter mFormatter = new Formatter(mBuilder);
186 private final Object[] mFormatterArgs = new Object[1];
187
188 /**
189 * An unpublished intent flag requesting to return as soon as capturing
190 * is completed.
191 *
192 * TODO: consider publishing by moving into MediaStore.
193 */
194 private static final String EXTRA_QUICK_CAPTURE =
195 "android.intent.extra.quickCapture";
196
197 // The display rotation in degrees. This is only valid when mCameraState is
198 // not PREVIEW_STOPPED.
199 private int mDisplayRotation;
200 // The value for android.hardware.Camera.setDisplayOrientation.
201 private int mCameraDisplayOrientation;
202 // The value for UI components like indicators.
203 private int mDisplayOrientation;
204 // The value for android.hardware.Camera.Parameters.setRotation.
205 private int mJpegRotation;
206 private boolean mFirstTimeInitialized;
207 private boolean mIsImageCaptureIntent;
208
Michael Kolb25668cd2013-01-29 10:33:22 -0800209 private int mCameraState = PREVIEW_STOPPED;
210 private boolean mSnapshotOnIdle = false;
211
212 private ContentResolver mContentResolver;
213
214 private LocationManager mLocationManager;
215
Michael Kolb25668cd2013-01-29 10:33:22 -0800216 private final PostViewPictureCallback mPostViewPictureCallback =
217 new PostViewPictureCallback();
218 private final RawPictureCallback mRawPictureCallback =
219 new RawPictureCallback();
220 private final AutoFocusCallback mAutoFocusCallback =
221 new AutoFocusCallback();
222 private final Object mAutoFocusMoveCallback =
223 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
224 ? new AutoFocusMoveCallback()
225 : null;
226
227 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
228
229 private long mFocusStartTime;
230 private long mShutterCallbackTime;
231 private long mPostViewPictureCallbackTime;
232 private long mRawPictureCallbackTime;
233 private long mJpegPictureCallbackTime;
234 private long mOnResumeTime;
235 private byte[] mJpegImageData;
236
237 // These latency time are for the CameraLatency test.
238 public long mAutoFocusTime;
239 public long mShutterLag;
240 public long mShutterToPictureDisplayedTime;
241 public long mPictureDisplayedToJpegCallbackTime;
242 public long mJpegCallbackFinishTime;
243 public long mCaptureStartTime;
244
245 // This handles everything about focus.
246 private FocusOverlayManager mFocusManager;
247
Michael Kolb25668cd2013-01-29 10:33:22 -0800248 private String mSceneMode;
Michael Kolb25668cd2013-01-29 10:33:22 -0800249
250 private final Handler mHandler = new MainHandler();
251 private PreferenceGroup mPreferenceGroup;
252
253 private boolean mQuickCapture;
254
255 CameraStartUpThread mCameraStartUpThread;
256 ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
257
Angus Kongdd2e5112013-03-26 11:40:40 -0700258 private SensorManager mSensorManager;
259 private float[] mGData = new float[3];
260 private float[] mMData = new float[3];
261 private float[] mR = new float[16];
262 private int mHeading = -1;
263
Angus Kongc0993982013-01-29 17:43:48 -0800264 private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
265 new MediaSaveService.OnMediaSavedListener() {
266 @Override
267 public void onMediaSaved(Uri uri) {
268 if (uri != null) {
269 mActivity.addSecureAlbumItemIfNeeded(false, uri);
270 Util.broadcastNewPicture(mActivity, uri);
271 }
272 }
273 };
Michael Kolb25668cd2013-01-29 10:33:22 -0800274
275 // The purpose is not to block the main thread in onCreate and onResume.
276 private class CameraStartUpThread extends Thread {
277 private volatile boolean mCancelled;
278
279 public void cancel() {
280 mCancelled = true;
281 interrupt();
282 }
283
284 public boolean isCanceled() {
285 return mCancelled;
286 }
287
288 @Override
289 public void run() {
290 try {
291 // We need to check whether the activity is paused before long
292 // operations to ensure that onPause() can be done ASAP.
293 if (mCancelled) return;
294 mCameraDevice = Util.openCamera(mActivity, mCameraId);
295 mParameters = mCameraDevice.getParameters();
296 // Wait until all the initialization needed by startPreview are
297 // done.
298 mStartPreviewPrerequisiteReady.block();
299
300 initializeCapabilities();
301 if (mFocusManager == null) initializeFocusManager();
302 if (mCancelled) return;
303 setCameraParameters(UPDATE_PARAM_ALL);
304 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
305 if (mCancelled) return;
306 startPreview();
307 mHandler.sendEmptyMessage(START_PREVIEW_DONE);
308 mOnResumeTime = SystemClock.uptimeMillis();
309 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
310 } catch (CameraHardwareException e) {
311 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
312 } catch (CameraDisabledException e) {
313 mHandler.sendEmptyMessage(CAMERA_DISABLED);
314 }
315 }
316 }
317
318 /**
319 * This Handler is used to post message back onto the main thread of the
320 * application
321 */
322 private class MainHandler extends Handler {
323 @Override
324 public void handleMessage(Message msg) {
325 switch (msg.what) {
326 case SETUP_PREVIEW: {
327 setupPreview();
328 break;
329 }
330
331 case CLEAR_SCREEN_DELAY: {
332 mActivity.getWindow().clearFlags(
333 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
334 break;
335 }
336
337 case FIRST_TIME_INIT: {
338 initializeFirstTime();
339 break;
340 }
341
342 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
343 setCameraParametersWhenIdle(0);
344 break;
345 }
346
347 case CHECK_DISPLAY_ROTATION: {
348 // Set the display orientation if display rotation has changed.
349 // Sometimes this happens when the device is held upside
350 // down and camera app is opened. Rotation animation will
351 // take some time and the rotation value we have got may be
352 // wrong. Framework does not have a callback for this now.
353 if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
354 setDisplayOrientation();
355 }
356 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
357 mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
358 }
359 break;
360 }
361
362 case SHOW_TAP_TO_FOCUS_TOAST: {
363 showTapToFocusToast();
364 break;
365 }
366
367 case SWITCH_CAMERA: {
368 switchCamera();
369 break;
370 }
371
372 case SWITCH_CAMERA_START_ANIMATION: {
373 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
374 break;
375 }
376
377 case CAMERA_OPEN_DONE: {
Michael Kolbb28942d2013-03-08 20:43:01 -0800378 onCameraOpened();
Michael Kolb25668cd2013-01-29 10:33:22 -0800379 break;
380 }
381
382 case START_PREVIEW_DONE: {
Michael Kolbb28942d2013-03-08 20:43:01 -0800383 onPreviewStarted();
Michael Kolb25668cd2013-01-29 10:33:22 -0800384 break;
385 }
386
387 case OPEN_CAMERA_FAIL: {
388 mCameraStartUpThread = null;
389 mOpenCameraFail = true;
390 Util.showErrorAndFinish(mActivity,
391 R.string.cannot_connect_camera);
392 break;
393 }
394
395 case CAMERA_DISABLED: {
396 mCameraStartUpThread = null;
397 mCameraDisabled = true;
398 Util.showErrorAndFinish(mActivity,
399 R.string.camera_disabled);
400 break;
401 }
Michael Kolbc43e5b62013-04-30 15:34:19 -0700402 case CAPTURE_ANIMATION_DONE: {
403 mUI.enablePreviewThumb(false);
404 break;
405 }
Michael Kolb25668cd2013-01-29 10:33:22 -0800406 }
407 }
408 }
409
410 @Override
411 public void init(CameraActivity activity, View parent, boolean reuseNail) {
412 mActivity = activity;
Michael Kolbb28942d2013-03-08 20:43:01 -0800413 mUI = new PhotoUI(activity, this, parent);
Michael Kolb25668cd2013-01-29 10:33:22 -0800414 mPreferences = new ComboPreferences(mActivity);
415 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
416 mCameraId = getPreferredCameraId(mPreferences);
417
418 mContentResolver = mActivity.getContentResolver();
419
420 // To reduce startup time, open the camera and start the preview in
421 // another thread.
422 mCameraStartUpThread = new CameraStartUpThread();
423 mCameraStartUpThread.start();
424
Michael Kolb25668cd2013-01-29 10:33:22 -0800425
426 // Surface texture is from camera screen nail and startPreview needs it.
427 // This must be done before startPreview.
428 mIsImageCaptureIntent = isImageCaptureIntent();
429 if (reuseNail) {
430 mActivity.reuseCameraScreenNail(!mIsImageCaptureIntent);
431 } else {
432 mActivity.createCameraScreenNail(!mIsImageCaptureIntent);
433 }
434
435 mPreferences.setLocalId(mActivity, mCameraId);
436 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
437 // we need to reset exposure for the preview
438 resetExposureCompensation();
439 // Starting the preview needs preferences, camera screen nail, and
440 // focus area indicator.
441 mStartPreviewPrerequisiteReady.open();
442
443 initializeControlByIntent();
444 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Michael Kolbb28942d2013-03-08 20:43:01 -0800445 mLocationManager = new LocationManager(mActivity, mUI);
Angus Kongdd2e5112013-03-26 11:40:40 -0700446 mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
447
Michael Kolbb28942d2013-03-08 20:43:01 -0800448 }
449
450 private void initializeControlByIntent() {
451 mUI.initializeControlByIntent();
452 if (mIsImageCaptureIntent) {
453 setupCaptureParams();
454 }
455 }
456
457 private void onPreviewStarted() {
458 mCameraStartUpThread = null;
459 setCameraState(IDLE);
460 if (!ApiHelper.HAS_SURFACE_TEXTURE) {
461 // This may happen if surfaceCreated has arrived.
462 mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
463 }
464 startFaceDetection();
465 locationFirstRun();
Michael Kolb25668cd2013-01-29 10:33:22 -0800466 }
467
468 // Prompt the user to pick to record location for the very first run of
469 // camera only
470 private void locationFirstRun() {
471 if (RecordLocationPreference.isSet(mPreferences)) {
472 return;
473 }
474 if (mActivity.isSecureCamera()) return;
475 // Check if the back camera exists
476 int backCameraId = CameraHolder.instance().getBackCameraId();
477 if (backCameraId == -1) {
478 // If there is no back camera, do not show the prompt.
479 return;
480 }
481
482 new AlertDialog.Builder(mActivity)
483 .setTitle(R.string.remember_location_title)
484 .setMessage(R.string.remember_location_prompt)
485 .setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() {
486 @Override
487 public void onClick(DialogInterface dialog, int arg1) {
488 setLocationPreference(RecordLocationPreference.VALUE_ON);
489 }
490 })
491 .setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() {
492 @Override
493 public void onClick(DialogInterface dialog, int arg1) {
494 dialog.cancel();
495 }
496 })
497 .setOnCancelListener(new DialogInterface.OnCancelListener() {
498 @Override
499 public void onCancel(DialogInterface dialog) {
500 setLocationPreference(RecordLocationPreference.VALUE_OFF);
501 }
502 })
503 .show();
504 }
505
506 private void setLocationPreference(String value) {
507 mPreferences.edit()
508 .putString(CameraSettings.KEY_RECORD_LOCATION, value)
509 .apply();
510 // TODO: Fix this to use the actual onSharedPreferencesChanged listener
511 // instead of invoking manually
512 onSharedPreferenceChanged();
513 }
514
Michael Kolbb28942d2013-03-08 20:43:01 -0800515 private void onCameraOpened() {
516 View root = mUI.getRootView();
Michael Kolb25668cd2013-01-29 10:33:22 -0800517 // These depend on camera parameters.
Michael Kolbb28942d2013-03-08 20:43:01 -0800518
519 int width = root.getWidth();
520 int height = root.getHeight();
Doris Liuc7fe2c52013-02-26 10:54:25 -0800521 mFocusManager.setPreviewSize(width, height);
522 // Full-screen screennail
523 if (Util.getDisplayRotation(mActivity) % 180 == 0) {
524 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(width, height);
525 } else {
526 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(height, width);
527 }
528 // Set touch focus listener.
Michael Kolbb28942d2013-03-08 20:43:01 -0800529 mActivity.setSingleTapUpListener(root);
530 openCameraCommon();
Michael Kolb25668cd2013-01-29 10:33:22 -0800531 onFullScreenChanged(mActivity.isInCameraApp());
532 }
533
Michael Kolbb28942d2013-03-08 20:43:01 -0800534 private void switchCamera() {
535 if (mPaused) return;
536
537 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
538 mCameraId = mPendingSwitchCameraId;
539 mPendingSwitchCameraId = -1;
540 setCameraId(mCameraId);
541
542 // from onPause
543 closeCamera();
544 mUI.collapseCameraControls();
545 mUI.clearFaces();
546 if (mFocusManager != null) mFocusManager.removeMessages();
547
548 // Restart the camera and initialize the UI. From onCreate.
549 mPreferences.setLocalId(mActivity, mCameraId);
550 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
551 try {
552 mCameraDevice = Util.openCamera(mActivity, mCameraId);
553 mParameters = mCameraDevice.getParameters();
554 } catch (CameraHardwareException e) {
555 Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
556 return;
557 } catch (CameraDisabledException e) {
558 Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
559 return;
Doris Liu7298e882013-03-05 09:54:25 -0800560 }
Michael Kolbb28942d2013-03-08 20:43:01 -0800561 initializeCapabilities();
562 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
563 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
564 mFocusManager.setMirror(mirror);
565 mFocusManager.setParameters(mInitialParams);
566 setupPreview();
567
568 openCameraCommon();
569
570 if (ApiHelper.HAS_SURFACE_TEXTURE) {
571 // Start switch camera animation. Post a message because
572 // onFrameAvailable from the old camera may already exist.
573 mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
Doris Liu8d2d0f52013-03-04 22:19:10 -0800574 }
575 }
576
Michael Kolbb28942d2013-03-08 20:43:01 -0800577 protected void setCameraId(int cameraId) {
578 ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
579 pref.setValue("" + cameraId);
580 }
581
582 // either open a new camera or switch cameras
583 private void openCameraCommon() {
Michael Kolb25668cd2013-01-29 10:33:22 -0800584 loadCameraPreferences();
Michael Kolbb28942d2013-03-08 20:43:01 -0800585
586 mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
587 updateSceneMode();
588 showTapToFocusToastIfNeeded();
589
590
Michael Kolb25668cd2013-01-29 10:33:22 -0800591 }
592
Michael Kolbb28942d2013-03-08 20:43:01 -0800593 public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight) {
594 Log.d(TAG, "Preview size changed.");
595 if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
596 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(
597 previewWidth, previewHeight);
Doris Liudfde1ea2013-04-10 16:43:43 -0700598 mActivity.notifyScreenNailChanged();
Michael Kolbb28942d2013-03-08 20:43:01 -0800599 }
Michael Kolb25668cd2013-01-29 10:33:22 -0800600
601 private void resetExposureCompensation() {
602 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
603 CameraSettings.EXPOSURE_DEFAULT_VALUE);
604 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
605 Editor editor = mPreferences.edit();
606 editor.putString(CameraSettings.KEY_EXPOSURE, "0");
607 editor.apply();
608 }
609 }
610
611 private void keepMediaProviderInstance() {
612 // We want to keep a reference to MediaProvider in camera's lifecycle.
613 // TODO: Utilize mMediaProviderClient instance to replace
614 // ContentResolver calls.
615 if (mMediaProviderClient == null) {
616 mMediaProviderClient = mContentResolver
617 .acquireContentProviderClient(MediaStore.AUTHORITY);
618 }
619 }
620
621 // Snapshots can only be taken after this is called. It should be called
622 // once only. We could have done these things in onCreate() but we want to
623 // make preview screen appear as soon as possible.
624 private void initializeFirstTime() {
625 if (mFirstTimeInitialized) return;
626
627 // Initialize location service.
628 boolean recordLocation = RecordLocationPreference.get(
629 mPreferences, mContentResolver);
630 mLocationManager.recordLocation(recordLocation);
631
632 keepMediaProviderInstance();
633
Michael Kolbb28942d2013-03-08 20:43:01 -0800634 mUI.initializeFirstTime();
Angus Kong52fe0022013-01-31 18:22:44 -0800635 MediaSaveService s = mActivity.getMediaSaveService();
636 // We set the listener only when both service and shutterbutton
637 // are initialized.
638 if (s != null) {
639 s.setListener(this);
640 }
Michael Kolb25668cd2013-01-29 10:33:22 -0800641
Michael Kolb25668cd2013-01-29 10:33:22 -0800642 mNamedImages = new NamedImages();
643
644 mFirstTimeInitialized = true;
645 addIdleHandler();
646
647 mActivity.updateStorageSpaceAndHint();
648 }
649
Michael Kolbb28942d2013-03-08 20:43:01 -0800650 // If the activity is paused and resumed, this method will be called in
651 // onResume.
652 private void initializeSecondTime() {
653 // Start location update if needed.
654 boolean recordLocation = RecordLocationPreference.get(
655 mPreferences, mContentResolver);
656 mLocationManager.recordLocation(recordLocation);
657 MediaSaveService s = mActivity.getMediaSaveService();
658 if (s != null) {
659 s.setListener(this);
660 }
661 mNamedImages = new NamedImages();
662 mUI.initializeSecondTime(mParameters);
663 keepMediaProviderInstance();
664 }
665
666 @Override
667 public void onSurfaceCreated(SurfaceHolder holder) {
668 // Do not access the camera if camera start up thread is not finished.
669 if (mCameraDevice == null || mCameraStartUpThread != null)
670 return;
671
672 mCameraDevice.setPreviewDisplayAsync(holder);
673 // This happens when onConfigurationChanged arrives, surface has been
674 // destroyed, and there is no onFullScreenChanged.
675 if (mCameraState == PREVIEW_STOPPED) {
676 setupPreview();
677 }
678 }
679
Michael Kolb25668cd2013-01-29 10:33:22 -0800680 private void showTapToFocusToastIfNeeded() {
681 // Show the tap to focus toast if this is the first start.
682 if (mFocusAreaSupported &&
683 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
684 // Delay the toast for one second to wait for orientation.
685 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
686 }
687 }
688
689 private void addIdleHandler() {
690 MessageQueue queue = Looper.myQueue();
691 queue.addIdleHandler(new MessageQueue.IdleHandler() {
692 @Override
693 public boolean queueIdle() {
694 Storage.ensureOSXCompatible();
695 return false;
696 }
697 });
698 }
699
Michael Kolb25668cd2013-01-29 10:33:22 -0800700 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
701 @Override
702 public void startFaceDetection() {
703 if (!ApiHelper.HAS_FACE_DETECTION) return;
704 if (mFaceDetectionStarted) return;
705 if (mParameters.getMaxNumDetectedFaces() > 0) {
706 mFaceDetectionStarted = true;
Michael Kolb25668cd2013-01-29 10:33:22 -0800707 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Michael Kolbb28942d2013-03-08 20:43:01 -0800708 mUI.onStartFaceDetection(mDisplayOrientation,
709 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
710 mCameraDevice.setFaceDetectionListener(mUI);
Michael Kolb25668cd2013-01-29 10:33:22 -0800711 mCameraDevice.startFaceDetection();
712 }
713 }
714
715 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
716 @Override
717 public void stopFaceDetection() {
718 if (!ApiHelper.HAS_FACE_DETECTION) return;
719 if (!mFaceDetectionStarted) return;
720 if (mParameters.getMaxNumDetectedFaces() > 0) {
721 mFaceDetectionStarted = false;
722 mCameraDevice.setFaceDetectionListener(null);
723 mCameraDevice.stopFaceDetection();
Michael Kolbb28942d2013-03-08 20:43:01 -0800724 mUI.clearFaces();
Michael Kolb25668cd2013-01-29 10:33:22 -0800725 }
726 }
727
728 @Override
729 public boolean dispatchTouchEvent(MotionEvent m) {
730 if (mCameraState == SWITCHING_CAMERA) return true;
Michael Kolbb28942d2013-03-08 20:43:01 -0800731 return mUI.dispatchTouchEvent(m);
Michael Kolb25668cd2013-01-29 10:33:22 -0800732 }
733
734 private final class ShutterCallback
735 implements android.hardware.Camera.ShutterCallback {
Angus Kong1d752532013-03-25 23:11:43 -0700736
737 private boolean mAnimateFlash;
738
739 public ShutterCallback(boolean animateFlash) {
740 mAnimateFlash = animateFlash;
741 }
742
Michael Kolb25668cd2013-01-29 10:33:22 -0800743 @Override
744 public void onShutter() {
745 mShutterCallbackTime = System.currentTimeMillis();
746 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
747 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Angus Kong1d752532013-03-25 23:11:43 -0700748 if (mAnimateFlash) {
749 mActivity.runOnUiThread(mFlashRunnable);
750 }
Michael Kolb25668cd2013-01-29 10:33:22 -0800751 }
752 }
753
754 private final class PostViewPictureCallback implements PictureCallback {
755 @Override
756 public void onPictureTaken(
757 byte [] data, android.hardware.Camera camera) {
758 mPostViewPictureCallbackTime = System.currentTimeMillis();
759 Log.v(TAG, "mShutterToPostViewCallbackTime = "
760 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
761 + "ms");
762 }
763 }
764
765 private final class RawPictureCallback implements PictureCallback {
766 @Override
767 public void onPictureTaken(
768 byte [] rawData, android.hardware.Camera camera) {
769 mRawPictureCallbackTime = System.currentTimeMillis();
770 Log.v(TAG, "mShutterToRawCallbackTime = "
771 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
772 }
773 }
774
775 private final class JpegPictureCallback implements PictureCallback {
776 Location mLocation;
777
778 public JpegPictureCallback(Location loc) {
779 mLocation = loc;
780 }
781
782 @Override
783 public void onPictureTaken(
784 final byte [] jpegData, final android.hardware.Camera camera) {
785 if (mPaused) {
786 return;
787 }
788 if (mSceneMode == Util.SCENE_MODE_HDR) {
789 mActivity.showSwitcher();
790 mActivity.setSwipingEnabled(true);
791 }
792
793 mJpegPictureCallbackTime = System.currentTimeMillis();
794 // If postview callback has arrived, the captured image is displayed
795 // in postview callback. If not, the captured image is displayed in
796 // raw picture callback.
797 if (mPostViewPictureCallbackTime != 0) {
798 mShutterToPictureDisplayedTime =
799 mPostViewPictureCallbackTime - mShutterCallbackTime;
800 mPictureDisplayedToJpegCallbackTime =
801 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
802 } else {
803 mShutterToPictureDisplayedTime =
804 mRawPictureCallbackTime - mShutterCallbackTime;
805 mPictureDisplayedToJpegCallbackTime =
806 mJpegPictureCallbackTime - mRawPictureCallbackTime;
807 }
808 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
809 + mPictureDisplayedToJpegCallbackTime + "ms");
810
811 // Only animate when in full screen capture mode
812 // i.e. If monkey/a user swipes to the gallery during picture taking,
813 // don't show animation
814 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
815 && mActivity.mShowCameraAppView) {
816 // Finish capture animation
Michael Kolbc43e5b62013-04-30 15:34:19 -0700817 mHandler.removeMessages(CAPTURE_ANIMATION_DONE);
Michael Kolb25668cd2013-01-29 10:33:22 -0800818 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSlide();
Michael Kolbc43e5b62013-04-30 15:34:19 -0700819 mHandler.sendEmptyMessageDelayed(CAPTURE_ANIMATION_DONE,
820 CaptureAnimManager.getAnimationDuration());
Michael Kolb25668cd2013-01-29 10:33:22 -0800821 }
822 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
823 if (!mIsImageCaptureIntent) {
824 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
825 setupPreview();
826 } else {
827 // Camera HAL of some devices have a bug. Starting preview
828 // immediately after taking a picture will fail. Wait some
829 // time before starting the preview.
830 mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
831 }
832 }
833
834 if (!mIsImageCaptureIntent) {
835 // Calculate the width and the height of the jpeg.
836 Size s = mParameters.getPictureSize();
Angus Kongdd2e5112013-03-26 11:40:40 -0700837 ExifInterface exif = Exif.getExif(jpegData);
838 int orientation = Exif.getOrientation(exif);
Michael Kolb25668cd2013-01-29 10:33:22 -0800839 int width, height;
840 if ((mJpegRotation + orientation) % 180 == 0) {
841 width = s.width;
842 height = s.height;
843 } else {
844 width = s.height;
845 height = s.width;
846 }
847 String title = mNamedImages.getTitle();
848 long date = mNamedImages.getDate();
849 if (title == null) {
850 Log.e(TAG, "Unbalanced name/data pair");
851 } else {
852 if (date == -1) date = mCaptureStartTime;
Angus Kongdd2e5112013-03-26 11:40:40 -0700853 if (mHeading >= 0) {
854 // heading direction has been updated by the sensor.
855 ExifTag directionRefTag = exif.buildTag(
856 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
857 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
858 ExifTag directionTag = exif.buildTag(
859 ExifInterface.TAG_GPS_IMG_DIRECTION,
860 new Rational(mHeading, 1));
861 exif.setTag(directionRefTag);
862 exif.setTag(directionTag);
863 }
Angus Kong52fe0022013-01-31 18:22:44 -0800864 mActivity.getMediaSaveService().addImage(
865 jpegData, title, date, mLocation, width, height,
Angus Kongdd2e5112013-03-26 11:40:40 -0700866 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb25668cd2013-01-29 10:33:22 -0800867 }
868 } else {
869 mJpegImageData = jpegData;
870 if (!mQuickCapture) {
Michael Kolbb28942d2013-03-08 20:43:01 -0800871 mUI.showPostCaptureAlert();
Michael Kolb25668cd2013-01-29 10:33:22 -0800872 } else {
Michael Kolbb28942d2013-03-08 20:43:01 -0800873 onCaptureDone();
Michael Kolb25668cd2013-01-29 10:33:22 -0800874 }
875 }
876
877 // Check this in advance of each shot so we don't add to shutter
878 // latency. It's true that someone else could write to the SD card in
879 // the mean time and fill it, but that could have happened between the
880 // shutter press and saving the JPEG too.
881 mActivity.updateStorageSpaceAndHint();
882
883 long now = System.currentTimeMillis();
884 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
885 Log.v(TAG, "mJpegCallbackFinishTime = "
886 + mJpegCallbackFinishTime + "ms");
887 mJpegPictureCallbackTime = 0;
888 }
889 }
890
891 private final class AutoFocusCallback
892 implements android.hardware.Camera.AutoFocusCallback {
893 @Override
894 public void onAutoFocus(
895 boolean focused, android.hardware.Camera camera) {
896 if (mPaused) return;
897
898 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
899 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
900 setCameraState(IDLE);
Michael Kolbb28942d2013-03-08 20:43:01 -0800901 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb25668cd2013-01-29 10:33:22 -0800902 }
903 }
904
905 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
906 private final class AutoFocusMoveCallback
907 implements android.hardware.Camera.AutoFocusMoveCallback {
908 @Override
909 public void onAutoFocusMoving(
910 boolean moving, android.hardware.Camera camera) {
911 mFocusManager.onAutoFocusMoving(moving);
912 }
913 }
914
915 private static class NamedImages {
916 private ArrayList<NamedEntity> mQueue;
917 private boolean mStop;
918 private NamedEntity mNamedEntity;
919
920 public NamedImages() {
921 mQueue = new ArrayList<NamedEntity>();
922 }
923
924 public void nameNewImage(ContentResolver resolver, long date) {
925 NamedEntity r = new NamedEntity();
926 r.title = Util.createJpegName(date);
927 r.date = date;
928 mQueue.add(r);
929 }
930
931 public String getTitle() {
932 if (mQueue.isEmpty()) {
933 mNamedEntity = null;
934 return null;
935 }
936 mNamedEntity = mQueue.get(0);
937 mQueue.remove(0);
938
939 return mNamedEntity.title;
940 }
941
942 // Must be called after getTitle().
943 public long getDate() {
944 if (mNamedEntity == null) return -1;
945 return mNamedEntity.date;
946 }
947
948 private static class NamedEntity {
949 String title;
950 long date;
951 }
952 }
953
954 private void setCameraState(int state) {
955 mCameraState = state;
956 switch (state) {
Michael Kolbb28942d2013-03-08 20:43:01 -0800957 case PhotoController.PREVIEW_STOPPED:
958 case PhotoController.SNAPSHOT_IN_PROGRESS:
959 case PhotoController.FOCUSING:
960 case PhotoController.SWITCHING_CAMERA:
961 mUI.enableGestures(false);
962 break;
963 case PhotoController.IDLE:
964 if (mActivity.isInCameraApp()) {
965 mUI.enableGestures(true);
966 }
967 break;
Michael Kolb25668cd2013-01-29 10:33:22 -0800968 }
969 }
970
971 private void animateFlash() {
972 // Only animate when in full screen capture mode
973 // i.e. If monkey/a user swipes to the gallery during picture taking,
974 // don't show animation
975 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
976 && mActivity.mShowCameraAppView) {
977 // Start capture animation.
978 ((CameraScreenNail) mActivity.mCameraScreenNail).animateFlash(mDisplayRotation);
Michael Kolbc43e5b62013-04-30 15:34:19 -0700979 mUI.enablePreviewThumb(true);
980 mHandler.sendEmptyMessageDelayed(CAPTURE_ANIMATION_DONE,
981 CaptureAnimManager.getAnimationDuration());
Michael Kolb25668cd2013-01-29 10:33:22 -0800982 }
983 }
984
985 @Override
986 public boolean capture() {
987 // If we are already in the middle of taking a snapshot or the image save request
988 // is full then ignore.
989 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Angus Kong52fe0022013-01-31 18:22:44 -0800990 || mCameraState == SWITCHING_CAMERA
991 || mActivity.getMediaSaveService().isQueueFull()) {
Michael Kolb25668cd2013-01-29 10:33:22 -0800992 return false;
993 }
994 mCaptureStartTime = System.currentTimeMillis();
995 mPostViewPictureCallbackTime = 0;
996 mJpegImageData = null;
997
998 final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR);
999
1000 if (animateBefore) {
1001 animateFlash();
1002 }
1003
1004 // Set rotation and gps data.
Doris Liu9a324802013-02-15 10:55:37 -08001005 int orientation;
1006 // We need to be consistent with the framework orientation (i.e. the
1007 // orientation of the UI.) when the auto-rotate screen setting is on.
1008 if (mActivity.isAutoRotateScreen()) {
1009 orientation = (360 - mDisplayRotation) % 360;
1010 } else {
1011 orientation = mOrientation;
1012 }
1013 mJpegRotation = Util.getJpegRotation(mCameraId, orientation);
Michael Kolb25668cd2013-01-29 10:33:22 -08001014 mParameters.setRotation(mJpegRotation);
1015 Location loc = mLocationManager.getCurrentLocation();
1016 Util.setGpsParameters(mParameters, loc);
1017 mCameraDevice.setParameters(mParameters);
1018
Angus Kong1d752532013-03-25 23:11:43 -07001019 mCameraDevice.takePicture2(new ShutterCallback(!animateBefore),
1020 mRawPictureCallback, mPostViewPictureCallback,
1021 new JpegPictureCallback(loc), mCameraState,
1022 mFocusManager.getFocusState());
Michael Kolb25668cd2013-01-29 10:33:22 -08001023
1024 mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime);
1025
1026 mFaceDetectionStarted = false;
1027 setCameraState(SNAPSHOT_IN_PROGRESS);
Bobby Georgescu1d99f5c2013-04-01 15:33:17 -07001028 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1029 UsageStatistics.ACTION_CAPTURE_DONE, "Photo");
Michael Kolb25668cd2013-01-29 10:33:22 -08001030 return true;
1031 }
1032
1033 @Override
1034 public void setFocusParameters() {
1035 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1036 }
1037
1038 private int getPreferredCameraId(ComboPreferences preferences) {
1039 int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
1040 if (intentCameraId != -1) {
1041 // Testing purpose. Launch a specific camera through the intent
1042 // extras.
1043 return intentCameraId;
1044 } else {
1045 return CameraSettings.readPreferredCameraId(preferences);
1046 }
1047 }
1048
Michael Kolb25668cd2013-01-29 10:33:22 -08001049 @Override
1050 public void onFullScreenChanged(boolean full) {
Michael Kolbb28942d2013-03-08 20:43:01 -08001051 mUI.onFullScreenChanged(full);
Michael Kolb25668cd2013-01-29 10:33:22 -08001052 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1053 if (mActivity.mCameraScreenNail != null) {
1054 ((CameraScreenNail) mActivity.mCameraScreenNail).setFullScreen(full);
1055 }
1056 return;
1057 }
Michael Kolb25668cd2013-01-29 10:33:22 -08001058 }
1059
Michael Kolbb28942d2013-03-08 20:43:01 -08001060 private void updateSceneMode() {
Michael Kolb25668cd2013-01-29 10:33:22 -08001061 // If scene mode is set, we cannot set flash mode, white balance, and
1062 // focus mode, instead, we read it from driver
1063 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1064 overrideCameraSettings(mParameters.getFlashMode(),
1065 mParameters.getWhiteBalance(), mParameters.getFocusMode());
1066 } else {
1067 overrideCameraSettings(null, null, null);
1068 }
1069 }
1070
1071 private void overrideCameraSettings(final String flashMode,
1072 final String whiteBalance, final String focusMode) {
Michael Kolbb28942d2013-03-08 20:43:01 -08001073 mUI.overrideSettings(
1074 CameraSettings.KEY_FLASH_MODE, flashMode,
1075 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
1076 CameraSettings.KEY_FOCUS_MODE, focusMode);
Michael Kolb25668cd2013-01-29 10:33:22 -08001077 }
1078
1079 private void loadCameraPreferences() {
1080 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
1081 mCameraId, CameraHolder.instance().getCameraInfo());
1082 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
1083 }
1084
1085 @Override
Michael Kolb25668cd2013-01-29 10:33:22 -08001086 public void onOrientationChanged(int orientation) {
1087 // We keep the last known orientation. So if the user first orient
1088 // the camera then point the camera to floor or sky, we still have
1089 // the correct orientation.
1090 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
1091 mOrientation = Util.roundOrientation(orientation, mOrientation);
1092
1093 // Show the toast after getting the first orientation changed.
1094 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1095 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1096 showTapToFocusToast();
1097 }
1098 }
1099
1100 @Override
1101 public void onStop() {
1102 if (mMediaProviderClient != null) {
1103 mMediaProviderClient.release();
1104 mMediaProviderClient = null;
1105 }
1106 }
1107
Michael Kolbb28942d2013-03-08 20:43:01 -08001108 @Override
1109 public void onCaptureCancelled() {
1110 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1111 mActivity.finish();
Michael Kolb25668cd2013-01-29 10:33:22 -08001112 }
1113
Michael Kolbb28942d2013-03-08 20:43:01 -08001114 @Override
1115 public void onCaptureRetake() {
Michael Kolb25668cd2013-01-29 10:33:22 -08001116 if (mPaused)
1117 return;
Michael Kolbb28942d2013-03-08 20:43:01 -08001118 mUI.hidePostCaptureAlert();
Michael Kolb25668cd2013-01-29 10:33:22 -08001119 setupPreview();
1120 }
1121
Michael Kolbb28942d2013-03-08 20:43:01 -08001122 @Override
1123 public void onCaptureDone() {
Michael Kolb25668cd2013-01-29 10:33:22 -08001124 if (mPaused) {
1125 return;
1126 }
1127
1128 byte[] data = mJpegImageData;
1129
1130 if (mCropValue == null) {
1131 // First handle the no crop case -- just return the value. If the
1132 // caller specifies a "save uri" then write the data to its
1133 // stream. Otherwise, pass back a scaled down version of the bitmap
1134 // directly in the extras.
1135 if (mSaveUri != null) {
1136 OutputStream outputStream = null;
1137 try {
1138 outputStream = mContentResolver.openOutputStream(mSaveUri);
1139 outputStream.write(data);
1140 outputStream.close();
1141
1142 mActivity.setResultEx(Activity.RESULT_OK);
1143 mActivity.finish();
1144 } catch (IOException ex) {
1145 // ignore exception
1146 } finally {
1147 Util.closeSilently(outputStream);
1148 }
1149 } else {
Angus Kongdd2e5112013-03-26 11:40:40 -07001150 ExifInterface exif = Exif.getExif(data);
1151 int orientation = Exif.getOrientation(exif);
Michael Kolb25668cd2013-01-29 10:33:22 -08001152 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
1153 bitmap = Util.rotate(bitmap, orientation);
1154 mActivity.setResultEx(Activity.RESULT_OK,
1155 new Intent("inline-data").putExtra("data", bitmap));
1156 mActivity.finish();
1157 }
1158 } else {
1159 // Save the image to a temp file and invoke the cropper
1160 Uri tempUri = null;
1161 FileOutputStream tempStream = null;
1162 try {
1163 File path = mActivity.getFileStreamPath(sTempCropFilename);
1164 path.delete();
1165 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1166 tempStream.write(data);
1167 tempStream.close();
1168 tempUri = Uri.fromFile(path);
1169 } catch (FileNotFoundException ex) {
1170 mActivity.setResultEx(Activity.RESULT_CANCELED);
1171 mActivity.finish();
1172 return;
1173 } catch (IOException ex) {
1174 mActivity.setResultEx(Activity.RESULT_CANCELED);
1175 mActivity.finish();
1176 return;
1177 } finally {
1178 Util.closeSilently(tempStream);
1179 }
1180
1181 Bundle newExtras = new Bundle();
1182 if (mCropValue.equals("circle")) {
1183 newExtras.putString("circleCrop", "true");
1184 }
1185 if (mSaveUri != null) {
1186 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1187 } else {
1188 newExtras.putBoolean(CropExtras.KEY_RETURN_DATA, true);
1189 }
1190 if (mActivity.isSecureCamera()) {
1191 newExtras.putBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, true);
1192 }
1193
1194 Intent cropIntent = new Intent(FilterShowActivity.CROP_ACTION);
1195
1196 cropIntent.setData(tempUri);
1197 cropIntent.putExtras(newExtras);
1198
1199 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1200 }
1201 }
1202
Michael Kolb25668cd2013-01-29 10:33:22 -08001203 @Override
1204 public void onShutterButtonFocus(boolean pressed) {
Michael Kolbb28942d2013-03-08 20:43:01 -08001205 if (mPaused || mUI.collapseCameraControls()
Michael Kolb25668cd2013-01-29 10:33:22 -08001206 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1207 || (mCameraState == PREVIEW_STOPPED)) return;
1208
1209 // Do not do focus if there is not enough storage.
1210 if (pressed && !canTakePicture()) return;
1211
1212 if (pressed) {
Michael Kolb25668cd2013-01-29 10:33:22 -08001213 mFocusManager.onShutterDown();
1214 } else {
Doris Liu723353a2013-02-07 14:36:32 -08001215 // for countdown mode, we need to postpone the shutter release
1216 // i.e. lock the focus during countdown.
Michael Kolbb28942d2013-03-08 20:43:01 -08001217 if (!mUI.isCountingDown()) {
Doris Liu723353a2013-02-07 14:36:32 -08001218 mFocusManager.onShutterUp();
1219 }
Michael Kolb25668cd2013-01-29 10:33:22 -08001220 }
1221 }
1222
1223 @Override
1224 public void onShutterButtonClick() {
Michael Kolbb28942d2013-03-08 20:43:01 -08001225 if (mPaused || mUI.collapseCameraControls()
Michael Kolb25668cd2013-01-29 10:33:22 -08001226 || (mCameraState == SWITCHING_CAMERA)
1227 || (mCameraState == PREVIEW_STOPPED)) return;
1228
1229 // Do not take the picture if there is not enough storage.
1230 if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
1231 Log.i(TAG, "Not enough space or storage not ready. remaining="
1232 + mActivity.getStorageSpace());
1233 return;
1234 }
1235 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1236
Doris Liucf18f7e2013-04-16 09:50:56 -07001237 if (mSceneMode == Util.SCENE_MODE_HDR) {
1238 mActivity.hideSwitcher();
1239 mActivity.setSwipingEnabled(false);
1240 }
Michael Kolb25668cd2013-01-29 10:33:22 -08001241 // If the user wants to do a snapshot while the previous one is still
1242 // in progress, remember the fact and do it after we finish the previous
1243 // one and re-start the preview. Snapshot in progress also includes the
1244 // state that autofocus is focusing and a picture will be taken when
1245 // focus callback arrives.
1246 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1247 && !mIsImageCaptureIntent) {
1248 mSnapshotOnIdle = true;
1249 return;
1250 }
1251
1252 String timer = mPreferences.getString(
1253 CameraSettings.KEY_TIMER,
1254 mActivity.getString(R.string.pref_camera_timer_default));
1255 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1256 mActivity.getString(R.string.pref_camera_timer_sound_default))
1257 .equals(mActivity.getString(R.string.setting_on_value));
1258
1259 int seconds = Integer.parseInt(timer);
1260 // When shutter button is pressed, check whether the previous countdown is
1261 // finished. If not, cancel the previous countdown and start a new one.
Michael Kolbb28942d2013-03-08 20:43:01 -08001262 if (mUI.isCountingDown()) {
1263 mUI.cancelCountDown();
1264 }
1265 if (seconds > 0) {
1266 mUI.startCountDown(seconds, playSound);
Michael Kolb25668cd2013-01-29 10:33:22 -08001267 } else {
1268 mSnapshotOnIdle = false;
1269 mFocusManager.doSnap();
1270 }
1271 }
1272
1273 @Override
1274 public void installIntentFilter() {
1275 }
1276
1277 @Override
1278 public boolean updateStorageHintOnResume() {
1279 return mFirstTimeInitialized;
1280 }
1281
1282 @Override
1283 public void updateCameraAppView() {
1284 }
1285
1286 @Override
1287 public void onResumeBeforeSuper() {
1288 mPaused = false;
1289 }
1290
1291 @Override
1292 public void onResumeAfterSuper() {
1293 if (mOpenCameraFail || mCameraDisabled) return;
1294
1295 mJpegPictureCallbackTime = 0;
1296 mZoomValue = 0;
1297
1298 // Start the preview if it is not started.
1299 if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
1300 resetExposureCompensation();
1301 mCameraStartUpThread = new CameraStartUpThread();
1302 mCameraStartUpThread.start();
1303 }
1304
1305 // If first time initialization is not finished, put it in the
1306 // message queue.
1307 if (!mFirstTimeInitialized) {
1308 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1309 } else {
1310 initializeSecondTime();
1311 }
1312 keepScreenOnAwhile();
1313
1314 // Dismiss open menu if exists.
1315 PopupManager.getInstance(mActivity).notifyShowPopup(null);
Bobby Georgescuf37648b2013-03-12 22:45:17 -07001316 UsageStatistics.onContentViewChanged(
1317 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
Angus Kongdd2e5112013-03-26 11:40:40 -07001318
1319 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1320 if (gsensor != null) {
1321 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1322 }
1323
1324 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1325 if (msensor != null) {
1326 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1327 }
Michael Kolb25668cd2013-01-29 10:33:22 -08001328 }
1329
1330 void waitCameraStartUpThread() {
1331 try {
1332 if (mCameraStartUpThread != null) {
1333 mCameraStartUpThread.cancel();
1334 mCameraStartUpThread.join();
1335 mCameraStartUpThread = null;
1336 setCameraState(IDLE);
1337 }
1338 } catch (InterruptedException e) {
1339 // ignore
1340 }
1341 }
1342
1343 @Override
1344 public void onPauseBeforeSuper() {
1345 mPaused = true;
Angus Kongdd2e5112013-03-26 11:40:40 -07001346 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1347 if (gsensor != null) {
1348 mSensorManager.unregisterListener(this, gsensor);
1349 }
1350
1351 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1352 if (msensor != null) {
1353 mSensorManager.unregisterListener(this, msensor);
1354 }
Michael Kolb25668cd2013-01-29 10:33:22 -08001355 }
1356
1357 @Override
1358 public void onPauseAfterSuper() {
1359 // Wait the camera start up thread to finish.
1360 waitCameraStartUpThread();
1361
1362 // When camera is started from secure lock screen for the first time
1363 // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1364 // To reduce the latency, keep the camera for a short time so it does
1365 // not need to be opened again.
1366 if (mCameraDevice != null && mActivity.isSecureCamera()
1367 && ActivityBase.isFirstStartAfterScreenOn()) {
1368 ActivityBase.resetFirstStartAfterScreenOn();
1369 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1370 }
1371 // Reset the focus first. Camera CTS does not guarantee that
1372 // cancelAutoFocus is allowed after preview stops.
1373 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1374 mCameraDevice.cancelAutoFocus();
1375 }
1376 stopPreview();
Doris Liu67de6632013-02-22 14:31:17 -08001377 // Release surface texture.
1378 ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture();
Michael Kolb25668cd2013-01-29 10:33:22 -08001379
Angus Kongc0993982013-01-29 17:43:48 -08001380 mNamedImages = null;
Michael Kolb25668cd2013-01-29 10:33:22 -08001381
1382 if (mLocationManager != null) mLocationManager.recordLocation(false);
1383
1384 // If we are in an image capture intent and has taken
1385 // a picture, we just clear it in onPause.
1386 mJpegImageData = null;
1387
1388 // Remove the messages in the event queue.
1389 mHandler.removeMessages(SETUP_PREVIEW);
1390 mHandler.removeMessages(FIRST_TIME_INIT);
1391 mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
1392 mHandler.removeMessages(SWITCH_CAMERA);
1393 mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
1394 mHandler.removeMessages(CAMERA_OPEN_DONE);
1395 mHandler.removeMessages(START_PREVIEW_DONE);
1396 mHandler.removeMessages(OPEN_CAMERA_FAIL);
1397 mHandler.removeMessages(CAMERA_DISABLED);
1398
Michael Kolbb28942d2013-03-08 20:43:01 -08001399 closeCamera();
1400
1401 resetScreenOn();
1402 mUI.onPause();
1403
Michael Kolb25668cd2013-01-29 10:33:22 -08001404 mPendingSwitchCameraId = -1;
1405 if (mFocusManager != null) mFocusManager.removeMessages();
Angus Kong52fe0022013-01-31 18:22:44 -08001406 MediaSaveService s = mActivity.getMediaSaveService();
1407 if (s != null) {
1408 s.setListener(null);
1409 }
Michael Kolb25668cd2013-01-29 10:33:22 -08001410 }
1411
Michael Kolb25668cd2013-01-29 10:33:22 -08001412 /**
1413 * The focus manager is the first UI related element to get initialized,
1414 * and it requires the RenderOverlay, so initialize it here
1415 */
1416 private void initializeFocusManager() {
1417 // Create FocusManager object. startPreview needs it.
Michael Kolb25668cd2013-01-29 10:33:22 -08001418 // if mFocusManager not null, reuse it
1419 // otherwise create a new instance
1420 if (mFocusManager != null) {
1421 mFocusManager.removeMessages();
1422 } else {
1423 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1424 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
1425 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1426 R.array.pref_camera_focusmode_default_array);
1427 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
1428 mInitialParams, this, mirror,
Michael Kolbb28942d2013-03-08 20:43:01 -08001429 mActivity.getMainLooper(), mUI);
Michael Kolb25668cd2013-01-29 10:33:22 -08001430 }
1431 }
1432
Michael Kolb25668cd2013-01-29 10:33:22 -08001433 @Override
1434 public void onConfigurationChanged(Configuration newConfig) {
1435 Log.v(TAG, "onConfigurationChanged");
1436 setDisplayOrientation();
Michael Kolb25668cd2013-01-29 10:33:22 -08001437 }
1438
1439 @Override
1440 public void onActivityResult(
1441 int requestCode, int resultCode, Intent data) {
1442 switch (requestCode) {
1443 case REQUEST_CROP: {
1444 Intent intent = new Intent();
1445 if (data != null) {
1446 Bundle extras = data.getExtras();
1447 if (extras != null) {
1448 intent.putExtras(extras);
1449 }
1450 }
1451 mActivity.setResultEx(resultCode, intent);
1452 mActivity.finish();
1453
1454 File path = mActivity.getFileStreamPath(sTempCropFilename);
1455 path.delete();
1456
1457 break;
1458 }
1459 }
1460 }
1461
1462 private boolean canTakePicture() {
1463 return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
1464 }
1465
1466 @Override
1467 public void autoFocus() {
1468 mFocusStartTime = System.currentTimeMillis();
1469 mCameraDevice.autoFocus(mAutoFocusCallback);
1470 setCameraState(FOCUSING);
1471 }
1472
1473 @Override
1474 public void cancelAutoFocus() {
1475 mCameraDevice.cancelAutoFocus();
1476 setCameraState(IDLE);
1477 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1478 }
1479
1480 // Preview area is touched. Handle touch focus.
1481 @Override
1482 public void onSingleTapUp(View view, int x, int y) {
1483 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1484 || mCameraState == SNAPSHOT_IN_PROGRESS
1485 || mCameraState == SWITCHING_CAMERA
1486 || mCameraState == PREVIEW_STOPPED) {
1487 return;
1488 }
1489
1490 // Do not trigger touch focus if popup window is opened.
Michael Kolbb28942d2013-03-08 20:43:01 -08001491 if (mUI.removeTopLevelPopup()) return;
Michael Kolb25668cd2013-01-29 10:33:22 -08001492
1493 // Check if metering area or focus area is supported.
1494 if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1495 mFocusManager.onSingleTapUp(x, y);
1496 }
1497
1498 @Override
1499 public boolean onBackPressed() {
Michael Kolbb28942d2013-03-08 20:43:01 -08001500 return mUI.onBackPressed();
Michael Kolb25668cd2013-01-29 10:33:22 -08001501 }
1502
1503 @Override
1504 public boolean onKeyDown(int keyCode, KeyEvent event) {
1505 switch (keyCode) {
1506 case KeyEvent.KEYCODE_VOLUME_UP:
1507 case KeyEvent.KEYCODE_VOLUME_DOWN:
1508 case KeyEvent.KEYCODE_FOCUS:
1509 if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
1510 if (event.getRepeatCount() == 0) {
1511 onShutterButtonFocus(true);
1512 }
1513 return true;
1514 }
1515 return false;
1516 case KeyEvent.KEYCODE_CAMERA:
1517 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1518 onShutterButtonClick();
1519 }
1520 return true;
1521 case KeyEvent.KEYCODE_DPAD_CENTER:
1522 // If we get a dpad center event without any focused view, move
1523 // the focus to the shutter button and press it.
1524 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1525 // Start auto-focus immediately to reduce shutter lag. After
1526 // the shutter button gets the focus, onShutterButtonFocus()
1527 // will be called again but it is fine.
Michael Kolbb28942d2013-03-08 20:43:01 -08001528 if (mUI.removeTopLevelPopup()) return true;
Michael Kolb25668cd2013-01-29 10:33:22 -08001529 onShutterButtonFocus(true);
Michael Kolbb28942d2013-03-08 20:43:01 -08001530 mUI.pressShutterButton();
Michael Kolb25668cd2013-01-29 10:33:22 -08001531 }
1532 return true;
1533 }
1534 return false;
1535 }
1536
1537 @Override
1538 public boolean onKeyUp(int keyCode, KeyEvent event) {
1539 switch (keyCode) {
1540 case KeyEvent.KEYCODE_VOLUME_UP:
1541 case KeyEvent.KEYCODE_VOLUME_DOWN:
1542 if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
1543 onShutterButtonClick();
1544 return true;
1545 }
1546 return false;
1547 case KeyEvent.KEYCODE_FOCUS:
1548 if (mFirstTimeInitialized) {
1549 onShutterButtonFocus(false);
1550 }
1551 return true;
1552 }
1553 return false;
1554 }
1555
1556 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1557 private void closeCamera() {
1558 if (mCameraDevice != null) {
1559 mCameraDevice.setZoomChangeListener(null);
1560 if(ApiHelper.HAS_FACE_DETECTION) {
1561 mCameraDevice.setFaceDetectionListener(null);
1562 }
1563 mCameraDevice.setErrorCallback(null);
1564 CameraHolder.instance().release();
1565 mFaceDetectionStarted = false;
1566 mCameraDevice = null;
1567 setCameraState(PREVIEW_STOPPED);
1568 mFocusManager.onCameraReleased();
1569 }
1570 }
1571
1572 private void setDisplayOrientation() {
1573 mDisplayRotation = Util.getDisplayRotation(mActivity);
1574 mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
1575 mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId);
Michael Kolbb28942d2013-03-08 20:43:01 -08001576 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb25668cd2013-01-29 10:33:22 -08001577 if (mFocusManager != null) {
1578 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1579 }
1580 // GLRoot also uses the DisplayRotation, and needs to be told to layout to update
1581 mActivity.getGLRoot().requestLayoutContentPane();
1582 }
1583
1584 // Only called by UI thread.
1585 private void setupPreview() {
1586 mFocusManager.resetTouchFocus();
1587 startPreview();
1588 setCameraState(IDLE);
1589 startFaceDetection();
1590 }
1591
1592 // This can be called by UI Thread or CameraStartUpThread. So this should
1593 // not modify the views.
1594 private void startPreview() {
1595 mCameraDevice.setErrorCallback(mErrorCallback);
1596
1597 // ICS camera frameworks has a bug. Face detection state is not cleared
1598 // after taking a picture. Stop the preview to work around it. The bug
1599 // was fixed in JB.
1600 if (mCameraState != PREVIEW_STOPPED) stopPreview();
1601
1602 setDisplayOrientation();
1603
1604 if (!mSnapshotOnIdle) {
1605 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1606 // resume it because it may have been paused by autoFocus call.
1607 if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
1608 mCameraDevice.cancelAutoFocus();
1609 }
1610 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1611 }
1612 setCameraParameters(UPDATE_PARAM_ALL);
1613
1614 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1615 CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail;
Michael Kolbb28942d2013-03-08 20:43:01 -08001616 if (mUI.getSurfaceTexture() == null) {
Michael Kolb25668cd2013-01-29 10:33:22 -08001617 Size size = mParameters.getPreviewSize();
1618 if (mCameraDisplayOrientation % 180 == 0) {
1619 screenNail.setSize(size.width, size.height);
1620 } else {
1621 screenNail.setSize(size.height, size.width);
1622 }
1623 screenNail.enableAspectRatioClamping();
1624 mActivity.notifyScreenNailChanged();
1625 screenNail.acquireSurfaceTexture();
1626 CameraStartUpThread t = mCameraStartUpThread;
1627 if (t != null && t.isCanceled()) {
1628 return; // Exiting, so no need to get the surface texture.
1629 }
Michael Kolbb28942d2013-03-08 20:43:01 -08001630 mUI.setSurfaceTexture(screenNail.getSurfaceTexture());
Michael Kolb25668cd2013-01-29 10:33:22 -08001631 }
1632 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
Michael Kolbb28942d2013-03-08 20:43:01 -08001633 Object st = mUI.getSurfaceTexture();
1634 if (st != null) {
1635 mCameraDevice.setPreviewTextureAsync((SurfaceTexture) st);
Michael Kolb25668cd2013-01-29 10:33:22 -08001636 }
1637 } else {
1638 mCameraDevice.setDisplayOrientation(mDisplayOrientation);
Michael Kolbb28942d2013-03-08 20:43:01 -08001639 mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
Michael Kolb25668cd2013-01-29 10:33:22 -08001640 }
1641
1642 Log.v(TAG, "startPreview");
1643 mCameraDevice.startPreviewAsync();
1644
1645 mFocusManager.onPreviewStarted();
1646
1647 if (mSnapshotOnIdle) {
1648 mHandler.post(mDoSnapRunnable);
1649 }
1650 }
1651
Michael Kolbb28942d2013-03-08 20:43:01 -08001652 @Override
1653 public void stopPreview() {
Michael Kolb25668cd2013-01-29 10:33:22 -08001654 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1655 Log.v(TAG, "stopPreview");
1656 mCameraDevice.stopPreview();
1657 mFaceDetectionStarted = false;
1658 }
1659 setCameraState(PREVIEW_STOPPED);
1660 if (mFocusManager != null) mFocusManager.onPreviewStopped();
1661 }
1662
1663 @SuppressWarnings("deprecation")
1664 private void updateCameraParametersInitialize() {
1665 // Reset preview frame rate to the maximum because it may be lowered by
1666 // video camera application.
1667 List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates();
1668 if (frameRates != null) {
1669 Integer max = Collections.max(frameRates);
1670 mParameters.setPreviewFrameRate(max);
1671 }
1672
1673 mParameters.set(Util.RECORDING_HINT, Util.FALSE);
1674
1675 // Disable video stabilization. Convenience methods not available in API
1676 // level <= 14
1677 String vstabSupported = mParameters.get("video-stabilization-supported");
1678 if ("true".equals(vstabSupported)) {
1679 mParameters.set("video-stabilization", "false");
1680 }
1681 }
1682
1683 private void updateCameraParametersZoom() {
1684 // Set zoom.
1685 if (mParameters.isZoomSupported()) {
1686 mParameters.setZoom(mZoomValue);
1687 }
1688 }
1689
1690 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1691 private void setAutoExposureLockIfSupported() {
1692 if (mAeLockSupported) {
1693 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1694 }
1695 }
1696
1697 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1698 private void setAutoWhiteBalanceLockIfSupported() {
1699 if (mAwbLockSupported) {
1700 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1701 }
1702 }
1703
1704 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1705 private void setFocusAreasIfSupported() {
1706 if (mFocusAreaSupported) {
1707 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1708 }
1709 }
1710
1711 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1712 private void setMeteringAreasIfSupported() {
1713 if (mMeteringAreaSupported) {
1714 // Use the same area for focus and metering.
1715 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1716 }
1717 }
1718
1719 private void updateCameraParametersPreference() {
1720 setAutoExposureLockIfSupported();
1721 setAutoWhiteBalanceLockIfSupported();
1722 setFocusAreasIfSupported();
1723 setMeteringAreasIfSupported();
1724
1725 // Set picture size.
1726 String pictureSize = mPreferences.getString(
1727 CameraSettings.KEY_PICTURE_SIZE, null);
1728 if (pictureSize == null) {
1729 CameraSettings.initialCameraPictureSize(mActivity, mParameters);
1730 } else {
1731 List<Size> supported = mParameters.getSupportedPictureSizes();
1732 CameraSettings.setCameraPictureSize(
1733 pictureSize, supported, mParameters);
1734 }
1735 Size size = mParameters.getPictureSize();
1736
1737 // Set a preview size that is closest to the viewfinder height and has
1738 // the right aspect ratio.
1739 List<Size> sizes = mParameters.getSupportedPreviewSizes();
1740 Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes,
1741 (double) size.width / size.height);
1742 Size original = mParameters.getPreviewSize();
1743 if (!original.equals(optimalSize)) {
1744 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
1745
1746 // Zoom related settings will be changed for different preview
1747 // sizes, so set and read the parameters to get latest values
1748 mCameraDevice.setParameters(mParameters);
1749 mParameters = mCameraDevice.getParameters();
1750 }
1751 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1752
1753 // Since changing scene mode may change supported values, set scene mode
1754 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1755 // separate preference.
1756 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
1757 mActivity.getString(R.string.pref_camera_hdr_default));
1758 if (mActivity.getString(R.string.setting_on_value).equals(hdr)) {
1759 mSceneMode = Util.SCENE_MODE_HDR;
1760 } else {
1761 mSceneMode = mPreferences.getString(
1762 CameraSettings.KEY_SCENE_MODE,
1763 mActivity.getString(R.string.pref_camera_scenemode_default));
1764 }
1765 if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
1766 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1767 mParameters.setSceneMode(mSceneMode);
1768
1769 // Setting scene mode will change the settings of flash mode,
1770 // white balance, and focus mode. Here we read back the
1771 // parameters, so we can know those settings.
1772 mCameraDevice.setParameters(mParameters);
1773 mParameters = mCameraDevice.getParameters();
1774 }
1775 } else {
1776 mSceneMode = mParameters.getSceneMode();
1777 if (mSceneMode == null) {
1778 mSceneMode = Parameters.SCENE_MODE_AUTO;
1779 }
1780 }
1781
1782 // Set JPEG quality.
1783 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1784 CameraProfile.QUALITY_HIGH);
1785 mParameters.setJpegQuality(jpegQuality);
1786
1787 // For the following settings, we need to check if the settings are
1788 // still supported by latest driver, if not, ignore the settings.
1789
1790 // Set exposure compensation
1791 int value = CameraSettings.readExposure(mPreferences);
1792 int max = mParameters.getMaxExposureCompensation();
1793 int min = mParameters.getMinExposureCompensation();
1794 if (value >= min && value <= max) {
1795 mParameters.setExposureCompensation(value);
1796 } else {
1797 Log.w(TAG, "invalid exposure range: " + value);
1798 }
1799
1800 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1801 // Set flash mode.
1802 String flashMode = mPreferences.getString(
1803 CameraSettings.KEY_FLASH_MODE,
1804 mActivity.getString(R.string.pref_camera_flashmode_default));
1805 List<String> supportedFlash = mParameters.getSupportedFlashModes();
1806 if (Util.isSupported(flashMode, supportedFlash)) {
1807 mParameters.setFlashMode(flashMode);
1808 } else {
1809 flashMode = mParameters.getFlashMode();
1810 if (flashMode == null) {
1811 flashMode = mActivity.getString(
1812 R.string.pref_camera_flashmode_no_flash);
1813 }
1814 }
1815
1816 // Set white balance parameter.
1817 String whiteBalance = mPreferences.getString(
1818 CameraSettings.KEY_WHITE_BALANCE,
1819 mActivity.getString(R.string.pref_camera_whitebalance_default));
1820 if (Util.isSupported(whiteBalance,
1821 mParameters.getSupportedWhiteBalance())) {
1822 mParameters.setWhiteBalance(whiteBalance);
1823 } else {
1824 whiteBalance = mParameters.getWhiteBalance();
1825 if (whiteBalance == null) {
1826 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1827 }
1828 }
1829
1830 // Set focus mode.
1831 mFocusManager.overrideFocusMode(null);
1832 mParameters.setFocusMode(mFocusManager.getFocusMode());
1833 } else {
1834 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1835 }
1836
1837 if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1838 updateAutoFocusMoveCallback();
1839 }
1840 }
1841
1842 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1843 private void updateAutoFocusMoveCallback() {
1844 if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
1845 mCameraDevice.setAutoFocusMoveCallback(
1846 (AutoFocusMoveCallback) mAutoFocusMoveCallback);
1847 } else {
1848 mCameraDevice.setAutoFocusMoveCallback(null);
1849 }
1850 }
1851
1852 // We separate the parameters into several subsets, so we can update only
1853 // the subsets actually need updating. The PREFERENCE set needs extra
1854 // locking because the preference can be changed from GLThread as well.
1855 private void setCameraParameters(int updateSet) {
1856 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1857 updateCameraParametersInitialize();
1858 }
1859
1860 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1861 updateCameraParametersZoom();
1862 }
1863
1864 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
1865 updateCameraParametersPreference();
1866 }
1867
1868 mCameraDevice.setParameters(mParameters);
1869 }
1870
1871 // If the Camera is idle, update the parameters immediately, otherwise
1872 // accumulate them in mUpdateSet and update later.
1873 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1874 mUpdateSet |= additionalUpdateSet;
1875 if (mCameraDevice == null) {
1876 // We will update all the parameters when we open the device, so
1877 // we don't need to do anything now.
1878 mUpdateSet = 0;
1879 return;
1880 } else if (isCameraIdle()) {
1881 setCameraParameters(mUpdateSet);
Michael Kolbb28942d2013-03-08 20:43:01 -08001882 updateSceneMode();
Michael Kolb25668cd2013-01-29 10:33:22 -08001883 mUpdateSet = 0;
1884 } else {
1885 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1886 mHandler.sendEmptyMessageDelayed(
1887 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1888 }
1889 }
1890 }
1891
Michael Kolbb28942d2013-03-08 20:43:01 -08001892 public boolean isCameraIdle() {
Michael Kolb25668cd2013-01-29 10:33:22 -08001893 return (mCameraState == IDLE) ||
1894 (mCameraState == PREVIEW_STOPPED) ||
1895 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1896 && (mCameraState != SWITCHING_CAMERA));
1897 }
1898
Michael Kolbb28942d2013-03-08 20:43:01 -08001899 public boolean isImageCaptureIntent() {
Michael Kolb25668cd2013-01-29 10:33:22 -08001900 String action = mActivity.getIntent().getAction();
1901 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
1902 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
1903 }
1904
1905 private void setupCaptureParams() {
1906 Bundle myExtras = mActivity.getIntent().getExtras();
1907 if (myExtras != null) {
1908 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1909 mCropValue = myExtras.getString("crop");
1910 }
1911 }
1912
Michael Kolb25668cd2013-01-29 10:33:22 -08001913 @Override
1914 public void onSharedPreferenceChanged() {
1915 // ignore the events after "onPause()"
1916 if (mPaused) return;
1917
1918 boolean recordLocation = RecordLocationPreference.get(
1919 mPreferences, mContentResolver);
1920 mLocationManager.recordLocation(recordLocation);
1921
1922 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
Michael Kolbec0f8902013-04-26 11:09:29 -07001923 mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, mPreferences);
Michael Kolb25668cd2013-01-29 10:33:22 -08001924 }
1925
1926 @Override
1927 public void onCameraPickerClicked(int cameraId) {
1928 if (mPaused || mPendingSwitchCameraId != -1) return;
1929
1930 mPendingSwitchCameraId = cameraId;
1931 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1932 Log.v(TAG, "Start to copy texture. cameraId=" + cameraId);
1933 // We need to keep a preview frame for the animation before
1934 // releasing the camera. This will trigger onPreviewTextureCopied.
1935 ((CameraScreenNail) mActivity.mCameraScreenNail).copyTexture();
1936 // Disable all camera controls.
1937 setCameraState(SWITCHING_CAMERA);
1938 } else {
1939 switchCamera();
1940 }
1941 }
1942
Michael Kolb25668cd2013-01-29 10:33:22 -08001943 // Preview texture has been copied. Now camera can be released and the
1944 // animation can be started.
1945 @Override
1946 public void onPreviewTextureCopied() {
1947 mHandler.sendEmptyMessage(SWITCH_CAMERA);
1948 }
1949
1950 @Override
1951 public void onCaptureTextureCopied() {
1952 }
1953
1954 @Override
1955 public void onUserInteraction() {
1956 if (!mActivity.isFinishing()) keepScreenOnAwhile();
1957 }
1958
1959 private void resetScreenOn() {
1960 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1961 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1962 }
1963
1964 private void keepScreenOnAwhile() {
1965 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1966 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1967 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1968 }
1969
1970 // TODO: Delete this function after old camera code is removed
1971 @Override
1972 public void onRestorePreferencesClicked() {
1973 }
1974
1975 @Override
1976 public void onOverriddenPreferencesClicked() {
1977 if (mPaused) return;
Michael Kolbb28942d2013-03-08 20:43:01 -08001978 mUI.showPreferencesToast();
Michael Kolb25668cd2013-01-29 10:33:22 -08001979 }
1980
1981 private void showTapToFocusToast() {
1982 // TODO: Use a toast?
1983 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1984 // Clear the preference.
1985 Editor editor = mPreferences.edit();
1986 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
1987 editor.apply();
1988 }
1989
1990 private void initializeCapabilities() {
1991 mInitialParams = mCameraDevice.getParameters();
1992 mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
1993 mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
1994 mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
1995 mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams);
1996 mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
1997 Util.FOCUS_MODE_CONTINUOUS_PICTURE);
1998 }
1999
Michael Kolb25668cd2013-01-29 10:33:22 -08002000 @Override
2001 public void onCountDownFinished() {
2002 mSnapshotOnIdle = false;
2003 mFocusManager.doSnap();
Doris Liu723353a2013-02-07 14:36:32 -08002004 mFocusManager.onShutterUp();
Michael Kolb25668cd2013-01-29 10:33:22 -08002005 }
2006
Michael Kolb25668cd2013-01-29 10:33:22 -08002007 @Override
2008 public boolean needsSwitcher() {
2009 return !mIsImageCaptureIntent;
2010 }
2011
Doris Liuc7fe2c52013-02-26 10:54:25 -08002012 @Override
2013 public boolean needsPieMenu() {
2014 return true;
2015 }
2016
Michael Kolb25668cd2013-01-29 10:33:22 -08002017 @Override
2018 public void onShowSwitcherPopup() {
Michael Kolbb28942d2013-03-08 20:43:01 -08002019 mUI.onShowSwitcherPopup();
Michael Kolb25668cd2013-01-29 10:33:22 -08002020 }
2021
Angus Kongc0993982013-01-29 17:43:48 -08002022 @Override
Michael Kolbb28942d2013-03-08 20:43:01 -08002023 public int onZoomChanged(int index) {
2024 // Not useful to change zoom value when the activity is paused.
2025 if (mPaused) return index;
2026 mZoomValue = index;
2027 if (mParameters == null || mCameraDevice == null) return index;
2028 // Set zoom parameters asynchronously
2029 mParameters.setZoom(mZoomValue);
Angus Kongf7771f92013-04-15 12:11:15 -07002030 mCameraDevice.setParameters(mParameters);
Michael Kolbb28942d2013-03-08 20:43:01 -08002031 Parameters p = mCameraDevice.getParameters();
2032 if (p != null) return p.getZoom();
2033 return index;
Angus Kongc0993982013-01-29 17:43:48 -08002034 }
2035
2036 @Override
Michael Kolbb28942d2013-03-08 20:43:01 -08002037 public int getCameraState() {
2038 return mCameraState;
2039 }
2040
2041 @Override
2042 public void onQueueStatus(boolean full) {
2043 mUI.enableShutter(!full);
Angus Kongc0993982013-01-29 17:43:48 -08002044 }
Angus Kong52fe0022013-01-31 18:22:44 -08002045
2046 @Override
2047 public void onMediaSaveServiceConnected(MediaSaveService s) {
2048 // We set the listener only when both service and shutterbutton
2049 // are initialized.
Michael Kolbb28942d2013-03-08 20:43:01 -08002050 if (mFirstTimeInitialized) {
2051 s.setListener(this);
2052 }
Angus Kong52fe0022013-01-31 18:22:44 -08002053 }
Angus Kongdd2e5112013-03-26 11:40:40 -07002054
2055 @Override
2056 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2057 }
2058
2059 @Override
2060 public void onSensorChanged(SensorEvent event) {
2061 int type = event.sensor.getType();
2062 float[] data;
2063 if (type == Sensor.TYPE_ACCELEROMETER) {
2064 data = mGData;
2065 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2066 data = mMData;
2067 } else {
2068 // we should not be here.
2069 return;
2070 }
2071 for (int i = 0; i < 3 ; i++) {
2072 data[i] = event.values[i];
2073 }
2074 float[] orientation = new float[3];
2075 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2076 SensorManager.getOrientation(mR, orientation);
2077 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2078 if (mHeading < 0) {
2079 mHeading += 360;
2080 }
Angus Kongdd2e5112013-03-26 11:40:40 -07002081 }
Michael Kolb25668cd2013-01-29 10:33:22 -08002082}