blob: 5174825c500b8f628a0c3d93d46419bd45a7eb31 [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.annotation.TargetApi;
20import android.app.Activity;
Michael Kolb8872c232013-01-29 10:33:22 -080021import android.content.ContentProviderClient;
22import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070023import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080024import android.content.Intent;
25import android.content.SharedPreferences.Editor;
26import android.content.res.Configuration;
27import android.graphics.Bitmap;
Doris Liu36ebcb12013-10-28 14:44:24 -070028import android.graphics.Rect;
Michael Kolb8872c232013-01-29 10:33:22 -080029import android.graphics.SurfaceTexture;
30import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080031import android.hardware.Camera.Parameters;
Michael Kolb8872c232013-01-29 10:33:22 -080032import android.hardware.Camera.Size;
Angus Kong0d00a892013-03-26 11:40:40 -070033import android.hardware.Sensor;
34import android.hardware.SensorEvent;
35import android.hardware.SensorEventListener;
36import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080037import android.location.Location;
38import android.media.CameraProfile;
39import android.net.Uri;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070040import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080041import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080042import android.os.Handler;
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -080043import android.os.HandlerThread;
Michael Kolb8872c232013-01-29 10:33:22 -080044import android.os.Looper;
45import android.os.Message;
46import android.os.MessageQueue;
47import android.os.SystemClock;
48import android.provider.MediaStore;
49import android.util.Log;
Michael Kolb8872c232013-01-29 10:33:22 -080050import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080051import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080052import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080053import android.view.WindowManager;
Michael Kolb8872c232013-01-29 10:33:22 -080054
Angus Kong9ef99252013-07-18 18:04:19 -070055import com.android.camera.CameraManager.CameraAFCallback;
56import com.android.camera.CameraManager.CameraAFMoveCallback;
57import com.android.camera.CameraManager.CameraPictureCallback;
Michael Kolb8872c232013-01-29 10:33:22 -080058import com.android.camera.CameraManager.CameraProxy;
Angus Kong9ef99252013-07-18 18:04:19 -070059import com.android.camera.CameraManager.CameraShutterCallback;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070060import com.android.camera.PhotoModule.NamedImages.NamedEntity;
Angus Kongfd4fc0e2013-11-07 15:38:09 -080061import com.android.camera.app.MediaSaver;
ztenghuia16e7b52013-08-23 11:47:56 -070062import com.android.camera.exif.ExifInterface;
63import com.android.camera.exif.ExifTag;
64import com.android.camera.exif.Rational;
Michael Kolbd6954f32013-03-08 20:43:01 -080065import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070066import com.android.camera.ui.ModuleSwitcher;
Michael Kolb8872c232013-01-29 10:33:22 -080067import com.android.camera.ui.RotateTextToast;
Angus Kongdcccc512013-08-08 17:06:03 -070068import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070069import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070070import com.android.camera.util.GcamHelper;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070071import com.android.camera.util.UsageStatistics;
72import com.android.camera2.R;
Michael Kolb8872c232013-01-29 10:33:22 -080073
Angus Kongdcccc512013-08-08 17:06:03 -070074import java.io.File;
75import java.io.FileNotFoundException;
76import java.io.FileOutputStream;
77import java.io.IOException;
78import java.io.OutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070079import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070080import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070081
Michael Kolb8872c232013-01-29 10:33:22 -080082public class PhotoModule
Dan Aminzade92ae10e2013-08-13 14:44:25 -070083 implements CameraModule,
84 PhotoController,
85 FocusOverlayManager.Listener,
86 CameraPreference.OnPreferenceChangedListener,
Angus Kongfd4fc0e2013-11-07 15:38:09 -080087 ShutterButton.OnShutterButtonListener, MediaSaver.QueueListener,
Dan Aminzade92ae10e2013-08-13 14:44:25 -070088 OnCountDownFinishedListener,
89 SensorEventListener {
Michael Kolb8872c232013-01-29 10:33:22 -080090
91 private static final String TAG = "CAM_PhotoModule";
92
93 // We number the request code from 1000 to avoid collision with Gallery.
94 private static final int REQUEST_CROP = 1000;
95
96 private static final int SETUP_PREVIEW = 1;
97 private static final int FIRST_TIME_INIT = 2;
98 private static final int CLEAR_SCREEN_DELAY = 3;
99 private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
Angus Kongdcccc512013-08-08 17:06:03 -0700100 private static final int SHOW_TAP_TO_FOCUS_TOAST = 5;
101 private static final int SWITCH_CAMERA = 6;
102 private static final int SWITCH_CAMERA_START_ANIMATION = 7;
103 private static final int CAMERA_OPEN_DONE = 8;
104 private static final int OPEN_CAMERA_FAIL = 9;
105 private static final int CAMERA_DISABLED = 10;
ztenghui367c7c82013-10-16 14:43:26 -0700106 private static final int SWITCH_TO_GCAM_MODULE = 11;
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800107 private static final int CAMERA_PREVIEW_DONE = 12;
108
109 private static final int OPEN_CAMERA_ASYNC = 1;
110 private static final int START_PREVIEW_ASYNC = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800111
112 // The subset of parameters we need to update in setCameraParameters().
113 private static final int UPDATE_PARAM_INITIALIZE = 1;
114 private static final int UPDATE_PARAM_ZOOM = 2;
115 private static final int UPDATE_PARAM_PREFERENCE = 4;
116 private static final int UPDATE_PARAM_ALL = -1;
117
118 // This is the timeout to keep the camera in onPause for the first time
119 // after screen on if the activity is started from secure lock screen.
120 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
121
Ruben Brunkd7488272013-10-10 18:45:53 -0700122 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
123
Michael Kolb8872c232013-01-29 10:33:22 -0800124 // copied from Camera hierarchy
125 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800126 private CameraProxy mCameraDevice;
127 private int mCameraId;
128 private Parameters mParameters;
129 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800130
131 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800132
Michael Kolb8872c232013-01-29 10:33:22 -0800133 // The activity is going to switch to the specified camera id. This is
134 // needed because texture copy is done in GL thread. -1 means camera is not
135 // switching.
136 protected int mPendingSwitchCameraId = -1;
137 private boolean mOpenCameraFail;
138 private boolean mCameraDisabled;
139
140 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
141 // needed to be updated in mUpdateSet.
142 private int mUpdateSet;
143
144 private static final int SCREEN_DELAY = 2 * 60 * 1000;
145
146 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800147
148 private Parameters mInitialParams;
149 private boolean mFocusAreaSupported;
150 private boolean mMeteringAreaSupported;
151 private boolean mAeLockSupported;
152 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700153 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800154
155 // The degrees of the device rotated clockwise from its natural orientation.
156 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
157 private ComboPreferences mPreferences;
158
159 private static final String sTempCropFilename = "crop-temp";
160
161 private ContentProviderClient mMediaProviderClient;
Michael Kolb8872c232013-01-29 10:33:22 -0800162 private boolean mFaceDetectionStarted = false;
163
Michael Kolb8872c232013-01-29 10:33:22 -0800164 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
165 private String mCropValue;
166 private Uri mSaveUri;
167
Ruben Brunkd217ed02013-10-08 23:31:13 -0700168 private Uri mDebugUri;
169
Angus Kongce5480e2013-01-29 17:43:48 -0800170 // We use a queue to generated names of the images to be used later
171 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800172 private NamedImages mNamedImages;
173
174 private Runnable mDoSnapRunnable = new Runnable() {
175 @Override
176 public void run() {
177 onShutterButtonClick();
178 }
179 };
180
Michael Kolb8872c232013-01-29 10:33:22 -0800181 /**
182 * An unpublished intent flag requesting to return as soon as capturing
183 * is completed.
184 *
185 * TODO: consider publishing by moving into MediaStore.
186 */
187 private static final String EXTRA_QUICK_CAPTURE =
188 "android.intent.extra.quickCapture";
189
190 // The display rotation in degrees. This is only valid when mCameraState is
191 // not PREVIEW_STOPPED.
192 private int mDisplayRotation;
193 // The value for android.hardware.Camera.setDisplayOrientation.
194 private int mCameraDisplayOrientation;
195 // The value for UI components like indicators.
196 private int mDisplayOrientation;
197 // The value for android.hardware.Camera.Parameters.setRotation.
198 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700199 // Indicates whether we are using front camera
200 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800201 private boolean mFirstTimeInitialized;
202 private boolean mIsImageCaptureIntent;
203
Michael Kolb8872c232013-01-29 10:33:22 -0800204 private int mCameraState = PREVIEW_STOPPED;
205 private boolean mSnapshotOnIdle = false;
206
207 private ContentResolver mContentResolver;
208
209 private LocationManager mLocationManager;
210
Michael Kolb8872c232013-01-29 10:33:22 -0800211 private final PostViewPictureCallback mPostViewPictureCallback =
212 new PostViewPictureCallback();
213 private final RawPictureCallback mRawPictureCallback =
214 new RawPictureCallback();
215 private final AutoFocusCallback mAutoFocusCallback =
216 new AutoFocusCallback();
217 private final Object mAutoFocusMoveCallback =
218 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700219 ? new AutoFocusMoveCallback()
220 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800221
222 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
223
224 private long mFocusStartTime;
225 private long mShutterCallbackTime;
226 private long mPostViewPictureCallbackTime;
227 private long mRawPictureCallbackTime;
228 private long mJpegPictureCallbackTime;
229 private long mOnResumeTime;
230 private byte[] mJpegImageData;
231
232 // These latency time are for the CameraLatency test.
233 public long mAutoFocusTime;
234 public long mShutterLag;
235 public long mShutterToPictureDisplayedTime;
236 public long mPictureDisplayedToJpegCallbackTime;
237 public long mJpegCallbackFinishTime;
238 public long mCaptureStartTime;
239
240 // This handles everything about focus.
241 private FocusOverlayManager mFocusManager;
242
Michael Kolb8872c232013-01-29 10:33:22 -0800243 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800244
245 private final Handler mHandler = new MainHandler();
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800246
247 /** A thread separate from the UI thread for camera startup. */
248 private HandlerThread mOpenCameraThread;
249 /** A handler to run on the camera startup thread. */
250 private Handler mOpenCameraHandler;
251 /** This lock should always protect openCamera and closeCamera. */
252 private final Object mCameraOpenLock = new Object();
253
Michael Kolb8872c232013-01-29 10:33:22 -0800254 private PreferenceGroup mPreferenceGroup;
255
256 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700257 private SensorManager mSensorManager;
258 private float[] mGData = new float[3];
259 private float[] mMData = new float[3];
260 private float[] mR = new float[16];
261 private int mHeading = -1;
262
Angus Kongdcccc512013-08-08 17:06:03 -0700263 // True if all the parameters needed to start preview is ready.
264 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700265
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800266 private MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
267 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800268 @Override
269 public void onMediaSaved(Uri uri) {
270 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700271 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800272 }
273 }
274 };
Michael Kolb8872c232013-01-29 10:33:22 -0800275
Angus Kongdcccc512013-08-08 17:06:03 -0700276 private void checkDisplayRotation() {
277 // Set the display orientation if display rotation has changed.
278 // Sometimes this happens when the device is held upside
279 // down and camera app is opened. Rotation animation will
280 // take some time and the rotation value we have got may be
281 // wrong. Framework does not have a callback for this now.
282 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
283 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800284 }
Angus Kongdcccc512013-08-08 17:06:03 -0700285 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
286 mHandler.postDelayed(new Runnable() {
287 @Override
288 public void run() {
289 checkDisplayRotation();
290 }
291 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800292 }
293 }
294
295 /**
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800296 * This Handler is used to open the camera.
297 */
298 private class OpenCameraHandler extends Handler {
299 public OpenCameraHandler(Looper looper) {
300 super(looper);
301 }
302
303 @Override
304 public void handleMessage(Message msg) {
305 switch (msg.what) {
306 case OPEN_CAMERA_ASYNC: {
307 // Prevent closeCamera from thinking the camera
308 // is already closed when it is still opening.
309 //
310 // This happens during the lockscreen sequence:
311 // onResume -> onPause -> onResume.
312 synchronized (mCameraOpenLock) {
313 Log.v(TAG, "openCamera");
314 if (mCameraDevice != null) {
315 throw new IllegalArgumentException("Camera already open.");
316 }
317
318 mCameraDevice = CameraUtil.openCamera(
319 mActivity, mCameraId, mHandler,
320 mActivity.getCameraOpenErrorCallback());
321
322 if (mCameraDevice == null) {
323 Log.e(TAG, "Failed to open camera:" + mCameraId);
324 break;
325 }
326 mParameters = mCameraDevice.getParameters();
327
328 initializeCapabilities();
329 if (mFocusManager == null) {
330 initializeFocusManager();
331 }
332
333 // The views can't be updated from a non UI thread.
334 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
335
336 setCameraParameters(UPDATE_PARAM_ALL);
337 mCameraPreviewParamsReady = true;
338
339 // This will exit early if the surface texture
340 // isn't ready. We also need to protect the surface
341 // texture from concurrent updates/checks.
342 startPreview();
343 }
344 break;
345 }
346
347 case START_PREVIEW_ASYNC: {
348 if (mCameraDevice == null) {
349 throw new IllegalStateException("Camera not yet opened.");
350 }
351
352 startPreview();
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800353 break;
354 }
355
356 default: {
357 throw new UnsupportedOperationException("Unknown message " + msg.what);
358 }
359 }
360 };
361 }
362
363 /**
Michael Kolb8872c232013-01-29 10:33:22 -0800364 * This Handler is used to post message back onto the main thread of the
365 * application
366 */
367 private class MainHandler extends Handler {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800368 public MainHandler() {
369 super(Looper.getMainLooper());
370 }
371
Michael Kolb8872c232013-01-29 10:33:22 -0800372 @Override
373 public void handleMessage(Message msg) {
374 switch (msg.what) {
375 case SETUP_PREVIEW: {
376 setupPreview();
377 break;
378 }
379
380 case CLEAR_SCREEN_DELAY: {
381 mActivity.getWindow().clearFlags(
382 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
383 break;
384 }
385
386 case FIRST_TIME_INIT: {
387 initializeFirstTime();
388 break;
389 }
390
391 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
392 setCameraParametersWhenIdle(0);
393 break;
394 }
395
Michael Kolb8872c232013-01-29 10:33:22 -0800396 case SHOW_TAP_TO_FOCUS_TOAST: {
397 showTapToFocusToast();
398 break;
399 }
400
401 case SWITCH_CAMERA: {
402 switchCamera();
403 break;
404 }
405
406 case SWITCH_CAMERA_START_ANIMATION: {
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700407 // TODO: Need to revisit
408 // ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -0800409 break;
410 }
411
412 case CAMERA_OPEN_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800413 onCameraOpened();
Michael Kolb8872c232013-01-29 10:33:22 -0800414 break;
415 }
416
Michael Kolb8872c232013-01-29 10:33:22 -0800417 case OPEN_CAMERA_FAIL: {
Michael Kolb8872c232013-01-29 10:33:22 -0800418 mOpenCameraFail = true;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700419 CameraUtil.showErrorAndFinish(mActivity,
Michael Kolb8872c232013-01-29 10:33:22 -0800420 R.string.cannot_connect_camera);
421 break;
422 }
423
424 case CAMERA_DISABLED: {
Michael Kolb8872c232013-01-29 10:33:22 -0800425 mCameraDisabled = true;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700426 CameraUtil.showErrorAndFinish(mActivity,
Michael Kolb8872c232013-01-29 10:33:22 -0800427 R.string.camera_disabled);
428 break;
429 }
ztenghui367c7c82013-10-16 14:43:26 -0700430
431 case SWITCH_TO_GCAM_MODULE: {
432 mActivity.onModuleSelected(ModuleSwitcher.GCAM_MODULE_INDEX);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800433 break;
434 }
435
436 case CAMERA_PREVIEW_DONE: {
437 // Modifies views, so must be executed on the UI thread.
438 onPreviewStarted();
ztenghui367c7c82013-10-16 14:43:26 -0700439 }
Michael Kolb8872c232013-01-29 10:33:22 -0800440 }
441 }
442 }
443
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700444
Michael Kolb8872c232013-01-29 10:33:22 -0800445 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700446 public void init(CameraActivity activity, View parent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800447 mActivity = activity;
Michael Kolbd6954f32013-03-08 20:43:01 -0800448 mUI = new PhotoUI(activity, this, parent);
Michael Kolb8872c232013-01-29 10:33:22 -0800449 mPreferences = new ComboPreferences(mActivity);
450 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
451 mCameraId = getPreferredCameraId(mPreferences);
452
453 mContentResolver = mActivity.getContentResolver();
454
Michael Kolb8872c232013-01-29 10:33:22 -0800455 // Surface texture is from camera screen nail and startPreview needs it.
456 // This must be done before startPreview.
457 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800458
459 mPreferences.setLocalId(mActivity, mCameraId);
460 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
461 // we need to reset exposure for the preview
462 resetExposureCompensation();
Michael Kolb8872c232013-01-29 10:33:22 -0800463
464 initializeControlByIntent();
465 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Michael Kolbd6954f32013-03-08 20:43:01 -0800466 mLocationManager = new LocationManager(mActivity, mUI);
Angus Kong0d00a892013-03-26 11:40:40 -0700467 mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
Michael Kolbd6954f32013-03-08 20:43:01 -0800468 }
469
470 private void initializeControlByIntent() {
471 mUI.initializeControlByIntent();
472 if (mIsImageCaptureIntent) {
473 setupCaptureParams();
474 }
475 }
476
477 private void onPreviewStarted() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800478 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800479 startFaceDetection();
480 locationFirstRun();
Michael Kolb8872c232013-01-29 10:33:22 -0800481 }
482
483 // Prompt the user to pick to record location for the very first run of
484 // camera only
485 private void locationFirstRun() {
486 if (RecordLocationPreference.isSet(mPreferences)) {
487 return;
488 }
489 if (mActivity.isSecureCamera()) return;
490 // Check if the back camera exists
491 int backCameraId = CameraHolder.instance().getBackCameraId();
492 if (backCameraId == -1) {
493 // If there is no back camera, do not show the prompt.
494 return;
495 }
Doris Liu6a83d522013-07-02 12:03:32 -0700496 mUI.showLocationDialog();
497 }
Michael Kolb8872c232013-01-29 10:33:22 -0800498
ztenghui7b265a62013-09-09 14:58:44 -0700499 @Override
Doris Liu6a83d522013-07-02 12:03:32 -0700500 public void enableRecordingLocation(boolean enable) {
501 setLocationPreference(enable ? RecordLocationPreference.VALUE_ON
502 : RecordLocationPreference.VALUE_OFF);
Michael Kolb8872c232013-01-29 10:33:22 -0800503 }
504
Angus Kongdcccc512013-08-08 17:06:03 -0700505 @Override
506 public void onPreviewUIReady() {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800507 // Requires that OPEN_CAMERA_ASYNC has been already sent.
508 mOpenCameraHandler.sendEmptyMessage(START_PREVIEW_ASYNC);
Angus Kongdcccc512013-08-08 17:06:03 -0700509 }
510
511 @Override
512 public void onPreviewUIDestroyed() {
513 if (mCameraDevice == null) {
514 return;
515 }
516 mCameraDevice.setPreviewTexture(null);
517 stopPreview();
518 }
519
Michael Kolb8872c232013-01-29 10:33:22 -0800520 private void setLocationPreference(String value) {
521 mPreferences.edit()
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700522 .putString(CameraSettings.KEY_RECORD_LOCATION, value)
523 .apply();
Michael Kolb8872c232013-01-29 10:33:22 -0800524 // TODO: Fix this to use the actual onSharedPreferencesChanged listener
525 // instead of invoking manually
526 onSharedPreferenceChanged();
527 }
528
Michael Kolbd6954f32013-03-08 20:43:01 -0800529 private void onCameraOpened() {
530 View root = mUI.getRootView();
Michael Kolb8872c232013-01-29 10:33:22 -0800531 // These depend on camera parameters.
Michael Kolbd6954f32013-03-08 20:43:01 -0800532
533 int width = root.getWidth();
534 int height = root.getHeight();
Doris Liu6a0de792013-02-26 10:54:25 -0800535 mFocusManager.setPreviewSize(width, height);
Michael Kolbd6954f32013-03-08 20:43:01 -0800536 openCameraCommon();
Michael Kolb8872c232013-01-29 10:33:22 -0800537 }
538
Michael Kolbd6954f32013-03-08 20:43:01 -0800539 private void switchCamera() {
540 if (mPaused) return;
541
542 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
543 mCameraId = mPendingSwitchCameraId;
544 mPendingSwitchCameraId = -1;
545 setCameraId(mCameraId);
546
547 // from onPause
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800548 mOpenCameraHandler.removeMessages(OPEN_CAMERA_ASYNC);
549 mOpenCameraHandler.removeMessages(START_PREVIEW_ASYNC);
550 synchronized (mCameraOpenLock) {
551 closeCamera();
552 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800553 mUI.collapseCameraControls();
554 mUI.clearFaces();
555 if (mFocusManager != null) mFocusManager.removeMessages();
556
557 // Restart the camera and initialize the UI. From onCreate.
558 mPreferences.setLocalId(mActivity, mCameraId);
559 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800560 synchronized (mCameraOpenLock) {
561 Log.v(TAG, "openCamera");
562 mCameraDevice = CameraUtil.openCamera(
Angus Kong4f795b82013-09-16 14:25:35 -0700563 mActivity, mCameraId, mHandler,
564 mActivity.getCameraOpenErrorCallback());
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800565 }
Angus Kong4f795b82013-09-16 14:25:35 -0700566 if (mCameraDevice == null) {
567 Log.e(TAG, "Failed to open camera:" + mCameraId + ", aborting.");
Michael Kolbd6954f32013-03-08 20:43:01 -0800568 return;
Doris Liu09106a42013-03-05 09:54:25 -0800569 }
Angus Kong4f795b82013-09-16 14:25:35 -0700570 mParameters = mCameraDevice.getParameters();
Michael Kolbd6954f32013-03-08 20:43:01 -0800571 initializeCapabilities();
572 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Doris Liu29da2db2013-08-28 14:28:45 -0700573 mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
574 mFocusManager.setMirror(mMirror);
Michael Kolbd6954f32013-03-08 20:43:01 -0800575 mFocusManager.setParameters(mInitialParams);
576 setupPreview();
577
Doris Liu6432cd62013-06-13 17:20:31 -0700578 // reset zoom value index
579 mZoomValue = 0;
Michael Kolbd6954f32013-03-08 20:43:01 -0800580 openCameraCommon();
581
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700582 // Start switch camera animation. Post a message because
583 // onFrameAvailable from the old camera may already exist.
584 mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
Doris Liu48239f42013-03-04 22:19:10 -0800585 }
586
Michael Kolbd6954f32013-03-08 20:43:01 -0800587 protected void setCameraId(int cameraId) {
588 ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
589 pref.setValue("" + cameraId);
590 }
591
592 // either open a new camera or switch cameras
593 private void openCameraCommon() {
Michael Kolb8872c232013-01-29 10:33:22 -0800594 loadCameraPreferences();
Michael Kolbd6954f32013-03-08 20:43:01 -0800595
596 mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
Angus Kong0fb819b2013-10-08 13:44:19 -0700597 if (mIsImageCaptureIntent) {
598 mUI.overrideSettings(CameraSettings.KEY_CAMERA_HDR_PLUS,
599 mActivity.getString(R.string.setting_off_value));
600 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800601 updateSceneMode();
602 showTapToFocusToastIfNeeded();
603
604
Michael Kolb8872c232013-01-29 10:33:22 -0800605 }
606
ztenghui7b265a62013-09-09 14:58:44 -0700607 @Override
Doris Liu36ebcb12013-10-28 14:44:24 -0700608 public void onPreviewRectChanged(Rect previewRect) {
609 if (mFocusManager != null) mFocusManager.setPreviewRect(previewRect);
Michael Kolbd6954f32013-03-08 20:43:01 -0800610 }
Michael Kolb8872c232013-01-29 10:33:22 -0800611
612 private void resetExposureCompensation() {
613 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
614 CameraSettings.EXPOSURE_DEFAULT_VALUE);
615 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
616 Editor editor = mPreferences.edit();
617 editor.putString(CameraSettings.KEY_EXPOSURE, "0");
618 editor.apply();
619 }
620 }
621
622 private void keepMediaProviderInstance() {
623 // We want to keep a reference to MediaProvider in camera's lifecycle.
624 // TODO: Utilize mMediaProviderClient instance to replace
625 // ContentResolver calls.
626 if (mMediaProviderClient == null) {
627 mMediaProviderClient = mContentResolver
628 .acquireContentProviderClient(MediaStore.AUTHORITY);
629 }
630 }
631
632 // Snapshots can only be taken after this is called. It should be called
633 // once only. We could have done these things in onCreate() but we want to
634 // make preview screen appear as soon as possible.
635 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700636 if (mFirstTimeInitialized || mPaused) {
637 return;
638 }
Michael Kolb8872c232013-01-29 10:33:22 -0800639
640 // Initialize location service.
641 boolean recordLocation = RecordLocationPreference.get(
642 mPreferences, mContentResolver);
643 mLocationManager.recordLocation(recordLocation);
644
645 keepMediaProviderInstance();
646
Michael Kolbd6954f32013-03-08 20:43:01 -0800647 mUI.initializeFirstTime();
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800648 MediaSaver s = mActivity.getMediaSaver();
Angus Kong86d36312013-01-31 18:22:44 -0800649 // We set the listener only when both service and shutterbutton
650 // are initialized.
651 if (s != null) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800652 s.setQueueListener(this);
Angus Kong86d36312013-01-31 18:22:44 -0800653 }
Michael Kolb8872c232013-01-29 10:33:22 -0800654
Michael Kolb8872c232013-01-29 10:33:22 -0800655 mNamedImages = new NamedImages();
656
657 mFirstTimeInitialized = true;
658 addIdleHandler();
659
660 mActivity.updateStorageSpaceAndHint();
661 }
662
Michael Kolbd6954f32013-03-08 20:43:01 -0800663 // If the activity is paused and resumed, this method will be called in
664 // onResume.
665 private void initializeSecondTime() {
666 // Start location update if needed.
667 boolean recordLocation = RecordLocationPreference.get(
668 mPreferences, mContentResolver);
669 mLocationManager.recordLocation(recordLocation);
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800670 MediaSaver s = mActivity.getMediaSaver();
Michael Kolbd6954f32013-03-08 20:43:01 -0800671 if (s != null) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800672 s.setQueueListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800673 }
674 mNamedImages = new NamedImages();
675 mUI.initializeSecondTime(mParameters);
676 keepMediaProviderInstance();
677 }
678
Michael Kolb8872c232013-01-29 10:33:22 -0800679 private void showTapToFocusToastIfNeeded() {
680 // Show the tap to focus toast if this is the first start.
681 if (mFocusAreaSupported &&
682 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
683 // Delay the toast for one second to wait for orientation.
684 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
685 }
686 }
687
688 private void addIdleHandler() {
689 MessageQueue queue = Looper.myQueue();
690 queue.addIdleHandler(new MessageQueue.IdleHandler() {
691 @Override
692 public boolean queueIdle() {
693 Storage.ensureOSXCompatible();
694 return false;
695 }
696 });
697 }
698
Michael Kolb8872c232013-01-29 10:33:22 -0800699 @Override
700 public void startFaceDetection() {
Michael Kolb8872c232013-01-29 10:33:22 -0800701 if (mFaceDetectionStarted) return;
702 if (mParameters.getMaxNumDetectedFaces() > 0) {
703 mFaceDetectionStarted = true;
Michael Kolb8872c232013-01-29 10:33:22 -0800704 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Michael Kolbd6954f32013-03-08 20:43:01 -0800705 mUI.onStartFaceDetection(mDisplayOrientation,
706 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
Angus Kong9e765522013-07-31 14:05:20 -0700707 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800708 mCameraDevice.startFaceDetection();
709 }
710 }
711
Michael Kolb8872c232013-01-29 10:33:22 -0800712 @Override
713 public void stopFaceDetection() {
Michael Kolb8872c232013-01-29 10:33:22 -0800714 if (!mFaceDetectionStarted) return;
715 if (mParameters.getMaxNumDetectedFaces() > 0) {
716 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700717 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800718 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800719 mUI.clearFaces();
Michael Kolb8872c232013-01-29 10:33:22 -0800720 }
721 }
722
Michael Kolb8872c232013-01-29 10:33:22 -0800723 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700724 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700725
Sascha Haeberling37f36112013-08-06 14:31:52 -0700726 private boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700727
Sascha Haeberling37f36112013-08-06 14:31:52 -0700728 public ShutterCallback(boolean needsAnimation) {
729 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700730 }
731
Michael Kolb8872c232013-01-29 10:33:22 -0800732 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700733 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800734 mShutterCallbackTime = System.currentTimeMillis();
735 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
736 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700737 if (mNeedsAnimation) {
738 mActivity.runOnUiThread(new Runnable() {
739 @Override
740 public void run() {
741 animateAfterShutter();
742 }
743 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700744 }
Michael Kolb8872c232013-01-29 10:33:22 -0800745 }
746 }
747
Angus Kong9ef99252013-07-18 18:04:19 -0700748 private final class PostViewPictureCallback
749 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800750 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700751 public void onPictureTaken(byte [] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800752 mPostViewPictureCallbackTime = System.currentTimeMillis();
753 Log.v(TAG, "mShutterToPostViewCallbackTime = "
754 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
755 + "ms");
756 }
757 }
758
Angus Kong9ef99252013-07-18 18:04:19 -0700759 private final class RawPictureCallback
760 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800761 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700762 public void onPictureTaken(byte [] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800763 mRawPictureCallbackTime = System.currentTimeMillis();
764 Log.v(TAG, "mShutterToRawCallbackTime = "
765 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
766 }
767 }
768
Angus Kong9ef99252013-07-18 18:04:19 -0700769 private final class JpegPictureCallback
770 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800771 Location mLocation;
772
773 public JpegPictureCallback(Location loc) {
774 mLocation = loc;
775 }
776
777 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700778 public void onPictureTaken(final byte [] jpegData, CameraProxy camera) {
Sascha Haeberling88901942013-08-28 17:49:00 -0700779 mUI.enableShutter(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800780 if (mPaused) {
781 return;
782 }
Doris Liu6432cd62013-06-13 17:20:31 -0700783 if (mIsImageCaptureIntent) {
784 stopPreview();
785 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700786 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700787 mUI.showSwitcher();
788 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800789 }
790
791 mJpegPictureCallbackTime = System.currentTimeMillis();
792 // If postview callback has arrived, the captured image is displayed
793 // in postview callback. If not, the captured image is displayed in
794 // raw picture callback.
795 if (mPostViewPictureCallbackTime != 0) {
796 mShutterToPictureDisplayedTime =
797 mPostViewPictureCallbackTime - mShutterCallbackTime;
798 mPictureDisplayedToJpegCallbackTime =
799 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
800 } else {
801 mShutterToPictureDisplayedTime =
802 mRawPictureCallbackTime - mShutterCallbackTime;
803 mPictureDisplayedToJpegCallbackTime =
804 mJpegPictureCallbackTime - mRawPictureCallbackTime;
805 }
806 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
807 + mPictureDisplayedToJpegCallbackTime + "ms");
808
Michael Kolb8872c232013-01-29 10:33:22 -0800809 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
810 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700811 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800812 }
813
Doris Liu36e56fb2013-09-11 17:38:08 -0700814 ExifInterface exif = Exif.getExif(jpegData);
815 int orientation = Exif.getOrientation(exif);
Ruben Brunkd217ed02013-10-08 23:31:13 -0700816
Ruben Brunkd7488272013-10-10 18:45:53 -0700817 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800818 // Calculate the width and the height of the jpeg.
819 Size s = mParameters.getPictureSize();
Michael Kolb8872c232013-01-29 10:33:22 -0800820 int width, height;
821 if ((mJpegRotation + orientation) % 180 == 0) {
822 width = s.width;
823 height = s.height;
824 } else {
825 width = s.height;
826 height = s.width;
827 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700828 NamedEntity name = mNamedImages.getNextNameEntity();
829 String title = (name == null) ? null : name.title;
830 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -0700831
832 // Handle debug mode outputs
833 if (mDebugUri != null) {
834 // If using a debug uri, save jpeg there.
835 saveToDebugUri(jpegData);
836
837 // Adjust the title of the debug image shown in mediastore.
838 if (title != null) {
839 title = DEBUG_IMAGE_PREFIX + title;
840 }
841 }
842
Michael Kolb8872c232013-01-29 10:33:22 -0800843 if (title == null) {
844 Log.e(TAG, "Unbalanced name/data pair");
845 } else {
846 if (date == -1) date = mCaptureStartTime;
Angus Kong0d00a892013-03-26 11:40:40 -0700847 if (mHeading >= 0) {
848 // heading direction has been updated by the sensor.
849 ExifTag directionRefTag = exif.buildTag(
850 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
851 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
852 ExifTag directionTag = exif.buildTag(
853 ExifInterface.TAG_GPS_IMG_DIRECTION,
854 new Rational(mHeading, 1));
855 exif.setTag(directionRefTag);
856 exif.setTag(directionTag);
857 }
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800858 mActivity.getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -0800859 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700860 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800861 }
Doris Liuce2acbc2013-08-21 18:45:29 -0700862 // Animate capture with real jpeg data instead of a preview frame.
Doris Liu29da2db2013-08-28 14:28:45 -0700863 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800864 } else {
865 mJpegImageData = jpegData;
866 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -0700867 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800868 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800869 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800870 }
871 }
872
873 // Check this in advance of each shot so we don't add to shutter
874 // latency. It's true that someone else could write to the SD card in
875 // the mean time and fill it, but that could have happened between the
876 // shutter press and saving the JPEG too.
877 mActivity.updateStorageSpaceAndHint();
878
879 long now = System.currentTimeMillis();
880 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
881 Log.v(TAG, "mJpegCallbackFinishTime = "
882 + mJpegCallbackFinishTime + "ms");
883 mJpegPictureCallbackTime = 0;
884 }
885 }
886
Angus Kong9ef99252013-07-18 18:04:19 -0700887 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800888 @Override
889 public void onAutoFocus(
Angus Kong9ef99252013-07-18 18:04:19 -0700890 boolean focused, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800891 if (mPaused) return;
892
893 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
894 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
895 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800896 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb8872c232013-01-29 10:33:22 -0800897 }
898 }
899
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700900 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -0800901 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700902 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800903 @Override
904 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700905 boolean moving, CameraProxy camera) {
906 mFocusManager.onAutoFocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -0800907 }
908 }
909
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700910 /**
911 * This class is just a thread-safe queue for name,date holder objects.
912 */
913 public static class NamedImages {
914 private Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -0800915
916 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700917 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -0800918 }
919
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700920 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -0800921 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700922 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -0800923 r.date = date;
924 mQueue.add(r);
925 }
926
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700927 public NamedEntity getNextNameEntity() {
928 synchronized(mQueue) {
929 if (!mQueue.isEmpty()) {
930 return mQueue.remove(0);
931 }
Michael Kolb8872c232013-01-29 10:33:22 -0800932 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700933 return null;
Michael Kolb8872c232013-01-29 10:33:22 -0800934 }
935
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700936 public static class NamedEntity {
937 public String title;
938 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -0800939 }
940 }
941
942 private void setCameraState(int state) {
943 mCameraState = state;
944 switch (state) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700945 case PhotoController.PREVIEW_STOPPED:
946 case PhotoController.SNAPSHOT_IN_PROGRESS:
947 case PhotoController.SWITCHING_CAMERA:
948 mUI.enableGestures(false);
949 break;
950 case PhotoController.IDLE:
951 mUI.enableGestures(true);
952 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800953 }
954 }
955
Sascha Haeberling37f36112013-08-06 14:31:52 -0700956 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -0800957 // Only animate when in full screen capture mode
958 // i.e. If monkey/a user swipes to the gallery during picture taking,
959 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -0700960 if (!mIsImageCaptureIntent) {
961 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -0700962 }
Michael Kolb8872c232013-01-29 10:33:22 -0800963 }
964
965 @Override
966 public boolean capture() {
967 // If we are already in the middle of taking a snapshot or the image save request
968 // is full then ignore.
969 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Angus Kong86d36312013-01-31 18:22:44 -0800970 || mCameraState == SWITCHING_CAMERA
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800971 || mActivity.getMediaSaver() == null
972 || mActivity.getMediaSaver().isQueueFull()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800973 return false;
974 }
975 mCaptureStartTime = System.currentTimeMillis();
976 mPostViewPictureCallbackTime = 0;
977 mJpegImageData = null;
978
Angus Kongb50b5cb2013-08-09 14:55:20 -0700979 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -0800980
981 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700982 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -0800983 }
984
985 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800986 int orientation;
987 // We need to be consistent with the framework orientation (i.e. the
988 // orientation of the UI.) when the auto-rotate screen setting is on.
989 if (mActivity.isAutoRotateScreen()) {
990 orientation = (360 - mDisplayRotation) % 360;
991 } else {
992 orientation = mOrientation;
993 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700994 mJpegRotation = CameraUtil.getJpegRotation(mCameraId, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800995 mParameters.setRotation(mJpegRotation);
996 Location loc = mLocationManager.getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700997 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -0800998 mCameraDevice.setParameters(mParameters);
999
Sascha Haeberling88901942013-08-28 17:49:00 -07001000 // We don't want user to press the button again while taking a
1001 // multi-second HDR photo.
1002 mUI.enableShutter(false);
Angus Kong9ef99252013-07-18 18:04:19 -07001003 mCameraDevice.takePicture(mHandler,
1004 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001005 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001006 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001007
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001008 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001009
1010 mFaceDetectionStarted = false;
1011 setCameraState(SNAPSHOT_IN_PROGRESS);
Bobby Georgescu301b6462013-04-01 15:33:17 -07001012 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1013 UsageStatistics.ACTION_CAPTURE_DONE, "Photo");
Michael Kolb8872c232013-01-29 10:33:22 -08001014 return true;
1015 }
1016
1017 @Override
1018 public void setFocusParameters() {
1019 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1020 }
1021
1022 private int getPreferredCameraId(ComboPreferences preferences) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001023 int intentCameraId = CameraUtil.getCameraFacingIntentExtras(mActivity);
Michael Kolb8872c232013-01-29 10:33:22 -08001024 if (intentCameraId != -1) {
1025 // Testing purpose. Launch a specific camera through the intent
1026 // extras.
1027 return intentCameraId;
1028 } else {
1029 return CameraSettings.readPreferredCameraId(preferences);
1030 }
1031 }
1032
Michael Kolbd6954f32013-03-08 20:43:01 -08001033 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001034 // If scene mode is set, we cannot set flash mode, white balance, and
1035 // focus mode, instead, we read it from driver
1036 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1037 overrideCameraSettings(mParameters.getFlashMode(),
1038 mParameters.getWhiteBalance(), mParameters.getFocusMode());
1039 } else {
1040 overrideCameraSettings(null, null, null);
1041 }
1042 }
1043
1044 private void overrideCameraSettings(final String flashMode,
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001045 final String whiteBalance, final String focusMode) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001046 mUI.overrideSettings(
1047 CameraSettings.KEY_FLASH_MODE, flashMode,
1048 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
1049 CameraSettings.KEY_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001050 }
1051
1052 private void loadCameraPreferences() {
1053 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
1054 mCameraId, CameraHolder.instance().getCameraInfo());
1055 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
1056 }
1057
1058 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001059 public void onOrientationChanged(int orientation) {
1060 // We keep the last known orientation. So if the user first orient
1061 // the camera then point the camera to floor or sky, we still have
1062 // the correct orientation.
1063 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
Angus Kongb50b5cb2013-08-09 14:55:20 -07001064 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001065
1066 // Show the toast after getting the first orientation changed.
1067 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1068 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1069 showTapToFocusToast();
1070 }
1071 }
1072
1073 @Override
1074 public void onStop() {
1075 if (mMediaProviderClient != null) {
1076 mMediaProviderClient.release();
1077 mMediaProviderClient = null;
1078 }
1079 }
1080
Michael Kolbd6954f32013-03-08 20:43:01 -08001081 @Override
1082 public void onCaptureCancelled() {
1083 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1084 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001085 }
1086
Michael Kolbd6954f32013-03-08 20:43:01 -08001087 @Override
1088 public void onCaptureRetake() {
Michael Kolb8872c232013-01-29 10:33:22 -08001089 if (mPaused)
1090 return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001091 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001092 setupPreview();
1093 }
1094
Michael Kolbd6954f32013-03-08 20:43:01 -08001095 @Override
1096 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001097 if (mPaused) {
1098 return;
1099 }
1100
1101 byte[] data = mJpegImageData;
1102
1103 if (mCropValue == null) {
1104 // First handle the no crop case -- just return the value. If the
1105 // caller specifies a "save uri" then write the data to its
1106 // stream. Otherwise, pass back a scaled down version of the bitmap
1107 // directly in the extras.
1108 if (mSaveUri != null) {
1109 OutputStream outputStream = null;
1110 try {
1111 outputStream = mContentResolver.openOutputStream(mSaveUri);
1112 outputStream.write(data);
1113 outputStream.close();
1114
1115 mActivity.setResultEx(Activity.RESULT_OK);
1116 mActivity.finish();
1117 } catch (IOException ex) {
1118 // ignore exception
1119 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001120 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001121 }
1122 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001123 ExifInterface exif = Exif.getExif(data);
1124 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001125 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1126 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001127 mActivity.setResultEx(Activity.RESULT_OK,
1128 new Intent("inline-data").putExtra("data", bitmap));
1129 mActivity.finish();
1130 }
1131 } else {
1132 // Save the image to a temp file and invoke the cropper
1133 Uri tempUri = null;
1134 FileOutputStream tempStream = null;
1135 try {
1136 File path = mActivity.getFileStreamPath(sTempCropFilename);
1137 path.delete();
1138 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1139 tempStream.write(data);
1140 tempStream.close();
1141 tempUri = Uri.fromFile(path);
1142 } catch (FileNotFoundException ex) {
1143 mActivity.setResultEx(Activity.RESULT_CANCELED);
1144 mActivity.finish();
1145 return;
1146 } catch (IOException ex) {
1147 mActivity.setResultEx(Activity.RESULT_CANCELED);
1148 mActivity.finish();
1149 return;
1150 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001151 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001152 }
1153
1154 Bundle newExtras = new Bundle();
1155 if (mCropValue.equals("circle")) {
1156 newExtras.putString("circleCrop", "true");
1157 }
1158 if (mSaveUri != null) {
1159 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1160 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001161 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001162 }
1163 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001164 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001165 }
1166
Sascha Haeberling37f36112013-08-06 14:31:52 -07001167 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001168 final String CROP_ACTION = "com.android.camera.action.CROP";
1169 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001170
1171 cropIntent.setData(tempUri);
1172 cropIntent.putExtras(newExtras);
1173
1174 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1175 }
1176 }
1177
Michael Kolb8872c232013-01-29 10:33:22 -08001178 @Override
1179 public void onShutterButtonFocus(boolean pressed) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001180 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001181 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1182 || (mCameraState == PREVIEW_STOPPED)) return;
1183
1184 // Do not do focus if there is not enough storage.
1185 if (pressed && !canTakePicture()) return;
1186
1187 if (pressed) {
Michael Kolb8872c232013-01-29 10:33:22 -08001188 mFocusManager.onShutterDown();
1189 } else {
Doris Liuda50e052013-02-07 14:36:32 -08001190 // for countdown mode, we need to postpone the shutter release
1191 // i.e. lock the focus during countdown.
Michael Kolbd6954f32013-03-08 20:43:01 -08001192 if (!mUI.isCountingDown()) {
Doris Liuda50e052013-02-07 14:36:32 -08001193 mFocusManager.onShutterUp();
1194 }
Michael Kolb8872c232013-01-29 10:33:22 -08001195 }
1196 }
1197
1198 @Override
1199 public void onShutterButtonClick() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001200 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001201 || (mCameraState == SWITCHING_CAMERA)
1202 || (mCameraState == PREVIEW_STOPPED)) return;
1203
1204 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001205 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001206 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001207 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001208 return;
1209 }
1210 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1211
Angus Kongb50b5cb2013-08-09 14:55:20 -07001212 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001213 mUI.hideSwitcher();
1214 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001215 }
Michael Kolb8872c232013-01-29 10:33:22 -08001216 // If the user wants to do a snapshot while the previous one is still
1217 // in progress, remember the fact and do it after we finish the previous
1218 // one and re-start the preview. Snapshot in progress also includes the
1219 // state that autofocus is focusing and a picture will be taken when
1220 // focus callback arrives.
1221 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1222 && !mIsImageCaptureIntent) {
1223 mSnapshotOnIdle = true;
1224 return;
1225 }
1226
1227 String timer = mPreferences.getString(
1228 CameraSettings.KEY_TIMER,
1229 mActivity.getString(R.string.pref_camera_timer_default));
1230 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1231 mActivity.getString(R.string.pref_camera_timer_sound_default))
1232 .equals(mActivity.getString(R.string.setting_on_value));
1233
1234 int seconds = Integer.parseInt(timer);
1235 // When shutter button is pressed, check whether the previous countdown is
1236 // finished. If not, cancel the previous countdown and start a new one.
Michael Kolbd6954f32013-03-08 20:43:01 -08001237 if (mUI.isCountingDown()) {
1238 mUI.cancelCountDown();
1239 }
1240 if (seconds > 0) {
1241 mUI.startCountDown(seconds, playSound);
Michael Kolb8872c232013-01-29 10:33:22 -08001242 } else {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001243 mSnapshotOnIdle = false;
1244 mFocusManager.doSnap();
Michael Kolb8872c232013-01-29 10:33:22 -08001245 }
1246 }
1247
1248 @Override
1249 public void installIntentFilter() {
Sascha Haeberling4de78802013-10-06 18:07:07 -07001250 // Do nothing.
Michael Kolb8872c232013-01-29 10:33:22 -08001251 }
1252
1253 @Override
1254 public boolean updateStorageHintOnResume() {
1255 return mFirstTimeInitialized;
1256 }
1257
1258 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001259 public void onResumeBeforeSuper() {
1260 mPaused = false;
1261 }
1262
1263 @Override
1264 public void onResumeAfterSuper() {
Doris Liu3a45c332013-10-15 19:10:28 -07001265 Log.v(TAG, "On resume.");
Michael Kolb8872c232013-01-29 10:33:22 -08001266 if (mOpenCameraFail || mCameraDisabled) return;
1267
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001268 if (mOpenCameraThread == null) {
1269 Log.e("DEBUG", "new OpenCameraThread");
1270 mOpenCameraThread = new HandlerThread("OpenCameraThread");
1271 mOpenCameraThread.start();
1272 mOpenCameraHandler = new OpenCameraHandler(mOpenCameraThread.getLooper());
1273 }
1274
Michael Kolb8872c232013-01-29 10:33:22 -08001275 mJpegPictureCallbackTime = 0;
1276 mZoomValue = 0;
Angus Kongdcccc512013-08-08 17:06:03 -07001277 resetExposureCompensation();
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001278
1279 mOpenCameraHandler.sendEmptyMessage(OPEN_CAMERA_ASYNC);
1280 mOnResumeTime = SystemClock.uptimeMillis();
1281 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001282
1283 // If first time initialization is not finished, put it in the
1284 // message queue.
1285 if (!mFirstTimeInitialized) {
1286 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1287 } else {
1288 initializeSecondTime();
1289 }
Doris Liu59390062013-10-10 17:20:56 -07001290 mUI.initDisplayChangeListener();
Michael Kolb8872c232013-01-29 10:33:22 -08001291 keepScreenOnAwhile();
1292
Bobby Georgescu0a7dd572013-03-12 22:45:17 -07001293 UsageStatistics.onContentViewChanged(
1294 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
Angus Kong0d00a892013-03-26 11:40:40 -07001295
1296 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1297 if (gsensor != null) {
1298 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1299 }
1300
1301 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1302 if (msensor != null) {
1303 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1304 }
Michael Kolb8872c232013-01-29 10:33:22 -08001305 }
1306
Michael Kolb8872c232013-01-29 10:33:22 -08001307 @Override
1308 public void onPauseBeforeSuper() {
1309 mPaused = true;
Angus Kong0d00a892013-03-26 11:40:40 -07001310 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1311 if (gsensor != null) {
1312 mSensorManager.unregisterListener(this, gsensor);
1313 }
1314
1315 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1316 if (msensor != null) {
1317 mSensorManager.unregisterListener(this, msensor);
1318 }
Michael Kolb8872c232013-01-29 10:33:22 -08001319 }
1320
1321 @Override
1322 public void onPauseAfterSuper() {
Doris Liu3a45c332013-10-15 19:10:28 -07001323 Log.v(TAG, "On pause.");
1324 mUI.showPreviewCover();
Michael Kolb8872c232013-01-29 10:33:22 -08001325 // When camera is started from secure lock screen for the first time
1326 // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1327 // To reduce the latency, keep the camera for a short time so it does
1328 // not need to be opened again.
1329 if (mCameraDevice != null && mActivity.isSecureCamera()
Angus Kong6a8e8a12013-07-19 14:55:07 -07001330 && CameraActivity.isFirstStartAfterScreenOn()) {
1331 CameraActivity.resetFirstStartAfterScreenOn();
Michael Kolb8872c232013-01-29 10:33:22 -08001332 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1333 }
1334 // Reset the focus first. Camera CTS does not guarantee that
1335 // cancelAutoFocus is allowed after preview stops.
1336 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1337 mCameraDevice.cancelAutoFocus();
1338 }
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001339 // If the camera has not been opened asynchronously yet,
1340 // and startPreview hasn't been called, then this is a no-op.
1341 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001342 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001343
Angus Kongce5480e2013-01-29 17:43:48 -08001344 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001345
1346 if (mLocationManager != null) mLocationManager.recordLocation(false);
1347
1348 // If we are in an image capture intent and has taken
1349 // a picture, we just clear it in onPause.
1350 mJpegImageData = null;
1351
Angus Kongdcccc512013-08-08 17:06:03 -07001352 // Remove the messages and runnables in the queue.
1353 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001354
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001355 // Postpones actually releasing for KEEP_CAMERA_TIMEOUT,
1356 // so if onResume is directly called after this, the camera
1357 // simply needs to reconnect (takes about 2-5ms).
1358 mOpenCameraHandler.removeMessages(OPEN_CAMERA_ASYNC);
1359 mOpenCameraHandler.removeMessages(START_PREVIEW_ASYNC);
1360 synchronized (mCameraOpenLock) {
1361 closeCamera();
1362 }
1363 // Stop the long running open camera thread.
1364 mOpenCameraThread.quitSafely();
1365 mOpenCameraThread = null;
1366 Log.e(TAG, "Done quiting safely.");
Michael Kolbd6954f32013-03-08 20:43:01 -08001367
1368 resetScreenOn();
1369 mUI.onPause();
1370
Michael Kolb8872c232013-01-29 10:33:22 -08001371 mPendingSwitchCameraId = -1;
1372 if (mFocusManager != null) mFocusManager.removeMessages();
Angus Kongfd4fc0e2013-11-07 15:38:09 -08001373 MediaSaver s = mActivity.getMediaSaver();
Angus Kong86d36312013-01-31 18:22:44 -08001374 if (s != null) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -08001375 s.setQueueListener(null);
Angus Kong86d36312013-01-31 18:22:44 -08001376 }
Doris Liu59390062013-10-10 17:20:56 -07001377 mUI.removeDisplayChangeListener();
Michael Kolb8872c232013-01-29 10:33:22 -08001378 }
1379
Michael Kolb8872c232013-01-29 10:33:22 -08001380 /**
1381 * The focus manager is the first UI related element to get initialized,
1382 * and it requires the RenderOverlay, so initialize it here
1383 */
1384 private void initializeFocusManager() {
1385 // Create FocusManager object. startPreview needs it.
Michael Kolb8872c232013-01-29 10:33:22 -08001386 // if mFocusManager not null, reuse it
1387 // otherwise create a new instance
1388 if (mFocusManager != null) {
1389 mFocusManager.removeMessages();
1390 } else {
1391 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Doris Liu29da2db2013-08-28 14:28:45 -07001392 mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
Michael Kolb8872c232013-01-29 10:33:22 -08001393 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1394 R.array.pref_camera_focusmode_default_array);
1395 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
Doris Liu29da2db2013-08-28 14:28:45 -07001396 mInitialParams, this, mMirror,
Michael Kolbd6954f32013-03-08 20:43:01 -08001397 mActivity.getMainLooper(), mUI);
Michael Kolb8872c232013-01-29 10:33:22 -08001398 }
1399 }
1400
Michael Kolb8872c232013-01-29 10:33:22 -08001401 @Override
1402 public void onConfigurationChanged(Configuration newConfig) {
1403 Log.v(TAG, "onConfigurationChanged");
1404 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001405 }
1406
1407 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001408 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001409 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001410 setDisplayOrientation();
1411 }
1412 }
1413
1414 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001415 public void onActivityResult(
1416 int requestCode, int resultCode, Intent data) {
1417 switch (requestCode) {
1418 case REQUEST_CROP: {
1419 Intent intent = new Intent();
1420 if (data != null) {
1421 Bundle extras = data.getExtras();
1422 if (extras != null) {
1423 intent.putExtras(extras);
1424 }
1425 }
1426 mActivity.setResultEx(resultCode, intent);
1427 mActivity.finish();
1428
1429 File path = mActivity.getFileStreamPath(sTempCropFilename);
1430 path.delete();
1431
1432 break;
1433 }
1434 }
1435 }
1436
1437 private boolean canTakePicture() {
Angus Kong2dcc0a92013-09-25 13:00:08 -07001438 return isCameraIdle() && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001439 }
1440
1441 @Override
1442 public void autoFocus() {
1443 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001444 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001445 setCameraState(FOCUSING);
1446 }
1447
1448 @Override
1449 public void cancelAutoFocus() {
1450 mCameraDevice.cancelAutoFocus();
1451 setCameraState(IDLE);
1452 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1453 }
1454
1455 // Preview area is touched. Handle touch focus.
1456 @Override
1457 public void onSingleTapUp(View view, int x, int y) {
1458 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1459 || mCameraState == SNAPSHOT_IN_PROGRESS
1460 || mCameraState == SWITCHING_CAMERA
1461 || mCameraState == PREVIEW_STOPPED) {
1462 return;
1463 }
1464
Michael Kolb8872c232013-01-29 10:33:22 -08001465 // Check if metering area or focus area is supported.
1466 if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1467 mFocusManager.onSingleTapUp(x, y);
1468 }
1469
1470 @Override
1471 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001472 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001473 }
1474
1475 @Override
1476 public boolean onKeyDown(int keyCode, KeyEvent event) {
1477 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001478 case KeyEvent.KEYCODE_VOLUME_UP:
1479 case KeyEvent.KEYCODE_VOLUME_DOWN:
1480 case KeyEvent.KEYCODE_FOCUS:
1481 if (/*TODO: mActivity.isInCameraApp() &&*/ mFirstTimeInitialized) {
1482 if (event.getRepeatCount() == 0) {
1483 onShutterButtonFocus(true);
1484 }
1485 return true;
1486 }
1487 return false;
1488 case KeyEvent.KEYCODE_CAMERA:
1489 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1490 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001491 }
1492 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001493 case KeyEvent.KEYCODE_DPAD_CENTER:
1494 // If we get a dpad center event without any focused view, move
1495 // the focus to the shutter button and press it.
1496 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1497 // Start auto-focus immediately to reduce shutter lag. After
1498 // the shutter button gets the focus, onShutterButtonFocus()
1499 // will be called again but it is fine.
1500 onShutterButtonFocus(true);
1501 mUI.pressShutterButton();
1502 }
1503 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001504 }
1505 return false;
1506 }
1507
1508 @Override
1509 public boolean onKeyUp(int keyCode, KeyEvent event) {
1510 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001511 case KeyEvent.KEYCODE_VOLUME_UP:
1512 case KeyEvent.KEYCODE_VOLUME_DOWN:
1513 if (/*mActivity.isInCameraApp() && */ mFirstTimeInitialized) {
1514 onShutterButtonClick();
1515 return true;
1516 }
1517 return false;
1518 case KeyEvent.KEYCODE_FOCUS:
1519 if (mFirstTimeInitialized) {
1520 onShutterButtonFocus(false);
1521 }
Michael Kolb8872c232013-01-29 10:33:22 -08001522 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001523 }
1524 return false;
1525 }
1526
Michael Kolb8872c232013-01-29 10:33:22 -08001527 private void closeCamera() {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001528 Log.v(TAG, "closeCamera");
Michael Kolb8872c232013-01-29 10:33:22 -08001529 if (mCameraDevice != null) {
1530 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001531 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001532 mCameraDevice.setErrorCallback(null);
Ruben Brunk59147832013-11-05 15:53:28 -08001533
1534 if (mActivity.isSecureCamera() && !CameraActivity.isFirstStartAfterScreenOn()) {
1535 // Blocks until camera is actually released.
1536 CameraHolder.instance().strongRelease();
1537 } else {
1538 CameraHolder.instance().release();
1539 }
1540
Michael Kolb8872c232013-01-29 10:33:22 -08001541 mFaceDetectionStarted = false;
1542 mCameraDevice = null;
1543 setCameraState(PREVIEW_STOPPED);
1544 mFocusManager.onCameraReleased();
1545 }
1546 }
1547
1548 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001549 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1550 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001551 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001552 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001553 if (mFocusManager != null) {
1554 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1555 }
Doris Liu6432cd62013-06-13 17:20:31 -07001556 // Change the camera display orientation
1557 if (mCameraDevice != null) {
1558 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1559 }
Michael Kolb8872c232013-01-29 10:33:22 -08001560 }
1561
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001562 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001563 private void setupPreview() {
1564 mFocusManager.resetTouchFocus();
1565 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001566 }
1567
Erin Dahlgrenc5effba2013-11-07 11:55:49 -08001568 /** This can run on a background thread, post any view updates to MainHandler. */
Michael Kolb8872c232013-01-29 10:33:22 -08001569 private void startPreview() {
ztenghuicfc148d2013-10-28 15:27:02 -07001570 if (mPaused || mCameraDevice == null) {
Angus Kongdcccc512013-08-08 17:06:03 -07001571 return;
1572 }
Michael Kolb8872c232013-01-29 10:33:22 -08001573
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001574 Object textureLock = mUI.getSurfaceTextureLock();
Michael Kolb8872c232013-01-29 10:33:22 -08001575
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001576 // Any decisions we make based on the surface texture state
1577 // need to be protected.
1578 synchronized (textureLock) {
1579 SurfaceTexture st = mUI.getSurfaceTexture();
1580 if (st == null) {
1581 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
1582 return;
Michael Kolb8872c232013-01-29 10:33:22 -08001583 }
Michael Kolb8872c232013-01-29 10:33:22 -08001584
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001585 if (!mCameraPreviewParamsReady) {
1586 Log.w(TAG, "startPreview: parameters for preview is not ready.");
1587 return;
1588 }
1589 mCameraDevice.setErrorCallback(mErrorCallback);
Erin Dahlgrenc5effba2013-11-07 11:55:49 -08001590 // ICS camera frameworks has a bug. Face detection state is not cleared 1589
1591 // after taking a picture. Stop the preview to work around it. The bug
1592 // was fixed in JB.
1593 if (mCameraState != PREVIEW_STOPPED) {
1594 stopPreview();
1595 }
Michael Kolb8872c232013-01-29 10:33:22 -08001596
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001597 setDisplayOrientation();
1598
1599 if (!mSnapshotOnIdle) {
1600 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1601 // resume it because it may have been paused by autoFocus call.
1602 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
1603 mCameraDevice.cancelAutoFocus();
1604 }
1605 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1606 }
1607 setCameraParameters(UPDATE_PARAM_ALL);
1608 // Let UI set its expected aspect ratio
1609 mCameraDevice.setPreviewTexture(st);
1610
1611 Log.v(TAG, "startPreview");
1612 mCameraDevice.startPreview();
1613
1614 // Since the preview actually started, remove any messages to
1615 // start it again.
1616 mOpenCameraHandler.removeMessages(START_PREVIEW_ASYNC);
1617 mFocusManager.onPreviewStarted();
1618
1619 if (mSnapshotOnIdle) {
1620 mHandler.post(mDoSnapRunnable);
1621 }
Erin Dahlgrenc5effba2013-11-07 11:55:49 -08001622 mHandler.sendEmptyMessage(CAMERA_PREVIEW_DONE);
Michael Kolb8872c232013-01-29 10:33:22 -08001623 }
1624 }
1625
Michael Kolbd6954f32013-03-08 20:43:01 -08001626 @Override
1627 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001628 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1629 Log.v(TAG, "stopPreview");
1630 mCameraDevice.stopPreview();
1631 mFaceDetectionStarted = false;
1632 }
1633 setCameraState(PREVIEW_STOPPED);
1634 if (mFocusManager != null) mFocusManager.onPreviewStopped();
1635 }
1636
1637 @SuppressWarnings("deprecation")
1638 private void updateCameraParametersInitialize() {
1639 // Reset preview frame rate to the maximum because it may be lowered by
1640 // video camera application.
ztenghui16a35202013-09-23 11:35:36 -07001641 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mParameters);
1642 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001643 mParameters.setPreviewFpsRange(
1644 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1645 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001646 }
1647
Angus Kongb50b5cb2013-08-09 14:55:20 -07001648 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001649
1650 // Disable video stabilization. Convenience methods not available in API
1651 // level <= 14
1652 String vstabSupported = mParameters.get("video-stabilization-supported");
1653 if ("true".equals(vstabSupported)) {
1654 mParameters.set("video-stabilization", "false");
1655 }
1656 }
1657
1658 private void updateCameraParametersZoom() {
1659 // Set zoom.
1660 if (mParameters.isZoomSupported()) {
1661 mParameters.setZoom(mZoomValue);
1662 }
1663 }
1664
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001665 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001666 private void setAutoExposureLockIfSupported() {
1667 if (mAeLockSupported) {
1668 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1669 }
1670 }
1671
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001672 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001673 private void setAutoWhiteBalanceLockIfSupported() {
1674 if (mAwbLockSupported) {
1675 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1676 }
1677 }
1678
Michael Kolb8872c232013-01-29 10:33:22 -08001679 private void setFocusAreasIfSupported() {
1680 if (mFocusAreaSupported) {
1681 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1682 }
1683 }
1684
Michael Kolb8872c232013-01-29 10:33:22 -08001685 private void setMeteringAreasIfSupported() {
1686 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001687 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1688 }
1689 }
1690
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001691 private boolean updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08001692 setAutoExposureLockIfSupported();
1693 setAutoWhiteBalanceLockIfSupported();
1694 setFocusAreasIfSupported();
1695 setMeteringAreasIfSupported();
1696
1697 // Set picture size.
1698 String pictureSize = mPreferences.getString(
1699 CameraSettings.KEY_PICTURE_SIZE, null);
1700 if (pictureSize == null) {
1701 CameraSettings.initialCameraPictureSize(mActivity, mParameters);
1702 } else {
1703 List<Size> supported = mParameters.getSupportedPictureSizes();
1704 CameraSettings.setCameraPictureSize(
1705 pictureSize, supported, mParameters);
1706 }
1707 Size size = mParameters.getPictureSize();
1708
1709 // Set a preview size that is closest to the viewfinder height and has
1710 // the right aspect ratio.
1711 List<Size> sizes = mParameters.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001712 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Michael Kolb8872c232013-01-29 10:33:22 -08001713 (double) size.width / size.height);
1714 Size original = mParameters.getPreviewSize();
1715 if (!original.equals(optimalSize)) {
1716 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
Doris Liu6432cd62013-06-13 17:20:31 -07001717
Michael Kolb8872c232013-01-29 10:33:22 -08001718 // Zoom related settings will be changed for different preview
1719 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07001720 if (mHandler.getLooper() == Looper.myLooper()) {
1721 // On UI thread only, not when camera starts up
1722 setupPreview();
1723 } else {
1724 mCameraDevice.setParameters(mParameters);
1725 }
Michael Kolb8872c232013-01-29 10:33:22 -08001726 mParameters = mCameraDevice.getParameters();
1727 }
Doris Liu95405742013-11-05 15:25:26 -08001728
1729 if(optimalSize.width != 0 && optimalSize.height != 0) {
1730 mUI.updatePreviewAspectRatio((float) optimalSize.width
1731 / (float) optimalSize.height);
1732 }
Michael Kolb8872c232013-01-29 10:33:22 -08001733 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1734
1735 // Since changing scene mode may change supported values, set scene mode
1736 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1737 // separate preference.
Sascha Haeberling98f38bb2013-09-24 18:58:34 -07001738 String onValue = mActivity.getString(R.string.setting_on_value);
Michael Kolb8872c232013-01-29 10:33:22 -08001739 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
1740 mActivity.getString(R.string.pref_camera_hdr_default));
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001741 String hdrPlus = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR_PLUS,
1742 mActivity.getString(R.string.pref_camera_hdr_plus_default));
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001743 boolean hdrOn = onValue.equals(hdr);
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001744 boolean hdrPlusOn = onValue.equals(hdrPlus);
Sascha Haeberling98f38bb2013-09-24 18:58:34 -07001745
1746 boolean doGcamModeSwitch = false;
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001747 if (hdrPlusOn && GcamHelper.hasGcamCapture()) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001748 // Kick off mode switch to gcam.
1749 doGcamModeSwitch = true;
Michael Kolb8872c232013-01-29 10:33:22 -08001750 } else {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001751 if (hdrOn) {
1752 mSceneMode = CameraUtil.SCENE_MODE_HDR;
1753 } else {
1754 mSceneMode = mPreferences.getString(
1755 CameraSettings.KEY_SCENE_MODE,
1756 mActivity.getString(R.string.pref_camera_scenemode_default));
1757 }
Michael Kolb8872c232013-01-29 10:33:22 -08001758 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001759 if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
Michael Kolb8872c232013-01-29 10:33:22 -08001760 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1761 mParameters.setSceneMode(mSceneMode);
1762
1763 // Setting scene mode will change the settings of flash mode,
1764 // white balance, and focus mode. Here we read back the
1765 // parameters, so we can know those settings.
1766 mCameraDevice.setParameters(mParameters);
1767 mParameters = mCameraDevice.getParameters();
1768 }
1769 } else {
1770 mSceneMode = mParameters.getSceneMode();
1771 if (mSceneMode == null) {
1772 mSceneMode = Parameters.SCENE_MODE_AUTO;
1773 }
1774 }
1775
1776 // Set JPEG quality.
1777 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1778 CameraProfile.QUALITY_HIGH);
1779 mParameters.setJpegQuality(jpegQuality);
1780
1781 // For the following settings, we need to check if the settings are
1782 // still supported by latest driver, if not, ignore the settings.
1783
1784 // Set exposure compensation
1785 int value = CameraSettings.readExposure(mPreferences);
1786 int max = mParameters.getMaxExposureCompensation();
1787 int min = mParameters.getMinExposureCompensation();
1788 if (value >= min && value <= max) {
1789 mParameters.setExposureCompensation(value);
1790 } else {
1791 Log.w(TAG, "invalid exposure range: " + value);
1792 }
1793
1794 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1795 // Set flash mode.
1796 String flashMode = mPreferences.getString(
1797 CameraSettings.KEY_FLASH_MODE,
1798 mActivity.getString(R.string.pref_camera_flashmode_default));
1799 List<String> supportedFlash = mParameters.getSupportedFlashModes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001800 if (CameraUtil.isSupported(flashMode, supportedFlash)) {
Michael Kolb8872c232013-01-29 10:33:22 -08001801 mParameters.setFlashMode(flashMode);
1802 } else {
1803 flashMode = mParameters.getFlashMode();
1804 if (flashMode == null) {
1805 flashMode = mActivity.getString(
1806 R.string.pref_camera_flashmode_no_flash);
1807 }
1808 }
1809
1810 // Set white balance parameter.
1811 String whiteBalance = mPreferences.getString(
1812 CameraSettings.KEY_WHITE_BALANCE,
1813 mActivity.getString(R.string.pref_camera_whitebalance_default));
Angus Kongb50b5cb2013-08-09 14:55:20 -07001814 if (CameraUtil.isSupported(whiteBalance,
Michael Kolb8872c232013-01-29 10:33:22 -08001815 mParameters.getSupportedWhiteBalance())) {
1816 mParameters.setWhiteBalance(whiteBalance);
1817 } else {
1818 whiteBalance = mParameters.getWhiteBalance();
1819 if (whiteBalance == null) {
1820 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1821 }
1822 }
1823
1824 // Set focus mode.
1825 mFocusManager.overrideFocusMode(null);
1826 mParameters.setFocusMode(mFocusManager.getFocusMode());
1827 } else {
1828 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1829 }
1830
Angus Kongdcccc512013-08-08 17:06:03 -07001831 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
Michael Kolb8872c232013-01-29 10:33:22 -08001832 updateAutoFocusMoveCallback();
1833 }
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001834
1835 return doGcamModeSwitch;
Michael Kolb8872c232013-01-29 10:33:22 -08001836 }
1837
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001838 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001839 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001840 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07001841 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07001842 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001843 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07001844 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001845 }
1846 }
1847
1848 // We separate the parameters into several subsets, so we can update only
1849 // the subsets actually need updating. The PREFERENCE set needs extra
1850 // locking because the preference can be changed from GLThread as well.
1851 private void setCameraParameters(int updateSet) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001852 boolean doModeSwitch = false;
1853
Michael Kolb8872c232013-01-29 10:33:22 -08001854 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1855 updateCameraParametersInitialize();
1856 }
1857
1858 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1859 updateCameraParametersZoom();
1860 }
1861
1862 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001863 doModeSwitch = updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08001864 }
1865
1866 mCameraDevice.setParameters(mParameters);
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001867
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001868 // Switch to gcam module if HDR+ was selected
Angus Kong0fb819b2013-10-08 13:44:19 -07001869 if (doModeSwitch && !mIsImageCaptureIntent) {
ztenghui367c7c82013-10-16 14:43:26 -07001870 mHandler.sendEmptyMessage(SWITCH_TO_GCAM_MODULE);
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001871 }
Michael Kolb8872c232013-01-29 10:33:22 -08001872 }
1873
1874 // If the Camera is idle, update the parameters immediately, otherwise
1875 // accumulate them in mUpdateSet and update later.
1876 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1877 mUpdateSet |= additionalUpdateSet;
1878 if (mCameraDevice == null) {
1879 // We will update all the parameters when we open the device, so
1880 // we don't need to do anything now.
1881 mUpdateSet = 0;
1882 return;
1883 } else if (isCameraIdle()) {
1884 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001885 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001886 mUpdateSet = 0;
1887 } else {
1888 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1889 mHandler.sendEmptyMessageDelayed(
1890 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1891 }
1892 }
1893 }
1894
ztenghui7b265a62013-09-09 14:58:44 -07001895 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001896 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001897 return (mCameraState == IDLE) ||
1898 (mCameraState == PREVIEW_STOPPED) ||
1899 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1900 && (mCameraState != SWITCHING_CAMERA));
1901 }
1902
ztenghui7b265a62013-09-09 14:58:44 -07001903 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001904 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001905 String action = mActivity.getIntent().getAction();
1906 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Angus Kong6a8e8a12013-07-19 14:55:07 -07001907 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08001908 }
1909
1910 private void setupCaptureParams() {
1911 Bundle myExtras = mActivity.getIntent().getExtras();
1912 if (myExtras != null) {
1913 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1914 mCropValue = myExtras.getString("crop");
1915 }
1916 }
1917
Michael Kolb8872c232013-01-29 10:33:22 -08001918 @Override
1919 public void onSharedPreferenceChanged() {
1920 // ignore the events after "onPause()"
1921 if (mPaused) return;
1922
1923 boolean recordLocation = RecordLocationPreference.get(
1924 mPreferences, mContentResolver);
1925 mLocationManager.recordLocation(recordLocation);
1926
1927 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
Michael Kolbeb8adc12013-04-26 11:09:29 -07001928 mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, mPreferences);
Michael Kolb8872c232013-01-29 10:33:22 -08001929 }
1930
1931 @Override
1932 public void onCameraPickerClicked(int cameraId) {
1933 if (mPaused || mPendingSwitchCameraId != -1) return;
1934
1935 mPendingSwitchCameraId = cameraId;
Doris Liu6432cd62013-06-13 17:20:31 -07001936
1937 Log.v(TAG, "Start to switch camera. cameraId=" + cameraId);
1938 // We need to keep a preview frame for the animation before
1939 // releasing the camera. This will trigger onPreviewTextureCopied.
1940 //TODO: Need to animate the camera switch
1941 switchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -08001942 }
1943
Michael Kolb8872c232013-01-29 10:33:22 -08001944 // Preview texture has been copied. Now camera can be released and the
1945 // animation can be started.
1946 @Override
1947 public void onPreviewTextureCopied() {
1948 mHandler.sendEmptyMessage(SWITCH_CAMERA);
1949 }
1950
1951 @Override
1952 public void onCaptureTextureCopied() {
1953 }
1954
1955 @Override
1956 public void onUserInteraction() {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001957 if (!mActivity.isFinishing()) keepScreenOnAwhile();
Michael Kolb8872c232013-01-29 10:33:22 -08001958 }
1959
1960 private void resetScreenOn() {
1961 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1962 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1963 }
1964
1965 private void keepScreenOnAwhile() {
1966 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1967 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1968 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1969 }
1970
Michael Kolb8872c232013-01-29 10:33:22 -08001971 @Override
1972 public void onOverriddenPreferencesClicked() {
1973 if (mPaused) return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001974 mUI.showPreferencesToast();
Michael Kolb8872c232013-01-29 10:33:22 -08001975 }
1976
1977 private void showTapToFocusToast() {
1978 // TODO: Use a toast?
1979 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1980 // Clear the preference.
1981 Editor editor = mPreferences.edit();
1982 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
1983 editor.apply();
1984 }
1985
1986 private void initializeCapabilities() {
1987 mInitialParams = mCameraDevice.getParameters();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001988 mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams);
1989 mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams);
1990 mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams);
1991 mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams);
Angus Kongdcccc512013-08-08 17:06:03 -07001992 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07001993 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08001994 }
1995
Michael Kolb8872c232013-01-29 10:33:22 -08001996 @Override
1997 public void onCountDownFinished() {
1998 mSnapshotOnIdle = false;
1999 mFocusManager.doSnap();
Doris Liuda50e052013-02-07 14:36:32 -08002000 mFocusManager.onShutterUp();
Michael Kolb8872c232013-01-29 10:33:22 -08002001 }
2002
Michael Kolb8872c232013-01-29 10:33:22 -08002003 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08002004 public void onShowSwitcherPopup() {
Michael Kolbd6954f32013-03-08 20:43:01 -08002005 mUI.onShowSwitcherPopup();
Michael Kolb8872c232013-01-29 10:33:22 -08002006 }
2007
Angus Kongce5480e2013-01-29 17:43:48 -08002008 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002009 public int onZoomChanged(int index) {
2010 // Not useful to change zoom value when the activity is paused.
2011 if (mPaused) return index;
2012 mZoomValue = index;
2013 if (mParameters == null || mCameraDevice == null) return index;
2014 // Set zoom parameters asynchronously
2015 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07002016 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08002017 Parameters p = mCameraDevice.getParameters();
2018 if (p != null) return p.getZoom();
2019 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08002020 }
2021
2022 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002023 public int getCameraState() {
2024 return mCameraState;
2025 }
2026
2027 @Override
2028 public void onQueueStatus(boolean full) {
2029 mUI.enableShutter(!full);
Angus Kongce5480e2013-01-29 17:43:48 -08002030 }
Angus Kong86d36312013-01-31 18:22:44 -08002031
2032 @Override
Angus Kongfd4fc0e2013-11-07 15:38:09 -08002033 public void onMediaSaverAvailable(MediaSaver s) {
Angus Kong86d36312013-01-31 18:22:44 -08002034 // We set the listener only when both service and shutterbutton
2035 // are initialized.
Michael Kolbd6954f32013-03-08 20:43:01 -08002036 if (mFirstTimeInitialized) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -08002037 s.setQueueListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -08002038 }
Angus Kong86d36312013-01-31 18:22:44 -08002039 }
Angus Kong0d00a892013-03-26 11:40:40 -07002040
2041 @Override
2042 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2043 }
2044
2045 @Override
2046 public void onSensorChanged(SensorEvent event) {
2047 int type = event.sensor.getType();
2048 float[] data;
2049 if (type == Sensor.TYPE_ACCELEROMETER) {
2050 data = mGData;
2051 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2052 data = mMData;
2053 } else {
2054 // we should not be here.
2055 return;
2056 }
2057 for (int i = 0; i < 3 ; i++) {
2058 data[i] = event.values[i];
2059 }
2060 float[] orientation = new float[3];
2061 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2062 SensorManager.getOrientation(mR, orientation);
2063 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2064 if (mHeading < 0) {
2065 mHeading += 360;
2066 }
Angus Kong0d00a892013-03-26 11:40:40 -07002067 }
Doris Liu6432cd62013-06-13 17:20:31 -07002068
2069 @Override
ztenghui7b265a62013-09-09 14:58:44 -07002070 public void onPreviewFocusChanged(boolean previewFocused) {
2071 mUI.onPreviewFocusChanged(previewFocused);
Doris Liu6432cd62013-06-13 17:20:31 -07002072 }
2073
Erin Dahlgren3044d8c2013-10-10 18:23:45 -07002074 @Override
2075 public boolean arePreviewControlsVisible() {
2076 return mUI.arePreviewControlsVisible();
2077 }
2078
Ruben Brunkd217ed02013-10-08 23:31:13 -07002079 // For debugging only.
2080 public void setDebugUri(Uri uri) {
2081 mDebugUri = uri;
2082 }
2083
2084 // For debugging only.
2085 private void saveToDebugUri(byte[] data) {
2086 if (mDebugUri != null) {
2087 OutputStream outputStream = null;
2088 try {
2089 outputStream = mContentResolver.openOutputStream(mDebugUri);
2090 outputStream.write(data);
2091 outputStream.close();
2092 } catch (IOException e) {
2093 Log.e(TAG, "Exception while writing debug jpeg file", e);
2094 } finally {
2095 CameraUtil.closeSilently(outputStream);
2096 }
2097 }
2098 }
2099
Doris Liu6432cd62013-06-13 17:20:31 -07002100/* Below is no longer needed, except to get rid of compile error
2101 * TODO: Remove these
2102 */
2103
2104 // TODO: Delete this function after old camera code is removed
2105 @Override
2106 public void onRestorePreferencesClicked() {}
2107
Michael Kolb8872c232013-01-29 10:33:22 -08002108}