blob: 8d68320bf8ac6f7cdf3e6d07c7120897ac1d9560 [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
Sascha Haeberling8e963a52013-08-06 11:43:02 -070019import java.io.File;
20import java.io.FileNotFoundException;
21import java.io.FileOutputStream;
22import java.io.IOException;
23import java.io.OutputStream;
24import java.util.ArrayList;
25import java.util.Formatter;
26import java.util.List;
27
Michael Kolb8872c232013-01-29 10:33:22 -080028import android.annotation.TargetApi;
29import android.app.Activity;
Michael Kolb8872c232013-01-29 10:33:22 -080030import android.content.ContentProviderClient;
31import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070032import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080033import android.content.Intent;
34import android.content.SharedPreferences.Editor;
35import android.content.res.Configuration;
36import android.graphics.Bitmap;
37import android.graphics.SurfaceTexture;
38import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080039import android.hardware.Camera.Parameters;
Michael Kolb8872c232013-01-29 10:33:22 -080040import android.hardware.Camera.Size;
Angus Kong0d00a892013-03-26 11:40:40 -070041import android.hardware.Sensor;
42import android.hardware.SensorEvent;
43import android.hardware.SensorEventListener;
44import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080045import android.location.Location;
46import android.media.CameraProfile;
47import android.net.Uri;
48import android.os.Bundle;
49import android.os.ConditionVariable;
50import android.os.Handler;
51import android.os.Looper;
52import android.os.Message;
53import android.os.MessageQueue;
54import android.os.SystemClock;
55import android.provider.MediaStore;
56import android.util.Log;
Michael Kolb8872c232013-01-29 10:33:22 -080057import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080058import android.view.OrientationEventListener;
59import android.view.SurfaceHolder;
60import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080061import android.view.WindowManager;
Michael Kolb8872c232013-01-29 10:33:22 -080062
Angus Kong9ef99252013-07-18 18:04:19 -070063import com.android.camera.CameraManager.CameraAFCallback;
64import com.android.camera.CameraManager.CameraAFMoveCallback;
65import com.android.camera.CameraManager.CameraPictureCallback;
Michael Kolb8872c232013-01-29 10:33:22 -080066import com.android.camera.CameraManager.CameraProxy;
Angus Kong9ef99252013-07-18 18:04:19 -070067import com.android.camera.CameraManager.CameraShutterCallback;
Angus Kongb50b5cb2013-08-09 14:55:20 -070068import com.android.camera.util.ApiHelper;
Michael Kolbd6954f32013-03-08 20:43:01 -080069import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
Michael Kolb8872c232013-01-29 10:33:22 -080070import com.android.camera.ui.PopupManager;
Michael Kolb8872c232013-01-29 10:33:22 -080071import com.android.camera.ui.RotateTextToast;
Angus Kongb50b5cb2013-08-09 14:55:20 -070072import com.android.camera.util.CameraUtil;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070073import com.android.camera.util.UsageStatistics;
74import com.android.camera2.R;
Angus Kong0d00a892013-03-26 11:40:40 -070075import com.android.gallery3d.exif.ExifInterface;
76import com.android.gallery3d.exif.ExifTag;
77import com.android.gallery3d.exif.Rational;
Michael Kolb8872c232013-01-29 10:33:22 -080078
79public class PhotoModule
80 implements CameraModule,
Michael Kolbd6954f32013-03-08 20:43:01 -080081 PhotoController,
Michael Kolb8872c232013-01-29 10:33:22 -080082 FocusOverlayManager.Listener,
83 CameraPreference.OnPreferenceChangedListener,
Michael Kolb8872c232013-01-29 10:33:22 -080084 ShutterButton.OnShutterButtonListener,
Michael Kolbd6954f32013-03-08 20:43:01 -080085 MediaSaveService.Listener,
Angus Kong0d00a892013-03-26 11:40:40 -070086 OnCountDownFinishedListener,
87 SensorEventListener {
Michael Kolb8872c232013-01-29 10:33:22 -080088
89 private static final String TAG = "CAM_PhotoModule";
90
91 // We number the request code from 1000 to avoid collision with Gallery.
92 private static final int REQUEST_CROP = 1000;
93
94 private static final int SETUP_PREVIEW = 1;
95 private static final int FIRST_TIME_INIT = 2;
96 private static final int CLEAR_SCREEN_DELAY = 3;
97 private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
98 private static final int CHECK_DISPLAY_ROTATION = 5;
99 private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
100 private static final int SWITCH_CAMERA = 7;
101 private static final int SWITCH_CAMERA_START_ANIMATION = 8;
102 private static final int CAMERA_OPEN_DONE = 9;
103 private static final int START_PREVIEW_DONE = 10;
104 private static final int OPEN_CAMERA_FAIL = 11;
105 private static final int CAMERA_DISABLED = 12;
Michael Kolb8872c232013-01-29 10:33:22 -0800106
107 // The subset of parameters we need to update in setCameraParameters().
108 private static final int UPDATE_PARAM_INITIALIZE = 1;
109 private static final int UPDATE_PARAM_ZOOM = 2;
110 private static final int UPDATE_PARAM_PREFERENCE = 4;
111 private static final int UPDATE_PARAM_ALL = -1;
112
113 // This is the timeout to keep the camera in onPause for the first time
114 // after screen on if the activity is started from secure lock screen.
115 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
116
117 // copied from Camera hierarchy
118 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800119 private CameraProxy mCameraDevice;
120 private int mCameraId;
121 private Parameters mParameters;
122 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800123
124 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800125
Michael Kolb8872c232013-01-29 10:33:22 -0800126 // The activity is going to switch to the specified camera id. This is
127 // needed because texture copy is done in GL thread. -1 means camera is not
128 // switching.
129 protected int mPendingSwitchCameraId = -1;
130 private boolean mOpenCameraFail;
131 private boolean mCameraDisabled;
132
133 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
134 // needed to be updated in mUpdateSet.
135 private int mUpdateSet;
136
137 private static final int SCREEN_DELAY = 2 * 60 * 1000;
138
139 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800140
141 private Parameters mInitialParams;
142 private boolean mFocusAreaSupported;
143 private boolean mMeteringAreaSupported;
144 private boolean mAeLockSupported;
145 private boolean mAwbLockSupported;
146 private boolean mContinousFocusSupported;
147
148 // The degrees of the device rotated clockwise from its natural orientation.
149 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
150 private ComboPreferences mPreferences;
151
152 private static final String sTempCropFilename = "crop-temp";
153
154 private ContentProviderClient mMediaProviderClient;
Michael Kolb8872c232013-01-29 10:33:22 -0800155 private boolean mFaceDetectionStarted = false;
156
Michael Kolb8872c232013-01-29 10:33:22 -0800157 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
158 private String mCropValue;
159 private Uri mSaveUri;
160
Angus Kongce5480e2013-01-29 17:43:48 -0800161 // We use a queue to generated names of the images to be used later
162 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800163 private NamedImages mNamedImages;
164
165 private Runnable mDoSnapRunnable = new Runnable() {
166 @Override
167 public void run() {
168 onShutterButtonClick();
169 }
170 };
171
172 private final StringBuilder mBuilder = new StringBuilder();
173 private final Formatter mFormatter = new Formatter(mBuilder);
174 private final Object[] mFormatterArgs = new Object[1];
175
176 /**
177 * An unpublished intent flag requesting to return as soon as capturing
178 * is completed.
179 *
180 * TODO: consider publishing by moving into MediaStore.
181 */
182 private static final String EXTRA_QUICK_CAPTURE =
183 "android.intent.extra.quickCapture";
184
185 // The display rotation in degrees. This is only valid when mCameraState is
186 // not PREVIEW_STOPPED.
187 private int mDisplayRotation;
188 // The value for android.hardware.Camera.setDisplayOrientation.
189 private int mCameraDisplayOrientation;
190 // The value for UI components like indicators.
191 private int mDisplayOrientation;
192 // The value for android.hardware.Camera.Parameters.setRotation.
193 private int mJpegRotation;
194 private boolean mFirstTimeInitialized;
195 private boolean mIsImageCaptureIntent;
196
Michael Kolb8872c232013-01-29 10:33:22 -0800197 private int mCameraState = PREVIEW_STOPPED;
198 private boolean mSnapshotOnIdle = false;
199
200 private ContentResolver mContentResolver;
201
202 private LocationManager mLocationManager;
203
Michael Kolb8872c232013-01-29 10:33:22 -0800204 private final PostViewPictureCallback mPostViewPictureCallback =
205 new PostViewPictureCallback();
206 private final RawPictureCallback mRawPictureCallback =
207 new RawPictureCallback();
208 private final AutoFocusCallback mAutoFocusCallback =
209 new AutoFocusCallback();
210 private final Object mAutoFocusMoveCallback =
211 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
212 ? new AutoFocusMoveCallback()
213 : null;
214
215 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
216
217 private long mFocusStartTime;
218 private long mShutterCallbackTime;
219 private long mPostViewPictureCallbackTime;
220 private long mRawPictureCallbackTime;
221 private long mJpegPictureCallbackTime;
222 private long mOnResumeTime;
223 private byte[] mJpegImageData;
224
225 // These latency time are for the CameraLatency test.
226 public long mAutoFocusTime;
227 public long mShutterLag;
228 public long mShutterToPictureDisplayedTime;
229 public long mPictureDisplayedToJpegCallbackTime;
230 public long mJpegCallbackFinishTime;
231 public long mCaptureStartTime;
232
233 // This handles everything about focus.
234 private FocusOverlayManager mFocusManager;
235
Michael Kolb8872c232013-01-29 10:33:22 -0800236 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800237
238 private final Handler mHandler = new MainHandler();
239 private PreferenceGroup mPreferenceGroup;
240
241 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700242 private SensorManager mSensorManager;
243 private float[] mGData = new float[3];
244 private float[] mMData = new float[3];
245 private float[] mR = new float[16];
246 private int mHeading = -1;
247
Doris Liu6432cd62013-06-13 17:20:31 -0700248 CameraStartUpThread mCameraStartUpThread;
249 ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
250
Angus Kongce5480e2013-01-29 17:43:48 -0800251 private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
252 new MediaSaveService.OnMediaSavedListener() {
253 @Override
254 public void onMediaSaved(Uri uri) {
255 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700256 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800257 }
258 }
259 };
Michael Kolb8872c232013-01-29 10:33:22 -0800260
261 // The purpose is not to block the main thread in onCreate and onResume.
262 private class CameraStartUpThread extends Thread {
263 private volatile boolean mCancelled;
264
265 public void cancel() {
266 mCancelled = true;
267 interrupt();
268 }
269
270 public boolean isCanceled() {
271 return mCancelled;
272 }
273
274 @Override
275 public void run() {
276 try {
277 // We need to check whether the activity is paused before long
278 // operations to ensure that onPause() can be done ASAP.
279 if (mCancelled) return;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700280 mCameraDevice = CameraUtil.openCamera(mActivity, mCameraId);
Michael Kolb8872c232013-01-29 10:33:22 -0800281 mParameters = mCameraDevice.getParameters();
282 // Wait until all the initialization needed by startPreview are
283 // done.
284 mStartPreviewPrerequisiteReady.block();
285
286 initializeCapabilities();
287 if (mFocusManager == null) initializeFocusManager();
288 if (mCancelled) return;
289 setCameraParameters(UPDATE_PARAM_ALL);
290 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
291 if (mCancelled) return;
292 startPreview();
293 mHandler.sendEmptyMessage(START_PREVIEW_DONE);
294 mOnResumeTime = SystemClock.uptimeMillis();
295 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
296 } catch (CameraHardwareException e) {
297 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
298 } catch (CameraDisabledException e) {
299 mHandler.sendEmptyMessage(CAMERA_DISABLED);
300 }
301 }
302 }
303
304 /**
305 * This Handler is used to post message back onto the main thread of the
306 * application
307 */
308 private class MainHandler extends Handler {
309 @Override
310 public void handleMessage(Message msg) {
311 switch (msg.what) {
312 case SETUP_PREVIEW: {
313 setupPreview();
314 break;
315 }
316
317 case CLEAR_SCREEN_DELAY: {
318 mActivity.getWindow().clearFlags(
319 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
320 break;
321 }
322
323 case FIRST_TIME_INIT: {
324 initializeFirstTime();
325 break;
326 }
327
328 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
329 setCameraParametersWhenIdle(0);
330 break;
331 }
332
333 case CHECK_DISPLAY_ROTATION: {
334 // Set the display orientation if display rotation has changed.
335 // Sometimes this happens when the device is held upside
336 // down and camera app is opened. Rotation animation will
337 // take some time and the rotation value we have got may be
338 // wrong. Framework does not have a callback for this now.
Angus Kongb50b5cb2013-08-09 14:55:20 -0700339 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
Michael Kolb8872c232013-01-29 10:33:22 -0800340 setDisplayOrientation();
341 }
342 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
343 mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
344 }
345 break;
346 }
347
348 case SHOW_TAP_TO_FOCUS_TOAST: {
349 showTapToFocusToast();
350 break;
351 }
352
353 case SWITCH_CAMERA: {
354 switchCamera();
355 break;
356 }
357
358 case SWITCH_CAMERA_START_ANIMATION: {
Doris Liu6432cd62013-06-13 17:20:31 -0700359 // TODO: Need to revisit
360 // ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -0800361 break;
362 }
363
364 case CAMERA_OPEN_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800365 onCameraOpened();
Michael Kolb8872c232013-01-29 10:33:22 -0800366 break;
367 }
368
369 case START_PREVIEW_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800370 onPreviewStarted();
Michael Kolb8872c232013-01-29 10:33:22 -0800371 break;
372 }
373
374 case OPEN_CAMERA_FAIL: {
375 mCameraStartUpThread = null;
376 mOpenCameraFail = true;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700377 CameraUtil.showErrorAndFinish(mActivity,
Michael Kolb8872c232013-01-29 10:33:22 -0800378 R.string.cannot_connect_camera);
379 break;
380 }
381
382 case CAMERA_DISABLED: {
383 mCameraStartUpThread = null;
384 mCameraDisabled = true;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700385 CameraUtil.showErrorAndFinish(mActivity,
Michael Kolb8872c232013-01-29 10:33:22 -0800386 R.string.camera_disabled);
387 break;
388 }
Michael Kolb8872c232013-01-29 10:33:22 -0800389 }
390 }
391 }
392
393 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700394 public void init(CameraActivity activity, View parent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800395 mActivity = activity;
Michael Kolbd6954f32013-03-08 20:43:01 -0800396 mUI = new PhotoUI(activity, this, parent);
Michael Kolb8872c232013-01-29 10:33:22 -0800397 mPreferences = new ComboPreferences(mActivity);
398 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
399 mCameraId = getPreferredCameraId(mPreferences);
400
401 mContentResolver = mActivity.getContentResolver();
402
403 // To reduce startup time, open the camera and start the preview in
404 // another thread.
405 mCameraStartUpThread = new CameraStartUpThread();
406 mCameraStartUpThread.start();
407
Michael Kolb8872c232013-01-29 10:33:22 -0800408 // Surface texture is from camera screen nail and startPreview needs it.
409 // This must be done before startPreview.
410 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800411
412 mPreferences.setLocalId(mActivity, mCameraId);
413 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
414 // we need to reset exposure for the preview
415 resetExposureCompensation();
416 // Starting the preview needs preferences, camera screen nail, and
417 // focus area indicator.
418 mStartPreviewPrerequisiteReady.open();
419
420 initializeControlByIntent();
421 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Michael Kolbd6954f32013-03-08 20:43:01 -0800422 mLocationManager = new LocationManager(mActivity, mUI);
Angus Kong0d00a892013-03-26 11:40:40 -0700423 mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
Michael Kolbd6954f32013-03-08 20:43:01 -0800424 }
425
426 private void initializeControlByIntent() {
427 mUI.initializeControlByIntent();
428 if (mIsImageCaptureIntent) {
429 setupCaptureParams();
430 }
431 }
432
433 private void onPreviewStarted() {
434 mCameraStartUpThread = null;
435 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800436 startFaceDetection();
437 locationFirstRun();
Michael Kolb8872c232013-01-29 10:33:22 -0800438 }
439
440 // Prompt the user to pick to record location for the very first run of
441 // camera only
442 private void locationFirstRun() {
443 if (RecordLocationPreference.isSet(mPreferences)) {
444 return;
445 }
446 if (mActivity.isSecureCamera()) return;
447 // Check if the back camera exists
448 int backCameraId = CameraHolder.instance().getBackCameraId();
449 if (backCameraId == -1) {
450 // If there is no back camera, do not show the prompt.
451 return;
452 }
Doris Liu6a83d522013-07-02 12:03:32 -0700453 mUI.showLocationDialog();
454 }
Michael Kolb8872c232013-01-29 10:33:22 -0800455
Doris Liu6a83d522013-07-02 12:03:32 -0700456 public void enableRecordingLocation(boolean enable) {
457 setLocationPreference(enable ? RecordLocationPreference.VALUE_ON
458 : RecordLocationPreference.VALUE_OFF);
Michael Kolb8872c232013-01-29 10:33:22 -0800459 }
460
461 private void setLocationPreference(String value) {
462 mPreferences.edit()
463 .putString(CameraSettings.KEY_RECORD_LOCATION, value)
464 .apply();
465 // TODO: Fix this to use the actual onSharedPreferencesChanged listener
466 // instead of invoking manually
467 onSharedPreferenceChanged();
468 }
469
Michael Kolbd6954f32013-03-08 20:43:01 -0800470 private void onCameraOpened() {
471 View root = mUI.getRootView();
Michael Kolb8872c232013-01-29 10:33:22 -0800472 // These depend on camera parameters.
Michael Kolbd6954f32013-03-08 20:43:01 -0800473
474 int width = root.getWidth();
475 int height = root.getHeight();
Doris Liu6a0de792013-02-26 10:54:25 -0800476 mFocusManager.setPreviewSize(width, height);
Michael Kolbd6954f32013-03-08 20:43:01 -0800477 openCameraCommon();
Michael Kolb8872c232013-01-29 10:33:22 -0800478 }
479
Michael Kolbd6954f32013-03-08 20:43:01 -0800480 private void switchCamera() {
481 if (mPaused) return;
482
483 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
484 mCameraId = mPendingSwitchCameraId;
485 mPendingSwitchCameraId = -1;
486 setCameraId(mCameraId);
487
488 // from onPause
489 closeCamera();
490 mUI.collapseCameraControls();
491 mUI.clearFaces();
492 if (mFocusManager != null) mFocusManager.removeMessages();
493
494 // Restart the camera and initialize the UI. From onCreate.
495 mPreferences.setLocalId(mActivity, mCameraId);
496 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
497 try {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700498 mCameraDevice = CameraUtil.openCamera(mActivity, mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800499 mParameters = mCameraDevice.getParameters();
500 } catch (CameraHardwareException e) {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700501 CameraUtil.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
Michael Kolbd6954f32013-03-08 20:43:01 -0800502 return;
503 } catch (CameraDisabledException e) {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700504 CameraUtil.showErrorAndFinish(mActivity, R.string.camera_disabled);
Michael Kolbd6954f32013-03-08 20:43:01 -0800505 return;
Doris Liu09106a42013-03-05 09:54:25 -0800506 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800507 initializeCapabilities();
508 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
509 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
510 mFocusManager.setMirror(mirror);
511 mFocusManager.setParameters(mInitialParams);
512 setupPreview();
513
Doris Liu6432cd62013-06-13 17:20:31 -0700514 // reset zoom value index
515 mZoomValue = 0;
Michael Kolbd6954f32013-03-08 20:43:01 -0800516 openCameraCommon();
517
518 if (ApiHelper.HAS_SURFACE_TEXTURE) {
519 // Start switch camera animation. Post a message because
520 // onFrameAvailable from the old camera may already exist.
521 mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
Doris Liu48239f42013-03-04 22:19:10 -0800522 }
523 }
524
Michael Kolbd6954f32013-03-08 20:43:01 -0800525 protected void setCameraId(int cameraId) {
526 ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
527 pref.setValue("" + cameraId);
528 }
529
530 // either open a new camera or switch cameras
531 private void openCameraCommon() {
Michael Kolb8872c232013-01-29 10:33:22 -0800532 loadCameraPreferences();
Michael Kolbd6954f32013-03-08 20:43:01 -0800533
534 mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
535 updateSceneMode();
536 showTapToFocusToastIfNeeded();
537
538
Michael Kolb8872c232013-01-29 10:33:22 -0800539 }
540
Michael Kolbd6954f32013-03-08 20:43:01 -0800541 public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800542 if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
Michael Kolbd6954f32013-03-08 20:43:01 -0800543 }
Michael Kolb8872c232013-01-29 10:33:22 -0800544
545 private void resetExposureCompensation() {
546 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
547 CameraSettings.EXPOSURE_DEFAULT_VALUE);
548 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
549 Editor editor = mPreferences.edit();
550 editor.putString(CameraSettings.KEY_EXPOSURE, "0");
551 editor.apply();
552 }
553 }
554
555 private void keepMediaProviderInstance() {
556 // We want to keep a reference to MediaProvider in camera's lifecycle.
557 // TODO: Utilize mMediaProviderClient instance to replace
558 // ContentResolver calls.
559 if (mMediaProviderClient == null) {
560 mMediaProviderClient = mContentResolver
561 .acquireContentProviderClient(MediaStore.AUTHORITY);
562 }
563 }
564
565 // Snapshots can only be taken after this is called. It should be called
566 // once only. We could have done these things in onCreate() but we want to
567 // make preview screen appear as soon as possible.
568 private void initializeFirstTime() {
569 if (mFirstTimeInitialized) return;
570
571 // Initialize location service.
572 boolean recordLocation = RecordLocationPreference.get(
573 mPreferences, mContentResolver);
574 mLocationManager.recordLocation(recordLocation);
575
576 keepMediaProviderInstance();
577
Michael Kolbd6954f32013-03-08 20:43:01 -0800578 mUI.initializeFirstTime();
Angus Kong86d36312013-01-31 18:22:44 -0800579 MediaSaveService s = mActivity.getMediaSaveService();
580 // We set the listener only when both service and shutterbutton
581 // are initialized.
582 if (s != null) {
583 s.setListener(this);
584 }
Michael Kolb8872c232013-01-29 10:33:22 -0800585
Michael Kolb8872c232013-01-29 10:33:22 -0800586 mNamedImages = new NamedImages();
587
588 mFirstTimeInitialized = true;
589 addIdleHandler();
590
591 mActivity.updateStorageSpaceAndHint();
592 }
593
Michael Kolbd6954f32013-03-08 20:43:01 -0800594 // If the activity is paused and resumed, this method will be called in
595 // onResume.
596 private void initializeSecondTime() {
597 // Start location update if needed.
598 boolean recordLocation = RecordLocationPreference.get(
599 mPreferences, mContentResolver);
600 mLocationManager.recordLocation(recordLocation);
601 MediaSaveService s = mActivity.getMediaSaveService();
602 if (s != null) {
603 s.setListener(this);
604 }
605 mNamedImages = new NamedImages();
606 mUI.initializeSecondTime(mParameters);
607 keepMediaProviderInstance();
608 }
609
610 @Override
611 public void onSurfaceCreated(SurfaceHolder holder) {
612 // Do not access the camera if camera start up thread is not finished.
613 if (mCameraDevice == null || mCameraStartUpThread != null)
614 return;
615
Angus Kong9ef99252013-07-18 18:04:19 -0700616 mCameraDevice.setPreviewDisplay(holder);
Michael Kolbd6954f32013-03-08 20:43:01 -0800617 // This happens when onConfigurationChanged arrives, surface has been
618 // destroyed, and there is no onFullScreenChanged.
619 if (mCameraState == PREVIEW_STOPPED) {
620 setupPreview();
621 }
622 }
623
Michael Kolb8872c232013-01-29 10:33:22 -0800624 private void showTapToFocusToastIfNeeded() {
625 // Show the tap to focus toast if this is the first start.
626 if (mFocusAreaSupported &&
627 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
628 // Delay the toast for one second to wait for orientation.
629 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
630 }
631 }
632
633 private void addIdleHandler() {
634 MessageQueue queue = Looper.myQueue();
635 queue.addIdleHandler(new MessageQueue.IdleHandler() {
636 @Override
637 public boolean queueIdle() {
638 Storage.ensureOSXCompatible();
639 return false;
640 }
641 });
642 }
643
Michael Kolb8872c232013-01-29 10:33:22 -0800644 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
645 @Override
646 public void startFaceDetection() {
647 if (!ApiHelper.HAS_FACE_DETECTION) return;
648 if (mFaceDetectionStarted) return;
649 if (mParameters.getMaxNumDetectedFaces() > 0) {
650 mFaceDetectionStarted = true;
Michael Kolb8872c232013-01-29 10:33:22 -0800651 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Michael Kolbd6954f32013-03-08 20:43:01 -0800652 mUI.onStartFaceDetection(mDisplayOrientation,
653 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
Angus Kong9e765522013-07-31 14:05:20 -0700654 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800655 mCameraDevice.startFaceDetection();
656 }
657 }
658
659 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
660 @Override
661 public void stopFaceDetection() {
662 if (!ApiHelper.HAS_FACE_DETECTION) return;
663 if (!mFaceDetectionStarted) return;
664 if (mParameters.getMaxNumDetectedFaces() > 0) {
665 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700666 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800667 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800668 mUI.clearFaces();
Michael Kolb8872c232013-01-29 10:33:22 -0800669 }
670 }
671
Michael Kolb8872c232013-01-29 10:33:22 -0800672 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700673 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700674
Sascha Haeberling37f36112013-08-06 14:31:52 -0700675 private boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700676
Sascha Haeberling37f36112013-08-06 14:31:52 -0700677 public ShutterCallback(boolean needsAnimation) {
678 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700679 }
680
Michael Kolb8872c232013-01-29 10:33:22 -0800681 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700682 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800683 mShutterCallbackTime = System.currentTimeMillis();
684 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
685 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700686 if (mNeedsAnimation) {
687 mActivity.runOnUiThread(new Runnable() {
688 @Override
689 public void run() {
690 animateAfterShutter();
691 }
692 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700693 }
Michael Kolb8872c232013-01-29 10:33:22 -0800694 }
695 }
696
Angus Kong9ef99252013-07-18 18:04:19 -0700697 private final class PostViewPictureCallback
698 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800699 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700700 public void onPictureTaken(byte [] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800701 mPostViewPictureCallbackTime = System.currentTimeMillis();
702 Log.v(TAG, "mShutterToPostViewCallbackTime = "
703 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
704 + "ms");
705 }
706 }
707
Angus Kong9ef99252013-07-18 18:04:19 -0700708 private final class RawPictureCallback
709 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800710 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700711 public void onPictureTaken(byte [] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800712 mRawPictureCallbackTime = System.currentTimeMillis();
713 Log.v(TAG, "mShutterToRawCallbackTime = "
714 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
715 }
716 }
717
Angus Kong9ef99252013-07-18 18:04:19 -0700718 private final class JpegPictureCallback
719 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800720 Location mLocation;
721
722 public JpegPictureCallback(Location loc) {
723 mLocation = loc;
724 }
725
726 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700727 public void onPictureTaken(final byte [] jpegData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800728 if (mPaused) {
729 return;
730 }
Doris Liu6432cd62013-06-13 17:20:31 -0700731 if (mIsImageCaptureIntent) {
732 stopPreview();
Sascha Haeberling37f36112013-08-06 14:31:52 -0700733 } else {
734 // Animate capture with real jpeg data instead of a preview frame.
735 mUI.animateCapture(jpegData);
Doris Liu6432cd62013-06-13 17:20:31 -0700736 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700737 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700738 mUI.showSwitcher();
739 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800740 }
741
742 mJpegPictureCallbackTime = System.currentTimeMillis();
743 // If postview callback has arrived, the captured image is displayed
744 // in postview callback. If not, the captured image is displayed in
745 // raw picture callback.
746 if (mPostViewPictureCallbackTime != 0) {
747 mShutterToPictureDisplayedTime =
748 mPostViewPictureCallbackTime - mShutterCallbackTime;
749 mPictureDisplayedToJpegCallbackTime =
750 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
751 } else {
752 mShutterToPictureDisplayedTime =
753 mRawPictureCallbackTime - mShutterCallbackTime;
754 mPictureDisplayedToJpegCallbackTime =
755 mJpegPictureCallbackTime - mRawPictureCallbackTime;
756 }
757 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
758 + mPictureDisplayedToJpegCallbackTime + "ms");
759
Michael Kolb8872c232013-01-29 10:33:22 -0800760 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
761 if (!mIsImageCaptureIntent) {
762 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
763 setupPreview();
764 } else {
765 // Camera HAL of some devices have a bug. Starting preview
766 // immediately after taking a picture will fail. Wait some
767 // time before starting the preview.
768 mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
769 }
770 }
771
772 if (!mIsImageCaptureIntent) {
773 // Calculate the width and the height of the jpeg.
774 Size s = mParameters.getPictureSize();
Angus Kong0d00a892013-03-26 11:40:40 -0700775 ExifInterface exif = Exif.getExif(jpegData);
776 int orientation = Exif.getOrientation(exif);
Michael Kolb8872c232013-01-29 10:33:22 -0800777 int width, height;
778 if ((mJpegRotation + orientation) % 180 == 0) {
779 width = s.width;
780 height = s.height;
781 } else {
782 width = s.height;
783 height = s.width;
784 }
785 String title = mNamedImages.getTitle();
786 long date = mNamedImages.getDate();
787 if (title == null) {
788 Log.e(TAG, "Unbalanced name/data pair");
789 } else {
790 if (date == -1) date = mCaptureStartTime;
Angus Kong0d00a892013-03-26 11:40:40 -0700791 if (mHeading >= 0) {
792 // heading direction has been updated by the sensor.
793 ExifTag directionRefTag = exif.buildTag(
794 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
795 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
796 ExifTag directionTag = exif.buildTag(
797 ExifInterface.TAG_GPS_IMG_DIRECTION,
798 new Rational(mHeading, 1));
799 exif.setTag(directionRefTag);
800 exif.setTag(directionTag);
801 }
Angus Kong86d36312013-01-31 18:22:44 -0800802 mActivity.getMediaSaveService().addImage(
803 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700804 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800805 }
806 } else {
807 mJpegImageData = jpegData;
808 if (!mQuickCapture) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800809 mUI.showPostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -0800810 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800811 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800812 }
813 }
814
815 // Check this in advance of each shot so we don't add to shutter
816 // latency. It's true that someone else could write to the SD card in
817 // the mean time and fill it, but that could have happened between the
818 // shutter press and saving the JPEG too.
819 mActivity.updateStorageSpaceAndHint();
820
821 long now = System.currentTimeMillis();
822 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
823 Log.v(TAG, "mJpegCallbackFinishTime = "
824 + mJpegCallbackFinishTime + "ms");
825 mJpegPictureCallbackTime = 0;
826 }
827 }
828
Angus Kong9ef99252013-07-18 18:04:19 -0700829 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800830 @Override
831 public void onAutoFocus(
Angus Kong9ef99252013-07-18 18:04:19 -0700832 boolean focused, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800833 if (mPaused) return;
834
835 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
836 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
837 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800838 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb8872c232013-01-29 10:33:22 -0800839 }
840 }
841
842 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
843 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700844 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800845 @Override
846 public void onAutoFocusMoving(
Angus Kong9ef99252013-07-18 18:04:19 -0700847 boolean moving, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800848 mFocusManager.onAutoFocusMoving(moving);
849 }
850 }
851
852 private static class NamedImages {
853 private ArrayList<NamedEntity> mQueue;
854 private boolean mStop;
855 private NamedEntity mNamedEntity;
856
857 public NamedImages() {
858 mQueue = new ArrayList<NamedEntity>();
859 }
860
861 public void nameNewImage(ContentResolver resolver, long date) {
862 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700863 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -0800864 r.date = date;
865 mQueue.add(r);
866 }
867
868 public String getTitle() {
869 if (mQueue.isEmpty()) {
870 mNamedEntity = null;
871 return null;
872 }
873 mNamedEntity = mQueue.get(0);
874 mQueue.remove(0);
875
876 return mNamedEntity.title;
877 }
878
879 // Must be called after getTitle().
880 public long getDate() {
881 if (mNamedEntity == null) return -1;
882 return mNamedEntity.date;
883 }
884
885 private static class NamedEntity {
886 String title;
887 long date;
888 }
889 }
890
891 private void setCameraState(int state) {
892 mCameraState = state;
893 switch (state) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800894 case PhotoController.PREVIEW_STOPPED:
895 case PhotoController.SNAPSHOT_IN_PROGRESS:
Michael Kolbd6954f32013-03-08 20:43:01 -0800896 case PhotoController.SWITCHING_CAMERA:
897 mUI.enableGestures(false);
898 break;
899 case PhotoController.IDLE:
Doris Liu6432cd62013-06-13 17:20:31 -0700900 mUI.enableGestures(true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800901 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800902 }
903 }
904
Sascha Haeberling37f36112013-08-06 14:31:52 -0700905 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -0800906 // Only animate when in full screen capture mode
907 // i.e. If monkey/a user swipes to the gallery during picture taking,
908 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -0700909 if (!mIsImageCaptureIntent) {
910 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -0700911 }
Michael Kolb8872c232013-01-29 10:33:22 -0800912 }
913
914 @Override
915 public boolean capture() {
916 // If we are already in the middle of taking a snapshot or the image save request
917 // is full then ignore.
918 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Angus Kong86d36312013-01-31 18:22:44 -0800919 || mCameraState == SWITCHING_CAMERA
920 || mActivity.getMediaSaveService().isQueueFull()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800921 return false;
922 }
923 mCaptureStartTime = System.currentTimeMillis();
924 mPostViewPictureCallbackTime = 0;
925 mJpegImageData = null;
926
Angus Kongb50b5cb2013-08-09 14:55:20 -0700927 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -0800928
929 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700930 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -0800931 }
932
933 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800934 int orientation;
935 // We need to be consistent with the framework orientation (i.e. the
936 // orientation of the UI.) when the auto-rotate screen setting is on.
937 if (mActivity.isAutoRotateScreen()) {
938 orientation = (360 - mDisplayRotation) % 360;
939 } else {
940 orientation = mOrientation;
941 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700942 mJpegRotation = CameraUtil.getJpegRotation(mCameraId, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800943 mParameters.setRotation(mJpegRotation);
944 Location loc = mLocationManager.getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700945 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -0800946 mCameraDevice.setParameters(mParameters);
947
Angus Kong9ef99252013-07-18 18:04:19 -0700948 mCameraDevice.takePicture(mHandler,
949 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -0700950 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -0700951 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -0800952
953 mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime);
954
955 mFaceDetectionStarted = false;
956 setCameraState(SNAPSHOT_IN_PROGRESS);
Bobby Georgescu301b6462013-04-01 15:33:17 -0700957 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
958 UsageStatistics.ACTION_CAPTURE_DONE, "Photo");
Michael Kolb8872c232013-01-29 10:33:22 -0800959 return true;
960 }
961
962 @Override
963 public void setFocusParameters() {
964 setCameraParameters(UPDATE_PARAM_PREFERENCE);
965 }
966
967 private int getPreferredCameraId(ComboPreferences preferences) {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700968 int intentCameraId = CameraUtil.getCameraFacingIntentExtras(mActivity);
Michael Kolb8872c232013-01-29 10:33:22 -0800969 if (intentCameraId != -1) {
970 // Testing purpose. Launch a specific camera through the intent
971 // extras.
972 return intentCameraId;
973 } else {
974 return CameraSettings.readPreferredCameraId(preferences);
975 }
976 }
977
Michael Kolbd6954f32013-03-08 20:43:01 -0800978 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -0800979 // If scene mode is set, we cannot set flash mode, white balance, and
980 // focus mode, instead, we read it from driver
981 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
982 overrideCameraSettings(mParameters.getFlashMode(),
983 mParameters.getWhiteBalance(), mParameters.getFocusMode());
984 } else {
985 overrideCameraSettings(null, null, null);
986 }
987 }
988
989 private void overrideCameraSettings(final String flashMode,
990 final String whiteBalance, final String focusMode) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800991 mUI.overrideSettings(
992 CameraSettings.KEY_FLASH_MODE, flashMode,
993 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
994 CameraSettings.KEY_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -0800995 }
996
997 private void loadCameraPreferences() {
998 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
999 mCameraId, CameraHolder.instance().getCameraInfo());
1000 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
1001 }
1002
1003 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001004 public void onOrientationChanged(int orientation) {
1005 // We keep the last known orientation. So if the user first orient
1006 // the camera then point the camera to floor or sky, we still have
1007 // the correct orientation.
1008 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
Angus Kongb50b5cb2013-08-09 14:55:20 -07001009 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001010
1011 // Show the toast after getting the first orientation changed.
1012 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1013 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1014 showTapToFocusToast();
1015 }
1016 }
1017
1018 @Override
1019 public void onStop() {
1020 if (mMediaProviderClient != null) {
1021 mMediaProviderClient.release();
1022 mMediaProviderClient = null;
1023 }
1024 }
1025
Michael Kolbd6954f32013-03-08 20:43:01 -08001026 @Override
1027 public void onCaptureCancelled() {
1028 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1029 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001030 }
1031
Michael Kolbd6954f32013-03-08 20:43:01 -08001032 @Override
1033 public void onCaptureRetake() {
Michael Kolb8872c232013-01-29 10:33:22 -08001034 if (mPaused)
1035 return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001036 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001037 setupPreview();
1038 }
1039
Michael Kolbd6954f32013-03-08 20:43:01 -08001040 @Override
1041 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001042 if (mPaused) {
1043 return;
1044 }
1045
1046 byte[] data = mJpegImageData;
1047
1048 if (mCropValue == null) {
1049 // First handle the no crop case -- just return the value. If the
1050 // caller specifies a "save uri" then write the data to its
1051 // stream. Otherwise, pass back a scaled down version of the bitmap
1052 // directly in the extras.
1053 if (mSaveUri != null) {
1054 OutputStream outputStream = null;
1055 try {
1056 outputStream = mContentResolver.openOutputStream(mSaveUri);
1057 outputStream.write(data);
1058 outputStream.close();
1059
1060 mActivity.setResultEx(Activity.RESULT_OK);
1061 mActivity.finish();
1062 } catch (IOException ex) {
1063 // ignore exception
1064 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001065 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001066 }
1067 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001068 ExifInterface exif = Exif.getExif(data);
1069 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001070 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1071 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001072 mActivity.setResultEx(Activity.RESULT_OK,
1073 new Intent("inline-data").putExtra("data", bitmap));
1074 mActivity.finish();
1075 }
1076 } else {
1077 // Save the image to a temp file and invoke the cropper
1078 Uri tempUri = null;
1079 FileOutputStream tempStream = null;
1080 try {
1081 File path = mActivity.getFileStreamPath(sTempCropFilename);
1082 path.delete();
1083 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1084 tempStream.write(data);
1085 tempStream.close();
1086 tempUri = Uri.fromFile(path);
1087 } catch (FileNotFoundException ex) {
1088 mActivity.setResultEx(Activity.RESULT_CANCELED);
1089 mActivity.finish();
1090 return;
1091 } catch (IOException ex) {
1092 mActivity.setResultEx(Activity.RESULT_CANCELED);
1093 mActivity.finish();
1094 return;
1095 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001096 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001097 }
1098
1099 Bundle newExtras = new Bundle();
1100 if (mCropValue.equals("circle")) {
1101 newExtras.putString("circleCrop", "true");
1102 }
1103 if (mSaveUri != null) {
1104 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1105 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001106 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001107 }
1108 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001109 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001110 }
1111
Sascha Haeberling37f36112013-08-06 14:31:52 -07001112 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001113 final String CROP_ACTION = "com.android.camera.action.CROP";
1114 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001115
1116 cropIntent.setData(tempUri);
1117 cropIntent.putExtras(newExtras);
1118
1119 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1120 }
1121 }
1122
Michael Kolb8872c232013-01-29 10:33:22 -08001123 @Override
1124 public void onShutterButtonFocus(boolean pressed) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001125 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001126 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1127 || (mCameraState == PREVIEW_STOPPED)) return;
1128
1129 // Do not do focus if there is not enough storage.
1130 if (pressed && !canTakePicture()) return;
1131
1132 if (pressed) {
Michael Kolb8872c232013-01-29 10:33:22 -08001133 mFocusManager.onShutterDown();
1134 } else {
Doris Liuda50e052013-02-07 14:36:32 -08001135 // for countdown mode, we need to postpone the shutter release
1136 // i.e. lock the focus during countdown.
Michael Kolbd6954f32013-03-08 20:43:01 -08001137 if (!mUI.isCountingDown()) {
Doris Liuda50e052013-02-07 14:36:32 -08001138 mFocusManager.onShutterUp();
1139 }
Michael Kolb8872c232013-01-29 10:33:22 -08001140 }
1141 }
1142
1143 @Override
1144 public void onShutterButtonClick() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001145 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001146 || (mCameraState == SWITCHING_CAMERA)
1147 || (mCameraState == PREVIEW_STOPPED)) return;
1148
1149 // Do not take the picture if there is not enough storage.
1150 if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
1151 Log.i(TAG, "Not enough space or storage not ready. remaining="
1152 + mActivity.getStorageSpace());
1153 return;
1154 }
1155 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1156
Angus Kongb50b5cb2013-08-09 14:55:20 -07001157 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001158 mUI.hideSwitcher();
1159 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001160 }
Michael Kolb8872c232013-01-29 10:33:22 -08001161 // If the user wants to do a snapshot while the previous one is still
1162 // in progress, remember the fact and do it after we finish the previous
1163 // one and re-start the preview. Snapshot in progress also includes the
1164 // state that autofocus is focusing and a picture will be taken when
1165 // focus callback arrives.
1166 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1167 && !mIsImageCaptureIntent) {
1168 mSnapshotOnIdle = true;
1169 return;
1170 }
1171
1172 String timer = mPreferences.getString(
1173 CameraSettings.KEY_TIMER,
1174 mActivity.getString(R.string.pref_camera_timer_default));
1175 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1176 mActivity.getString(R.string.pref_camera_timer_sound_default))
1177 .equals(mActivity.getString(R.string.setting_on_value));
1178
1179 int seconds = Integer.parseInt(timer);
1180 // When shutter button is pressed, check whether the previous countdown is
1181 // finished. If not, cancel the previous countdown and start a new one.
Michael Kolbd6954f32013-03-08 20:43:01 -08001182 if (mUI.isCountingDown()) {
1183 mUI.cancelCountDown();
1184 }
1185 if (seconds > 0) {
1186 mUI.startCountDown(seconds, playSound);
Michael Kolb8872c232013-01-29 10:33:22 -08001187 } else {
1188 mSnapshotOnIdle = false;
1189 mFocusManager.doSnap();
1190 }
1191 }
1192
1193 @Override
1194 public void installIntentFilter() {
1195 }
1196
1197 @Override
1198 public boolean updateStorageHintOnResume() {
1199 return mFirstTimeInitialized;
1200 }
1201
1202 @Override
1203 public void updateCameraAppView() {
1204 }
1205
1206 @Override
1207 public void onResumeBeforeSuper() {
1208 mPaused = false;
1209 }
1210
1211 @Override
1212 public void onResumeAfterSuper() {
1213 if (mOpenCameraFail || mCameraDisabled) return;
1214
1215 mJpegPictureCallbackTime = 0;
1216 mZoomValue = 0;
Michael Kolb8872c232013-01-29 10:33:22 -08001217 // Start the preview if it is not started.
1218 if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
1219 resetExposureCompensation();
1220 mCameraStartUpThread = new CameraStartUpThread();
1221 mCameraStartUpThread.start();
1222 }
1223
1224 // If first time initialization is not finished, put it in the
1225 // message queue.
1226 if (!mFirstTimeInitialized) {
1227 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1228 } else {
1229 initializeSecondTime();
1230 }
1231 keepScreenOnAwhile();
1232
1233 // Dismiss open menu if exists.
1234 PopupManager.getInstance(mActivity).notifyShowPopup(null);
Bobby Georgescu0a7dd572013-03-12 22:45:17 -07001235 UsageStatistics.onContentViewChanged(
1236 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
Angus Kong0d00a892013-03-26 11:40:40 -07001237
1238 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1239 if (gsensor != null) {
1240 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1241 }
1242
1243 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1244 if (msensor != null) {
1245 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1246 }
Michael Kolb8872c232013-01-29 10:33:22 -08001247 }
1248
1249 void waitCameraStartUpThread() {
1250 try {
1251 if (mCameraStartUpThread != null) {
1252 mCameraStartUpThread.cancel();
1253 mCameraStartUpThread.join();
1254 mCameraStartUpThread = null;
1255 setCameraState(IDLE);
1256 }
1257 } catch (InterruptedException e) {
1258 // ignore
1259 }
1260 }
1261
1262 @Override
1263 public void onPauseBeforeSuper() {
1264 mPaused = true;
Angus Kong0d00a892013-03-26 11:40:40 -07001265 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1266 if (gsensor != null) {
1267 mSensorManager.unregisterListener(this, gsensor);
1268 }
1269
1270 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1271 if (msensor != null) {
1272 mSensorManager.unregisterListener(this, msensor);
1273 }
Michael Kolb8872c232013-01-29 10:33:22 -08001274 }
1275
1276 @Override
1277 public void onPauseAfterSuper() {
1278 // Wait the camera start up thread to finish.
1279 waitCameraStartUpThread();
1280
1281 // When camera is started from secure lock screen for the first time
1282 // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1283 // To reduce the latency, keep the camera for a short time so it does
1284 // not need to be opened again.
1285 if (mCameraDevice != null && mActivity.isSecureCamera()
Angus Kong6a8e8a12013-07-19 14:55:07 -07001286 && CameraActivity.isFirstStartAfterScreenOn()) {
1287 CameraActivity.resetFirstStartAfterScreenOn();
Michael Kolb8872c232013-01-29 10:33:22 -08001288 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1289 }
1290 // Reset the focus first. Camera CTS does not guarantee that
1291 // cancelAutoFocus is allowed after preview stops.
1292 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1293 mCameraDevice.cancelAutoFocus();
1294 }
1295 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001296
Angus Kongce5480e2013-01-29 17:43:48 -08001297 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001298
1299 if (mLocationManager != null) mLocationManager.recordLocation(false);
1300
1301 // If we are in an image capture intent and has taken
1302 // a picture, we just clear it in onPause.
1303 mJpegImageData = null;
1304
1305 // Remove the messages in the event queue.
1306 mHandler.removeMessages(SETUP_PREVIEW);
1307 mHandler.removeMessages(FIRST_TIME_INIT);
1308 mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
1309 mHandler.removeMessages(SWITCH_CAMERA);
1310 mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
1311 mHandler.removeMessages(CAMERA_OPEN_DONE);
1312 mHandler.removeMessages(START_PREVIEW_DONE);
1313 mHandler.removeMessages(OPEN_CAMERA_FAIL);
1314 mHandler.removeMessages(CAMERA_DISABLED);
1315
Michael Kolbd6954f32013-03-08 20:43:01 -08001316 closeCamera();
1317
1318 resetScreenOn();
1319 mUI.onPause();
1320
Michael Kolb8872c232013-01-29 10:33:22 -08001321 mPendingSwitchCameraId = -1;
1322 if (mFocusManager != null) mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001323 MediaSaveService s = mActivity.getMediaSaveService();
1324 if (s != null) {
1325 s.setListener(null);
1326 }
Michael Kolb8872c232013-01-29 10:33:22 -08001327 }
1328
Michael Kolb8872c232013-01-29 10:33:22 -08001329 /**
1330 * The focus manager is the first UI related element to get initialized,
1331 * and it requires the RenderOverlay, so initialize it here
1332 */
1333 private void initializeFocusManager() {
1334 // Create FocusManager object. startPreview needs it.
Michael Kolb8872c232013-01-29 10:33:22 -08001335 // if mFocusManager not null, reuse it
1336 // otherwise create a new instance
1337 if (mFocusManager != null) {
1338 mFocusManager.removeMessages();
1339 } else {
1340 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1341 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
1342 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1343 R.array.pref_camera_focusmode_default_array);
1344 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
1345 mInitialParams, this, mirror,
Michael Kolbd6954f32013-03-08 20:43:01 -08001346 mActivity.getMainLooper(), mUI);
Michael Kolb8872c232013-01-29 10:33:22 -08001347 }
1348 }
1349
Michael Kolb8872c232013-01-29 10:33:22 -08001350 @Override
1351 public void onConfigurationChanged(Configuration newConfig) {
1352 Log.v(TAG, "onConfigurationChanged");
1353 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001354 }
1355
1356 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001357 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001358 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001359 setDisplayOrientation();
1360 }
1361 }
1362
1363 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001364 public void onActivityResult(
1365 int requestCode, int resultCode, Intent data) {
1366 switch (requestCode) {
1367 case REQUEST_CROP: {
1368 Intent intent = new Intent();
1369 if (data != null) {
1370 Bundle extras = data.getExtras();
1371 if (extras != null) {
1372 intent.putExtras(extras);
1373 }
1374 }
1375 mActivity.setResultEx(resultCode, intent);
1376 mActivity.finish();
1377
1378 File path = mActivity.getFileStreamPath(sTempCropFilename);
1379 path.delete();
1380
1381 break;
1382 }
1383 }
1384 }
1385
1386 private boolean canTakePicture() {
1387 return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
1388 }
1389
1390 @Override
1391 public void autoFocus() {
1392 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001393 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001394 setCameraState(FOCUSING);
1395 }
1396
1397 @Override
1398 public void cancelAutoFocus() {
1399 mCameraDevice.cancelAutoFocus();
1400 setCameraState(IDLE);
1401 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1402 }
1403
1404 // Preview area is touched. Handle touch focus.
1405 @Override
1406 public void onSingleTapUp(View view, int x, int y) {
1407 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1408 || mCameraState == SNAPSHOT_IN_PROGRESS
1409 || mCameraState == SWITCHING_CAMERA
1410 || mCameraState == PREVIEW_STOPPED) {
1411 return;
1412 }
1413
Michael Kolb8872c232013-01-29 10:33:22 -08001414 // 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:
Doris Liu6432cd62013-06-13 17:20:31 -07001430 if (/*TODO: mActivity.isInCameraApp() &&*/ mFirstTimeInitialized) {
Michael Kolb8872c232013-01-29 10:33:22 -08001431 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 Kolb8872c232013-01-29 10:33:22 -08001449 onShutterButtonFocus(true);
Michael Kolbd6954f32013-03-08 20:43:01 -08001450 mUI.pressShutterButton();
Michael Kolb8872c232013-01-29 10:33:22 -08001451 }
1452 return true;
1453 }
1454 return false;
1455 }
1456
1457 @Override
1458 public boolean onKeyUp(int keyCode, KeyEvent event) {
1459 switch (keyCode) {
1460 case KeyEvent.KEYCODE_VOLUME_UP:
1461 case KeyEvent.KEYCODE_VOLUME_DOWN:
Doris Liu6432cd62013-06-13 17:20:31 -07001462 if (/*mActivity.isInCameraApp() && */ mFirstTimeInitialized) {
Michael Kolb8872c232013-01-29 10:33:22 -08001463 onShutterButtonClick();
1464 return true;
1465 }
1466 return false;
1467 case KeyEvent.KEYCODE_FOCUS:
1468 if (mFirstTimeInitialized) {
1469 onShutterButtonFocus(false);
1470 }
1471 return true;
1472 }
1473 return false;
1474 }
1475
1476 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1477 private void closeCamera() {
1478 if (mCameraDevice != null) {
1479 mCameraDevice.setZoomChangeListener(null);
1480 if(ApiHelper.HAS_FACE_DETECTION) {
Angus Kong9e765522013-07-31 14:05:20 -07001481 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001482 }
1483 mCameraDevice.setErrorCallback(null);
1484 CameraHolder.instance().release();
1485 mFaceDetectionStarted = false;
1486 mCameraDevice = null;
1487 setCameraState(PREVIEW_STOPPED);
1488 mFocusManager.onCameraReleased();
1489 }
1490 }
1491
1492 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001493 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1494 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001495 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001496 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001497 if (mFocusManager != null) {
1498 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1499 }
Doris Liu6432cd62013-06-13 17:20:31 -07001500 // Change the camera display orientation
1501 if (mCameraDevice != null) {
1502 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1503 }
Michael Kolb8872c232013-01-29 10:33:22 -08001504 }
1505
1506 // Only called by UI thread.
1507 private void setupPreview() {
1508 mFocusManager.resetTouchFocus();
1509 startPreview();
1510 setCameraState(IDLE);
1511 startFaceDetection();
1512 }
1513
1514 // This can be called by UI Thread or CameraStartUpThread. So this should
1515 // not modify the views.
1516 private void startPreview() {
1517 mCameraDevice.setErrorCallback(mErrorCallback);
1518
1519 // ICS camera frameworks has a bug. Face detection state is not cleared
1520 // after taking a picture. Stop the preview to work around it. The bug
1521 // was fixed in JB.
1522 if (mCameraState != PREVIEW_STOPPED) stopPreview();
1523
1524 setDisplayOrientation();
1525
1526 if (!mSnapshotOnIdle) {
1527 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1528 // resume it because it may have been paused by autoFocus call.
Angus Kongb50b5cb2013-08-09 14:55:20 -07001529 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
Michael Kolb8872c232013-01-29 10:33:22 -08001530 mCameraDevice.cancelAutoFocus();
1531 }
1532 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1533 }
1534 setCameraParameters(UPDATE_PARAM_ALL);
Doris Liu6432cd62013-06-13 17:20:31 -07001535 // Let UI set its expected aspect ratio
1536 mUI.setPreviewSize(mParameters.getPreviewSize());
1537 Object st = mUI.getSurfaceTexture();
1538 if (st != null) {
Angus Kong9ef99252013-07-18 18:04:19 -07001539 mCameraDevice.setPreviewTexture((SurfaceTexture) st);
Michael Kolb8872c232013-01-29 10:33:22 -08001540 }
1541
1542 Log.v(TAG, "startPreview");
Angus Kong9ef99252013-07-18 18:04:19 -07001543 mCameraDevice.startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001544 mFocusManager.onPreviewStarted();
1545
1546 if (mSnapshotOnIdle) {
1547 mHandler.post(mDoSnapRunnable);
1548 }
1549 }
1550
Michael Kolbd6954f32013-03-08 20:43:01 -08001551 @Override
1552 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001553 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1554 Log.v(TAG, "stopPreview");
1555 mCameraDevice.stopPreview();
1556 mFaceDetectionStarted = false;
1557 }
1558 setCameraState(PREVIEW_STOPPED);
1559 if (mFocusManager != null) mFocusManager.onPreviewStopped();
1560 }
1561
1562 @SuppressWarnings("deprecation")
1563 private void updateCameraParametersInitialize() {
1564 // Reset preview frame rate to the maximum because it may be lowered by
1565 // video camera application.
Angus Kongb50b5cb2013-08-09 14:55:20 -07001566 int[] fpsRange = CameraUtil.getMaxPreviewFpsRange(mParameters);
Doris Liu6432cd62013-06-13 17:20:31 -07001567 if (fpsRange.length > 0) {
1568 mParameters.setPreviewFpsRange(
1569 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1570 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001571 }
1572
Angus Kongb50b5cb2013-08-09 14:55:20 -07001573 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001574
1575 // Disable video stabilization. Convenience methods not available in API
1576 // level <= 14
1577 String vstabSupported = mParameters.get("video-stabilization-supported");
1578 if ("true".equals(vstabSupported)) {
1579 mParameters.set("video-stabilization", "false");
1580 }
1581 }
1582
1583 private void updateCameraParametersZoom() {
1584 // Set zoom.
1585 if (mParameters.isZoomSupported()) {
1586 mParameters.setZoom(mZoomValue);
1587 }
1588 }
1589
1590 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1591 private void setAutoExposureLockIfSupported() {
1592 if (mAeLockSupported) {
1593 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1594 }
1595 }
1596
1597 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1598 private void setAutoWhiteBalanceLockIfSupported() {
1599 if (mAwbLockSupported) {
1600 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1601 }
1602 }
1603
1604 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1605 private void setFocusAreasIfSupported() {
1606 if (mFocusAreaSupported) {
1607 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1608 }
1609 }
1610
1611 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1612 private void setMeteringAreasIfSupported() {
1613 if (mMeteringAreaSupported) {
1614 // Use the same area for focus and metering.
1615 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1616 }
1617 }
1618
1619 private void updateCameraParametersPreference() {
1620 setAutoExposureLockIfSupported();
1621 setAutoWhiteBalanceLockIfSupported();
1622 setFocusAreasIfSupported();
1623 setMeteringAreasIfSupported();
1624
1625 // Set picture size.
1626 String pictureSize = mPreferences.getString(
1627 CameraSettings.KEY_PICTURE_SIZE, null);
1628 if (pictureSize == null) {
1629 CameraSettings.initialCameraPictureSize(mActivity, mParameters);
1630 } else {
1631 List<Size> supported = mParameters.getSupportedPictureSizes();
1632 CameraSettings.setCameraPictureSize(
1633 pictureSize, supported, mParameters);
1634 }
1635 Size size = mParameters.getPictureSize();
1636
1637 // Set a preview size that is closest to the viewfinder height and has
1638 // the right aspect ratio.
1639 List<Size> sizes = mParameters.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001640 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Michael Kolb8872c232013-01-29 10:33:22 -08001641 (double) size.width / size.height);
1642 Size original = mParameters.getPreviewSize();
1643 if (!original.equals(optimalSize)) {
1644 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
Doris Liu6432cd62013-06-13 17:20:31 -07001645
Michael Kolb8872c232013-01-29 10:33:22 -08001646 // Zoom related settings will be changed for different preview
1647 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07001648 if (mHandler.getLooper() == Looper.myLooper()) {
1649 // On UI thread only, not when camera starts up
1650 setupPreview();
1651 } else {
1652 mCameraDevice.setParameters(mParameters);
1653 }
Michael Kolb8872c232013-01-29 10:33:22 -08001654 mParameters = mCameraDevice.getParameters();
1655 }
1656 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1657
1658 // Since changing scene mode may change supported values, set scene mode
1659 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1660 // separate preference.
1661 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
1662 mActivity.getString(R.string.pref_camera_hdr_default));
1663 if (mActivity.getString(R.string.setting_on_value).equals(hdr)) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001664 mSceneMode = CameraUtil.SCENE_MODE_HDR;
Michael Kolb8872c232013-01-29 10:33:22 -08001665 } else {
1666 mSceneMode = mPreferences.getString(
1667 CameraSettings.KEY_SCENE_MODE,
1668 mActivity.getString(R.string.pref_camera_scenemode_default));
1669 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001670 if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
Michael Kolb8872c232013-01-29 10:33:22 -08001671 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1672 mParameters.setSceneMode(mSceneMode);
1673
1674 // Setting scene mode will change the settings of flash mode,
1675 // white balance, and focus mode. Here we read back the
1676 // parameters, so we can know those settings.
1677 mCameraDevice.setParameters(mParameters);
1678 mParameters = mCameraDevice.getParameters();
1679 }
1680 } else {
1681 mSceneMode = mParameters.getSceneMode();
1682 if (mSceneMode == null) {
1683 mSceneMode = Parameters.SCENE_MODE_AUTO;
1684 }
1685 }
1686
1687 // Set JPEG quality.
1688 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1689 CameraProfile.QUALITY_HIGH);
1690 mParameters.setJpegQuality(jpegQuality);
1691
1692 // For the following settings, we need to check if the settings are
1693 // still supported by latest driver, if not, ignore the settings.
1694
1695 // Set exposure compensation
1696 int value = CameraSettings.readExposure(mPreferences);
1697 int max = mParameters.getMaxExposureCompensation();
1698 int min = mParameters.getMinExposureCompensation();
1699 if (value >= min && value <= max) {
1700 mParameters.setExposureCompensation(value);
1701 } else {
1702 Log.w(TAG, "invalid exposure range: " + value);
1703 }
1704
1705 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1706 // Set flash mode.
1707 String flashMode = mPreferences.getString(
1708 CameraSettings.KEY_FLASH_MODE,
1709 mActivity.getString(R.string.pref_camera_flashmode_default));
1710 List<String> supportedFlash = mParameters.getSupportedFlashModes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001711 if (CameraUtil.isSupported(flashMode, supportedFlash)) {
Michael Kolb8872c232013-01-29 10:33:22 -08001712 mParameters.setFlashMode(flashMode);
1713 } else {
1714 flashMode = mParameters.getFlashMode();
1715 if (flashMode == null) {
1716 flashMode = mActivity.getString(
1717 R.string.pref_camera_flashmode_no_flash);
1718 }
1719 }
1720
1721 // Set white balance parameter.
1722 String whiteBalance = mPreferences.getString(
1723 CameraSettings.KEY_WHITE_BALANCE,
1724 mActivity.getString(R.string.pref_camera_whitebalance_default));
Angus Kongb50b5cb2013-08-09 14:55:20 -07001725 if (CameraUtil.isSupported(whiteBalance,
Michael Kolb8872c232013-01-29 10:33:22 -08001726 mParameters.getSupportedWhiteBalance())) {
1727 mParameters.setWhiteBalance(whiteBalance);
1728 } else {
1729 whiteBalance = mParameters.getWhiteBalance();
1730 if (whiteBalance == null) {
1731 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1732 }
1733 }
1734
1735 // Set focus mode.
1736 mFocusManager.overrideFocusMode(null);
1737 mParameters.setFocusMode(mFocusManager.getFocusMode());
1738 } else {
1739 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1740 }
1741
1742 if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1743 updateAutoFocusMoveCallback();
1744 }
1745 }
1746
1747 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1748 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001749 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07001750 mCameraDevice.setAutoFocusMoveCallback(mHandler,
1751 (CameraManager.CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001752 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07001753 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001754 }
1755 }
1756
1757 // We separate the parameters into several subsets, so we can update only
1758 // the subsets actually need updating. The PREFERENCE set needs extra
1759 // locking because the preference can be changed from GLThread as well.
1760 private void setCameraParameters(int updateSet) {
1761 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1762 updateCameraParametersInitialize();
1763 }
1764
1765 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1766 updateCameraParametersZoom();
1767 }
1768
1769 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
1770 updateCameraParametersPreference();
1771 }
1772
1773 mCameraDevice.setParameters(mParameters);
1774 }
1775
1776 // If the Camera is idle, update the parameters immediately, otherwise
1777 // accumulate them in mUpdateSet and update later.
1778 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1779 mUpdateSet |= additionalUpdateSet;
1780 if (mCameraDevice == null) {
1781 // We will update all the parameters when we open the device, so
1782 // we don't need to do anything now.
1783 mUpdateSet = 0;
1784 return;
1785 } else if (isCameraIdle()) {
1786 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001787 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001788 mUpdateSet = 0;
1789 } else {
1790 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1791 mHandler.sendEmptyMessageDelayed(
1792 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1793 }
1794 }
1795 }
1796
Michael Kolbd6954f32013-03-08 20:43:01 -08001797 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001798 return (mCameraState == IDLE) ||
1799 (mCameraState == PREVIEW_STOPPED) ||
1800 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1801 && (mCameraState != SWITCHING_CAMERA));
1802 }
1803
Michael Kolbd6954f32013-03-08 20:43:01 -08001804 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001805 String action = mActivity.getIntent().getAction();
1806 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Angus Kong6a8e8a12013-07-19 14:55:07 -07001807 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08001808 }
1809
1810 private void setupCaptureParams() {
1811 Bundle myExtras = mActivity.getIntent().getExtras();
1812 if (myExtras != null) {
1813 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1814 mCropValue = myExtras.getString("crop");
1815 }
1816 }
1817
Michael Kolb8872c232013-01-29 10:33:22 -08001818 @Override
1819 public void onSharedPreferenceChanged() {
1820 // ignore the events after "onPause()"
1821 if (mPaused) return;
1822
1823 boolean recordLocation = RecordLocationPreference.get(
1824 mPreferences, mContentResolver);
1825 mLocationManager.recordLocation(recordLocation);
1826
1827 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
Michael Kolbeb8adc12013-04-26 11:09:29 -07001828 mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, mPreferences);
Michael Kolb8872c232013-01-29 10:33:22 -08001829 }
1830
1831 @Override
1832 public void onCameraPickerClicked(int cameraId) {
1833 if (mPaused || mPendingSwitchCameraId != -1) return;
1834
1835 mPendingSwitchCameraId = cameraId;
Doris Liu6432cd62013-06-13 17:20:31 -07001836
1837 Log.v(TAG, "Start to switch camera. cameraId=" + cameraId);
1838 // We need to keep a preview frame for the animation before
1839 // releasing the camera. This will trigger onPreviewTextureCopied.
1840 //TODO: Need to animate the camera switch
1841 switchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -08001842 }
1843
Michael Kolb8872c232013-01-29 10:33:22 -08001844 // Preview texture has been copied. Now camera can be released and the
1845 // animation can be started.
1846 @Override
1847 public void onPreviewTextureCopied() {
1848 mHandler.sendEmptyMessage(SWITCH_CAMERA);
1849 }
1850
1851 @Override
1852 public void onCaptureTextureCopied() {
1853 }
1854
1855 @Override
1856 public void onUserInteraction() {
Doris Liu6432cd62013-06-13 17:20:31 -07001857 if (!mActivity.isFinishing()) keepScreenOnAwhile();
Michael Kolb8872c232013-01-29 10:33:22 -08001858 }
1859
1860 private void resetScreenOn() {
1861 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1862 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1863 }
1864
1865 private void keepScreenOnAwhile() {
1866 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1867 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1868 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1869 }
1870
Michael Kolb8872c232013-01-29 10:33:22 -08001871 @Override
1872 public void onOverriddenPreferencesClicked() {
1873 if (mPaused) return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001874 mUI.showPreferencesToast();
Michael Kolb8872c232013-01-29 10:33:22 -08001875 }
1876
1877 private void showTapToFocusToast() {
1878 // TODO: Use a toast?
1879 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1880 // Clear the preference.
1881 Editor editor = mPreferences.edit();
1882 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
1883 editor.apply();
1884 }
1885
1886 private void initializeCapabilities() {
1887 mInitialParams = mCameraDevice.getParameters();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001888 mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams);
1889 mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams);
1890 mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams);
1891 mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams);
Michael Kolb8872c232013-01-29 10:33:22 -08001892 mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07001893 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08001894 }
1895
Michael Kolb8872c232013-01-29 10:33:22 -08001896 @Override
1897 public void onCountDownFinished() {
1898 mSnapshotOnIdle = false;
1899 mFocusManager.doSnap();
Doris Liuda50e052013-02-07 14:36:32 -08001900 mFocusManager.onShutterUp();
Michael Kolb8872c232013-01-29 10:33:22 -08001901 }
1902
Michael Kolb8872c232013-01-29 10:33:22 -08001903 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001904 public void onShowSwitcherPopup() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001905 mUI.onShowSwitcherPopup();
Michael Kolb8872c232013-01-29 10:33:22 -08001906 }
1907
Angus Kongce5480e2013-01-29 17:43:48 -08001908 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001909 public int onZoomChanged(int index) {
1910 // Not useful to change zoom value when the activity is paused.
1911 if (mPaused) return index;
1912 mZoomValue = index;
1913 if (mParameters == null || mCameraDevice == null) return index;
1914 // Set zoom parameters asynchronously
1915 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07001916 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08001917 Parameters p = mCameraDevice.getParameters();
1918 if (p != null) return p.getZoom();
1919 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08001920 }
1921
1922 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001923 public int getCameraState() {
1924 return mCameraState;
1925 }
1926
1927 @Override
1928 public void onQueueStatus(boolean full) {
1929 mUI.enableShutter(!full);
Angus Kongce5480e2013-01-29 17:43:48 -08001930 }
Angus Kong86d36312013-01-31 18:22:44 -08001931
1932 @Override
1933 public void onMediaSaveServiceConnected(MediaSaveService s) {
1934 // We set the listener only when both service and shutterbutton
1935 // are initialized.
Michael Kolbd6954f32013-03-08 20:43:01 -08001936 if (mFirstTimeInitialized) {
1937 s.setListener(this);
1938 }
Angus Kong86d36312013-01-31 18:22:44 -08001939 }
Angus Kong0d00a892013-03-26 11:40:40 -07001940
1941 @Override
1942 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1943 }
1944
1945 @Override
1946 public void onSensorChanged(SensorEvent event) {
1947 int type = event.sensor.getType();
1948 float[] data;
1949 if (type == Sensor.TYPE_ACCELEROMETER) {
1950 data = mGData;
1951 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
1952 data = mMData;
1953 } else {
1954 // we should not be here.
1955 return;
1956 }
1957 for (int i = 0; i < 3 ; i++) {
1958 data[i] = event.values[i];
1959 }
1960 float[] orientation = new float[3];
1961 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
1962 SensorManager.getOrientation(mR, orientation);
1963 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
1964 if (mHeading < 0) {
1965 mHeading += 360;
1966 }
Angus Kong0d00a892013-03-26 11:40:40 -07001967 }
Doris Liu6432cd62013-06-13 17:20:31 -07001968
1969 @Override
1970 public void onSwitchMode(boolean toCamera) {
1971 mUI.onSwitchMode(toCamera);
1972 }
1973
1974/* Below is no longer needed, except to get rid of compile error
1975 * TODO: Remove these
1976 */
1977
1978 // TODO: Delete this function after old camera code is removed
1979 @Override
1980 public void onRestorePreferencesClicked() {}
1981
Michael Kolb8872c232013-01-29 10:33:22 -08001982}