blob: d06d7417e7655659c551a6ebef3d9bb5665e2f40 [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;
Dan Aminzade92ae10e2013-08-13 14:44:25 -070021import android.content.BroadcastReceiver;
Michael Kolb8872c232013-01-29 10:33:22 -080022import android.content.ContentProviderClient;
23import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070024import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080025import android.content.Intent;
Dan Aminzade92ae10e2013-08-13 14:44:25 -070026import android.content.IntentFilter;
Michael Kolb8872c232013-01-29 10:33:22 -080027import android.content.SharedPreferences.Editor;
28import android.content.res.Configuration;
29import android.graphics.Bitmap;
30import android.graphics.SurfaceTexture;
31import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080032import android.hardware.Camera.Parameters;
Michael Kolb8872c232013-01-29 10:33:22 -080033import android.hardware.Camera.Size;
Angus Kong0d00a892013-03-26 11:40:40 -070034import android.hardware.Sensor;
35import android.hardware.SensorEvent;
36import android.hardware.SensorEventListener;
37import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080038import android.location.Location;
39import android.media.CameraProfile;
40import android.net.Uri;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070041import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080042import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080043import android.os.Handler;
44import 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;
ztenghuia16e7b52013-08-23 11:47:56 -070061import com.android.camera.exif.ExifInterface;
62import com.android.camera.exif.ExifTag;
63import com.android.camera.exif.Rational;
Michael Kolbd6954f32013-03-08 20:43:01 -080064import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070065import com.android.camera.ui.ModuleSwitcher;
Michael Kolb8872c232013-01-29 10:33:22 -080066import com.android.camera.ui.RotateTextToast;
Angus Kongdcccc512013-08-08 17:06:03 -070067import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070068import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070069import com.android.camera.util.GcamHelper;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070070import com.android.camera.util.UsageStatistics;
71import com.android.camera2.R;
Michael Kolb8872c232013-01-29 10:33:22 -080072
Angus Kongdcccc512013-08-08 17:06:03 -070073import java.io.File;
74import java.io.FileNotFoundException;
75import java.io.FileOutputStream;
76import java.io.IOException;
77import java.io.OutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070078import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070079import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070080
Michael Kolb8872c232013-01-29 10:33:22 -080081public class PhotoModule
Dan Aminzade92ae10e2013-08-13 14:44:25 -070082 implements CameraModule,
83 PhotoController,
84 FocusOverlayManager.Listener,
85 CameraPreference.OnPreferenceChangedListener,
86 ShutterButton.OnShutterButtonListener,
87 MediaSaveService.Listener,
88 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;
Michael Kolb8872c232013-01-29 10:33:22 -0800106
107 // The subset of parameters we need to update in setCameraParameters().
108 private static final int UPDATE_PARAM_INITIALIZE = 1;
109 private static final int UPDATE_PARAM_ZOOM = 2;
110 private static final int UPDATE_PARAM_PREFERENCE = 4;
111 private static final int UPDATE_PARAM_ALL = -1;
112
113 // This is the timeout to keep the camera in onPause for the first time
114 // after screen on if the activity is started from secure lock screen.
115 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
116
117 // copied from Camera hierarchy
118 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800119 private CameraProxy mCameraDevice;
120 private int mCameraId;
121 private Parameters mParameters;
122 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800123
124 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800125
Michael Kolb8872c232013-01-29 10:33:22 -0800126 // The activity is going to switch to the specified camera id. This is
127 // needed because texture copy is done in GL thread. -1 means camera is not
128 // switching.
129 protected int mPendingSwitchCameraId = -1;
130 private boolean mOpenCameraFail;
131 private boolean mCameraDisabled;
132
133 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
134 // needed to be updated in mUpdateSet.
135 private int mUpdateSet;
136
137 private static final int SCREEN_DELAY = 2 * 60 * 1000;
138
139 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800140
141 private Parameters mInitialParams;
142 private boolean mFocusAreaSupported;
143 private boolean mMeteringAreaSupported;
144 private boolean mAeLockSupported;
145 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700146 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800147
148 // The degrees of the device rotated clockwise from its natural orientation.
149 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
150 private ComboPreferences mPreferences;
151
152 private static final String sTempCropFilename = "crop-temp";
153
154 private ContentProviderClient mMediaProviderClient;
Michael Kolb8872c232013-01-29 10:33:22 -0800155 private boolean mFaceDetectionStarted = false;
156
Michael Kolb8872c232013-01-29 10:33:22 -0800157 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
158 private String mCropValue;
159 private Uri mSaveUri;
160
Ruben Brunkd217ed02013-10-08 23:31:13 -0700161 private Uri mDebugUri;
162
Angus Kongce5480e2013-01-29 17:43:48 -0800163 // We use a queue to generated names of the images to be used later
164 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800165 private NamedImages mNamedImages;
166
167 private Runnable mDoSnapRunnable = new Runnable() {
168 @Override
169 public void run() {
170 onShutterButtonClick();
171 }
172 };
173
Michael Kolb8872c232013-01-29 10:33:22 -0800174 /**
175 * An unpublished intent flag requesting to return as soon as capturing
176 * is completed.
177 *
178 * TODO: consider publishing by moving into MediaStore.
179 */
180 private static final String EXTRA_QUICK_CAPTURE =
181 "android.intent.extra.quickCapture";
182
183 // The display rotation in degrees. This is only valid when mCameraState is
184 // not PREVIEW_STOPPED.
185 private int mDisplayRotation;
186 // The value for android.hardware.Camera.setDisplayOrientation.
187 private int mCameraDisplayOrientation;
188 // The value for UI components like indicators.
189 private int mDisplayOrientation;
190 // The value for android.hardware.Camera.Parameters.setRotation.
191 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700192 // Indicates whether we are using front camera
193 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800194 private boolean mFirstTimeInitialized;
195 private boolean mIsImageCaptureIntent;
196
Michael Kolb8872c232013-01-29 10:33:22 -0800197 private int mCameraState = PREVIEW_STOPPED;
198 private boolean mSnapshotOnIdle = false;
199
200 private ContentResolver mContentResolver;
201
202 private LocationManager mLocationManager;
203
Michael Kolb8872c232013-01-29 10:33:22 -0800204 private final PostViewPictureCallback mPostViewPictureCallback =
205 new PostViewPictureCallback();
206 private final RawPictureCallback mRawPictureCallback =
207 new RawPictureCallback();
208 private final AutoFocusCallback mAutoFocusCallback =
209 new AutoFocusCallback();
210 private final Object mAutoFocusMoveCallback =
211 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700212 ? new AutoFocusMoveCallback()
213 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800214
215 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
216
217 private long mFocusStartTime;
218 private long mShutterCallbackTime;
219 private long mPostViewPictureCallbackTime;
220 private long mRawPictureCallbackTime;
221 private long mJpegPictureCallbackTime;
222 private long mOnResumeTime;
223 private byte[] mJpegImageData;
224
225 // These latency time are for the CameraLatency test.
226 public long mAutoFocusTime;
227 public long mShutterLag;
228 public long mShutterToPictureDisplayedTime;
229 public long mPictureDisplayedToJpegCallbackTime;
230 public long mJpegCallbackFinishTime;
231 public long mCaptureStartTime;
232
233 // This handles everything about focus.
234 private FocusOverlayManager mFocusManager;
235
Michael Kolb8872c232013-01-29 10:33:22 -0800236 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800237
238 private final Handler mHandler = new MainHandler();
239 private PreferenceGroup mPreferenceGroup;
240
241 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700242 private SensorManager mSensorManager;
243 private float[] mGData = new float[3];
244 private float[] mMData = new float[3];
245 private float[] mR = new float[16];
246 private int mHeading = -1;
247
Angus Kongdcccc512013-08-08 17:06:03 -0700248 // True if all the parameters needed to start preview is ready.
249 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700250
Angus Kongce5480e2013-01-29 17:43:48 -0800251 private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
252 new MediaSaveService.OnMediaSavedListener() {
253 @Override
254 public void onMediaSaved(Uri uri) {
255 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700256 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800257 }
258 }
259 };
Michael Kolb8872c232013-01-29 10:33:22 -0800260
Angus Kongdcccc512013-08-08 17:06:03 -0700261 private void checkDisplayRotation() {
262 // Set the display orientation if display rotation has changed.
263 // Sometimes this happens when the device is held upside
264 // down and camera app is opened. Rotation animation will
265 // take some time and the rotation value we have got may be
266 // wrong. Framework does not have a callback for this now.
267 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
268 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800269 }
Angus Kongdcccc512013-08-08 17:06:03 -0700270 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
271 mHandler.postDelayed(new Runnable() {
272 @Override
273 public void run() {
274 checkDisplayRotation();
275 }
276 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800277 }
278 }
279
280 /**
281 * This Handler is used to post message back onto the main thread of the
282 * application
283 */
284 private class MainHandler extends Handler {
285 @Override
286 public void handleMessage(Message msg) {
287 switch (msg.what) {
288 case SETUP_PREVIEW: {
289 setupPreview();
290 break;
291 }
292
293 case CLEAR_SCREEN_DELAY: {
294 mActivity.getWindow().clearFlags(
295 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
296 break;
297 }
298
299 case FIRST_TIME_INIT: {
300 initializeFirstTime();
301 break;
302 }
303
304 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
305 setCameraParametersWhenIdle(0);
306 break;
307 }
308
Michael Kolb8872c232013-01-29 10:33:22 -0800309 case SHOW_TAP_TO_FOCUS_TOAST: {
310 showTapToFocusToast();
311 break;
312 }
313
314 case SWITCH_CAMERA: {
315 switchCamera();
316 break;
317 }
318
319 case SWITCH_CAMERA_START_ANIMATION: {
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700320 // TODO: Need to revisit
321 // ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -0800322 break;
323 }
324
325 case CAMERA_OPEN_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800326 onCameraOpened();
Michael Kolb8872c232013-01-29 10:33:22 -0800327 break;
328 }
329
Michael Kolb8872c232013-01-29 10:33:22 -0800330 case OPEN_CAMERA_FAIL: {
Michael Kolb8872c232013-01-29 10:33:22 -0800331 mOpenCameraFail = true;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700332 CameraUtil.showErrorAndFinish(mActivity,
Michael Kolb8872c232013-01-29 10:33:22 -0800333 R.string.cannot_connect_camera);
334 break;
335 }
336
337 case CAMERA_DISABLED: {
Michael Kolb8872c232013-01-29 10:33:22 -0800338 mCameraDisabled = true;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700339 CameraUtil.showErrorAndFinish(mActivity,
Michael Kolb8872c232013-01-29 10:33:22 -0800340 R.string.camera_disabled);
341 break;
342 }
Michael Kolb8872c232013-01-29 10:33:22 -0800343 }
344 }
345 }
346
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700347
Michael Kolb8872c232013-01-29 10:33:22 -0800348 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700349 public void init(CameraActivity activity, View parent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800350 mActivity = activity;
Michael Kolbd6954f32013-03-08 20:43:01 -0800351 mUI = new PhotoUI(activity, this, parent);
Michael Kolb8872c232013-01-29 10:33:22 -0800352 mPreferences = new ComboPreferences(mActivity);
353 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
354 mCameraId = getPreferredCameraId(mPreferences);
355
356 mContentResolver = mActivity.getContentResolver();
357
Michael Kolb8872c232013-01-29 10:33:22 -0800358 // Surface texture is from camera screen nail and startPreview needs it.
359 // This must be done before startPreview.
360 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800361
362 mPreferences.setLocalId(mActivity, mCameraId);
363 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
364 // we need to reset exposure for the preview
365 resetExposureCompensation();
Michael Kolb8872c232013-01-29 10:33:22 -0800366
367 initializeControlByIntent();
368 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Michael Kolbd6954f32013-03-08 20:43:01 -0800369 mLocationManager = new LocationManager(mActivity, mUI);
Angus Kong0d00a892013-03-26 11:40:40 -0700370 mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
Michael Kolbd6954f32013-03-08 20:43:01 -0800371 }
372
373 private void initializeControlByIntent() {
374 mUI.initializeControlByIntent();
375 if (mIsImageCaptureIntent) {
376 setupCaptureParams();
377 }
378 }
379
380 private void onPreviewStarted() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800381 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800382 startFaceDetection();
383 locationFirstRun();
Michael Kolb8872c232013-01-29 10:33:22 -0800384 }
385
386 // Prompt the user to pick to record location for the very first run of
387 // camera only
388 private void locationFirstRun() {
389 if (RecordLocationPreference.isSet(mPreferences)) {
390 return;
391 }
392 if (mActivity.isSecureCamera()) return;
393 // Check if the back camera exists
394 int backCameraId = CameraHolder.instance().getBackCameraId();
395 if (backCameraId == -1) {
396 // If there is no back camera, do not show the prompt.
397 return;
398 }
Doris Liu6a83d522013-07-02 12:03:32 -0700399 mUI.showLocationDialog();
400 }
Michael Kolb8872c232013-01-29 10:33:22 -0800401
ztenghui7b265a62013-09-09 14:58:44 -0700402 @Override
Doris Liu6a83d522013-07-02 12:03:32 -0700403 public void enableRecordingLocation(boolean enable) {
404 setLocationPreference(enable ? RecordLocationPreference.VALUE_ON
405 : RecordLocationPreference.VALUE_OFF);
Michael Kolb8872c232013-01-29 10:33:22 -0800406 }
407
Angus Kongdcccc512013-08-08 17:06:03 -0700408 @Override
409 public void onPreviewUIReady() {
410 startPreview();
411 }
412
413 @Override
414 public void onPreviewUIDestroyed() {
415 if (mCameraDevice == null) {
416 return;
417 }
418 mCameraDevice.setPreviewTexture(null);
419 stopPreview();
420 }
421
Michael Kolb8872c232013-01-29 10:33:22 -0800422 private void setLocationPreference(String value) {
423 mPreferences.edit()
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700424 .putString(CameraSettings.KEY_RECORD_LOCATION, value)
425 .apply();
Michael Kolb8872c232013-01-29 10:33:22 -0800426 // TODO: Fix this to use the actual onSharedPreferencesChanged listener
427 // instead of invoking manually
428 onSharedPreferenceChanged();
429 }
430
Michael Kolbd6954f32013-03-08 20:43:01 -0800431 private void onCameraOpened() {
432 View root = mUI.getRootView();
Michael Kolb8872c232013-01-29 10:33:22 -0800433 // These depend on camera parameters.
Michael Kolbd6954f32013-03-08 20:43:01 -0800434
435 int width = root.getWidth();
436 int height = root.getHeight();
Doris Liu6a0de792013-02-26 10:54:25 -0800437 mFocusManager.setPreviewSize(width, height);
Michael Kolbd6954f32013-03-08 20:43:01 -0800438 openCameraCommon();
Michael Kolb8872c232013-01-29 10:33:22 -0800439 }
440
Michael Kolbd6954f32013-03-08 20:43:01 -0800441 private void switchCamera() {
442 if (mPaused) return;
443
444 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
445 mCameraId = mPendingSwitchCameraId;
446 mPendingSwitchCameraId = -1;
447 setCameraId(mCameraId);
448
449 // from onPause
450 closeCamera();
451 mUI.collapseCameraControls();
452 mUI.clearFaces();
453 if (mFocusManager != null) mFocusManager.removeMessages();
454
455 // Restart the camera and initialize the UI. From onCreate.
456 mPreferences.setLocalId(mActivity, mCameraId);
457 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
Angus Kong4f795b82013-09-16 14:25:35 -0700458 mCameraDevice = CameraUtil.openCamera(
459 mActivity, mCameraId, mHandler,
460 mActivity.getCameraOpenErrorCallback());
461 if (mCameraDevice == null) {
462 Log.e(TAG, "Failed to open camera:" + mCameraId + ", aborting.");
Michael Kolbd6954f32013-03-08 20:43:01 -0800463 return;
Doris Liu09106a42013-03-05 09:54:25 -0800464 }
Angus Kong4f795b82013-09-16 14:25:35 -0700465 mParameters = mCameraDevice.getParameters();
Michael Kolbd6954f32013-03-08 20:43:01 -0800466 initializeCapabilities();
467 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Doris Liu29da2db2013-08-28 14:28:45 -0700468 mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
469 mFocusManager.setMirror(mMirror);
Michael Kolbd6954f32013-03-08 20:43:01 -0800470 mFocusManager.setParameters(mInitialParams);
471 setupPreview();
472
Doris Liu6432cd62013-06-13 17:20:31 -0700473 // reset zoom value index
474 mZoomValue = 0;
Michael Kolbd6954f32013-03-08 20:43:01 -0800475 openCameraCommon();
476
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700477 // Start switch camera animation. Post a message because
478 // onFrameAvailable from the old camera may already exist.
479 mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
Doris Liu48239f42013-03-04 22:19:10 -0800480 }
481
Michael Kolbd6954f32013-03-08 20:43:01 -0800482 protected void setCameraId(int cameraId) {
483 ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
484 pref.setValue("" + cameraId);
485 }
486
487 // either open a new camera or switch cameras
488 private void openCameraCommon() {
Michael Kolb8872c232013-01-29 10:33:22 -0800489 loadCameraPreferences();
Michael Kolbd6954f32013-03-08 20:43:01 -0800490
491 mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
Angus Kong0fb819b2013-10-08 13:44:19 -0700492 if (mIsImageCaptureIntent) {
493 mUI.overrideSettings(CameraSettings.KEY_CAMERA_HDR_PLUS,
494 mActivity.getString(R.string.setting_off_value));
495 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800496 updateSceneMode();
497 showTapToFocusToastIfNeeded();
498
499
Michael Kolb8872c232013-01-29 10:33:22 -0800500 }
501
ztenghui7b265a62013-09-09 14:58:44 -0700502 @Override
Doris Liub3749f22013-09-25 12:22:08 -0700503 public void onScreenSizeChanged(int width, int height) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800504 if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
Michael Kolbd6954f32013-03-08 20:43:01 -0800505 }
Michael Kolb8872c232013-01-29 10:33:22 -0800506
507 private void resetExposureCompensation() {
508 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
509 CameraSettings.EXPOSURE_DEFAULT_VALUE);
510 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
511 Editor editor = mPreferences.edit();
512 editor.putString(CameraSettings.KEY_EXPOSURE, "0");
513 editor.apply();
514 }
515 }
516
517 private void keepMediaProviderInstance() {
518 // We want to keep a reference to MediaProvider in camera's lifecycle.
519 // TODO: Utilize mMediaProviderClient instance to replace
520 // ContentResolver calls.
521 if (mMediaProviderClient == null) {
522 mMediaProviderClient = mContentResolver
523 .acquireContentProviderClient(MediaStore.AUTHORITY);
524 }
525 }
526
527 // Snapshots can only be taken after this is called. It should be called
528 // once only. We could have done these things in onCreate() but we want to
529 // make preview screen appear as soon as possible.
530 private void initializeFirstTime() {
531 if (mFirstTimeInitialized) return;
532
533 // Initialize location service.
534 boolean recordLocation = RecordLocationPreference.get(
535 mPreferences, mContentResolver);
536 mLocationManager.recordLocation(recordLocation);
537
538 keepMediaProviderInstance();
539
Michael Kolbd6954f32013-03-08 20:43:01 -0800540 mUI.initializeFirstTime();
Angus Kong86d36312013-01-31 18:22:44 -0800541 MediaSaveService s = mActivity.getMediaSaveService();
542 // We set the listener only when both service and shutterbutton
543 // are initialized.
544 if (s != null) {
545 s.setListener(this);
546 }
Michael Kolb8872c232013-01-29 10:33:22 -0800547
Michael Kolb8872c232013-01-29 10:33:22 -0800548 mNamedImages = new NamedImages();
549
550 mFirstTimeInitialized = true;
551 addIdleHandler();
552
553 mActivity.updateStorageSpaceAndHint();
554 }
555
Michael Kolbd6954f32013-03-08 20:43:01 -0800556 // If the activity is paused and resumed, this method will be called in
557 // onResume.
558 private void initializeSecondTime() {
559 // Start location update if needed.
560 boolean recordLocation = RecordLocationPreference.get(
561 mPreferences, mContentResolver);
562 mLocationManager.recordLocation(recordLocation);
563 MediaSaveService s = mActivity.getMediaSaveService();
564 if (s != null) {
565 s.setListener(this);
566 }
567 mNamedImages = new NamedImages();
568 mUI.initializeSecondTime(mParameters);
569 keepMediaProviderInstance();
570 }
571
Michael Kolb8872c232013-01-29 10:33:22 -0800572 private void showTapToFocusToastIfNeeded() {
573 // Show the tap to focus toast if this is the first start.
574 if (mFocusAreaSupported &&
575 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
576 // Delay the toast for one second to wait for orientation.
577 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
578 }
579 }
580
581 private void addIdleHandler() {
582 MessageQueue queue = Looper.myQueue();
583 queue.addIdleHandler(new MessageQueue.IdleHandler() {
584 @Override
585 public boolean queueIdle() {
586 Storage.ensureOSXCompatible();
587 return false;
588 }
589 });
590 }
591
Michael Kolb8872c232013-01-29 10:33:22 -0800592 @Override
593 public void startFaceDetection() {
Michael Kolb8872c232013-01-29 10:33:22 -0800594 if (mFaceDetectionStarted) return;
595 if (mParameters.getMaxNumDetectedFaces() > 0) {
596 mFaceDetectionStarted = true;
Michael Kolb8872c232013-01-29 10:33:22 -0800597 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Michael Kolbd6954f32013-03-08 20:43:01 -0800598 mUI.onStartFaceDetection(mDisplayOrientation,
599 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
Angus Kong9e765522013-07-31 14:05:20 -0700600 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800601 mCameraDevice.startFaceDetection();
602 }
603 }
604
Michael Kolb8872c232013-01-29 10:33:22 -0800605 @Override
606 public void stopFaceDetection() {
Michael Kolb8872c232013-01-29 10:33:22 -0800607 if (!mFaceDetectionStarted) return;
608 if (mParameters.getMaxNumDetectedFaces() > 0) {
609 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700610 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800611 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800612 mUI.clearFaces();
Michael Kolb8872c232013-01-29 10:33:22 -0800613 }
614 }
615
Michael Kolb8872c232013-01-29 10:33:22 -0800616 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700617 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700618
Sascha Haeberling37f36112013-08-06 14:31:52 -0700619 private boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700620
Sascha Haeberling37f36112013-08-06 14:31:52 -0700621 public ShutterCallback(boolean needsAnimation) {
622 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700623 }
624
Michael Kolb8872c232013-01-29 10:33:22 -0800625 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700626 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800627 mShutterCallbackTime = System.currentTimeMillis();
628 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
629 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700630 if (mNeedsAnimation) {
631 mActivity.runOnUiThread(new Runnable() {
632 @Override
633 public void run() {
634 animateAfterShutter();
635 }
636 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700637 }
Michael Kolb8872c232013-01-29 10:33:22 -0800638 }
639 }
640
Angus Kong9ef99252013-07-18 18:04:19 -0700641 private final class PostViewPictureCallback
642 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800643 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700644 public void onPictureTaken(byte [] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800645 mPostViewPictureCallbackTime = System.currentTimeMillis();
646 Log.v(TAG, "mShutterToPostViewCallbackTime = "
647 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
648 + "ms");
649 }
650 }
651
Angus Kong9ef99252013-07-18 18:04:19 -0700652 private final class RawPictureCallback
653 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800654 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700655 public void onPictureTaken(byte [] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800656 mRawPictureCallbackTime = System.currentTimeMillis();
657 Log.v(TAG, "mShutterToRawCallbackTime = "
658 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
659 }
660 }
661
Angus Kong9ef99252013-07-18 18:04:19 -0700662 private final class JpegPictureCallback
663 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800664 Location mLocation;
665
666 public JpegPictureCallback(Location loc) {
667 mLocation = loc;
668 }
669
670 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700671 public void onPictureTaken(final byte [] jpegData, CameraProxy camera) {
Sascha Haeberling88901942013-08-28 17:49:00 -0700672 mUI.enableShutter(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800673 if (mPaused) {
674 return;
675 }
Doris Liu6432cd62013-06-13 17:20:31 -0700676 if (mIsImageCaptureIntent) {
677 stopPreview();
678 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700679 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700680 mUI.showSwitcher();
681 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800682 }
683
684 mJpegPictureCallbackTime = System.currentTimeMillis();
685 // If postview callback has arrived, the captured image is displayed
686 // in postview callback. If not, the captured image is displayed in
687 // raw picture callback.
688 if (mPostViewPictureCallbackTime != 0) {
689 mShutterToPictureDisplayedTime =
690 mPostViewPictureCallbackTime - mShutterCallbackTime;
691 mPictureDisplayedToJpegCallbackTime =
692 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
693 } else {
694 mShutterToPictureDisplayedTime =
695 mRawPictureCallbackTime - mShutterCallbackTime;
696 mPictureDisplayedToJpegCallbackTime =
697 mJpegPictureCallbackTime - mRawPictureCallbackTime;
698 }
699 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
700 + mPictureDisplayedToJpegCallbackTime + "ms");
701
Michael Kolb8872c232013-01-29 10:33:22 -0800702 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
703 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700704 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800705 }
706
Doris Liu36e56fb2013-09-11 17:38:08 -0700707 ExifInterface exif = Exif.getExif(jpegData);
708 int orientation = Exif.getOrientation(exif);
Ruben Brunkd217ed02013-10-08 23:31:13 -0700709
710 if (mDebugUri != null) {
711 // If using a debug uri, save jpeg there
712 saveToDebugUri(jpegData);
713 }else if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800714 // Calculate the width and the height of the jpeg.
715 Size s = mParameters.getPictureSize();
Michael Kolb8872c232013-01-29 10:33:22 -0800716 int width, height;
717 if ((mJpegRotation + orientation) % 180 == 0) {
718 width = s.width;
719 height = s.height;
720 } else {
721 width = s.height;
722 height = s.width;
723 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700724 NamedEntity name = mNamedImages.getNextNameEntity();
725 String title = (name == null) ? null : name.title;
726 long date = (name == null) ? -1 : name.date;
Michael Kolb8872c232013-01-29 10:33:22 -0800727 if (title == null) {
728 Log.e(TAG, "Unbalanced name/data pair");
729 } else {
730 if (date == -1) date = mCaptureStartTime;
Angus Kong0d00a892013-03-26 11:40:40 -0700731 if (mHeading >= 0) {
732 // heading direction has been updated by the sensor.
733 ExifTag directionRefTag = exif.buildTag(
734 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
735 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
736 ExifTag directionTag = exif.buildTag(
737 ExifInterface.TAG_GPS_IMG_DIRECTION,
738 new Rational(mHeading, 1));
739 exif.setTag(directionRefTag);
740 exif.setTag(directionTag);
741 }
Angus Kong86d36312013-01-31 18:22:44 -0800742 mActivity.getMediaSaveService().addImage(
743 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700744 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800745 }
Doris Liuce2acbc2013-08-21 18:45:29 -0700746 // Animate capture with real jpeg data instead of a preview frame.
Doris Liu29da2db2013-08-28 14:28:45 -0700747 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800748 } else {
749 mJpegImageData = jpegData;
750 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -0700751 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800752 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800753 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800754 }
755 }
756
757 // Check this in advance of each shot so we don't add to shutter
758 // latency. It's true that someone else could write to the SD card in
759 // the mean time and fill it, but that could have happened between the
760 // shutter press and saving the JPEG too.
761 mActivity.updateStorageSpaceAndHint();
762
763 long now = System.currentTimeMillis();
764 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
765 Log.v(TAG, "mJpegCallbackFinishTime = "
766 + mJpegCallbackFinishTime + "ms");
767 mJpegPictureCallbackTime = 0;
768 }
769 }
770
Angus Kong9ef99252013-07-18 18:04:19 -0700771 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800772 @Override
773 public void onAutoFocus(
Angus Kong9ef99252013-07-18 18:04:19 -0700774 boolean focused, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800775 if (mPaused) return;
776
777 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
778 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
779 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800780 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb8872c232013-01-29 10:33:22 -0800781 }
782 }
783
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700784 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -0800785 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700786 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800787 @Override
788 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700789 boolean moving, CameraProxy camera) {
790 mFocusManager.onAutoFocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -0800791 }
792 }
793
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700794 /**
795 * This class is just a thread-safe queue for name,date holder objects.
796 */
797 public static class NamedImages {
798 private Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -0800799
800 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700801 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -0800802 }
803
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700804 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -0800805 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700806 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -0800807 r.date = date;
808 mQueue.add(r);
809 }
810
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700811 public NamedEntity getNextNameEntity() {
812 synchronized(mQueue) {
813 if (!mQueue.isEmpty()) {
814 return mQueue.remove(0);
815 }
Michael Kolb8872c232013-01-29 10:33:22 -0800816 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700817 return null;
Michael Kolb8872c232013-01-29 10:33:22 -0800818 }
819
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700820 public static class NamedEntity {
821 public String title;
822 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -0800823 }
824 }
825
826 private void setCameraState(int state) {
827 mCameraState = state;
828 switch (state) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700829 case PhotoController.PREVIEW_STOPPED:
830 case PhotoController.SNAPSHOT_IN_PROGRESS:
831 case PhotoController.SWITCHING_CAMERA:
832 mUI.enableGestures(false);
833 break;
834 case PhotoController.IDLE:
835 mUI.enableGestures(true);
836 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800837 }
838 }
839
Sascha Haeberling37f36112013-08-06 14:31:52 -0700840 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -0800841 // Only animate when in full screen capture mode
842 // i.e. If monkey/a user swipes to the gallery during picture taking,
843 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -0700844 if (!mIsImageCaptureIntent) {
845 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -0700846 }
Michael Kolb8872c232013-01-29 10:33:22 -0800847 }
848
849 @Override
850 public boolean capture() {
851 // If we are already in the middle of taking a snapshot or the image save request
852 // is full then ignore.
853 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Angus Kong86d36312013-01-31 18:22:44 -0800854 || mCameraState == SWITCHING_CAMERA
855 || mActivity.getMediaSaveService().isQueueFull()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800856 return false;
857 }
858 mCaptureStartTime = System.currentTimeMillis();
859 mPostViewPictureCallbackTime = 0;
860 mJpegImageData = null;
861
Angus Kongb50b5cb2013-08-09 14:55:20 -0700862 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -0800863
864 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700865 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -0800866 }
867
868 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800869 int orientation;
870 // We need to be consistent with the framework orientation (i.e. the
871 // orientation of the UI.) when the auto-rotate screen setting is on.
872 if (mActivity.isAutoRotateScreen()) {
873 orientation = (360 - mDisplayRotation) % 360;
874 } else {
875 orientation = mOrientation;
876 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700877 mJpegRotation = CameraUtil.getJpegRotation(mCameraId, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800878 mParameters.setRotation(mJpegRotation);
879 Location loc = mLocationManager.getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700880 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -0800881 mCameraDevice.setParameters(mParameters);
882
Sascha Haeberling88901942013-08-28 17:49:00 -0700883 // We don't want user to press the button again while taking a
884 // multi-second HDR photo.
885 mUI.enableShutter(false);
Angus Kong9ef99252013-07-18 18:04:19 -0700886 mCameraDevice.takePicture(mHandler,
887 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -0700888 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -0700889 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -0800890
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700891 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -0800892
893 mFaceDetectionStarted = false;
894 setCameraState(SNAPSHOT_IN_PROGRESS);
Bobby Georgescu301b6462013-04-01 15:33:17 -0700895 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
896 UsageStatistics.ACTION_CAPTURE_DONE, "Photo");
Michael Kolb8872c232013-01-29 10:33:22 -0800897 return true;
898 }
899
900 @Override
901 public void setFocusParameters() {
902 setCameraParameters(UPDATE_PARAM_PREFERENCE);
903 }
904
905 private int getPreferredCameraId(ComboPreferences preferences) {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700906 int intentCameraId = CameraUtil.getCameraFacingIntentExtras(mActivity);
Michael Kolb8872c232013-01-29 10:33:22 -0800907 if (intentCameraId != -1) {
908 // Testing purpose. Launch a specific camera through the intent
909 // extras.
910 return intentCameraId;
911 } else {
912 return CameraSettings.readPreferredCameraId(preferences);
913 }
914 }
915
Michael Kolbd6954f32013-03-08 20:43:01 -0800916 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -0800917 // If scene mode is set, we cannot set flash mode, white balance, and
918 // focus mode, instead, we read it from driver
919 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
920 overrideCameraSettings(mParameters.getFlashMode(),
921 mParameters.getWhiteBalance(), mParameters.getFocusMode());
922 } else {
923 overrideCameraSettings(null, null, null);
924 }
925 }
926
927 private void overrideCameraSettings(final String flashMode,
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700928 final String whiteBalance, final String focusMode) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800929 mUI.overrideSettings(
930 CameraSettings.KEY_FLASH_MODE, flashMode,
931 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
932 CameraSettings.KEY_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -0800933 }
934
935 private void loadCameraPreferences() {
936 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
937 mCameraId, CameraHolder.instance().getCameraInfo());
938 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
939 }
940
941 @Override
Michael Kolb8872c232013-01-29 10:33:22 -0800942 public void onOrientationChanged(int orientation) {
943 // We keep the last known orientation. So if the user first orient
944 // the camera then point the camera to floor or sky, we still have
945 // the correct orientation.
946 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700947 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800948
949 // Show the toast after getting the first orientation changed.
950 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
951 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
952 showTapToFocusToast();
953 }
954 }
955
956 @Override
957 public void onStop() {
958 if (mMediaProviderClient != null) {
959 mMediaProviderClient.release();
960 mMediaProviderClient = null;
961 }
962 }
963
Michael Kolbd6954f32013-03-08 20:43:01 -0800964 @Override
965 public void onCaptureCancelled() {
966 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
967 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -0800968 }
969
Michael Kolbd6954f32013-03-08 20:43:01 -0800970 @Override
971 public void onCaptureRetake() {
Michael Kolb8872c232013-01-29 10:33:22 -0800972 if (mPaused)
973 return;
Michael Kolbd6954f32013-03-08 20:43:01 -0800974 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -0800975 setupPreview();
976 }
977
Michael Kolbd6954f32013-03-08 20:43:01 -0800978 @Override
979 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -0800980 if (mPaused) {
981 return;
982 }
983
984 byte[] data = mJpegImageData;
985
986 if (mCropValue == null) {
987 // First handle the no crop case -- just return the value. If the
988 // caller specifies a "save uri" then write the data to its
989 // stream. Otherwise, pass back a scaled down version of the bitmap
990 // directly in the extras.
991 if (mSaveUri != null) {
992 OutputStream outputStream = null;
993 try {
994 outputStream = mContentResolver.openOutputStream(mSaveUri);
995 outputStream.write(data);
996 outputStream.close();
997
998 mActivity.setResultEx(Activity.RESULT_OK);
999 mActivity.finish();
1000 } catch (IOException ex) {
1001 // ignore exception
1002 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001003 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001004 }
1005 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001006 ExifInterface exif = Exif.getExif(data);
1007 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001008 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1009 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001010 mActivity.setResultEx(Activity.RESULT_OK,
1011 new Intent("inline-data").putExtra("data", bitmap));
1012 mActivity.finish();
1013 }
1014 } else {
1015 // Save the image to a temp file and invoke the cropper
1016 Uri tempUri = null;
1017 FileOutputStream tempStream = null;
1018 try {
1019 File path = mActivity.getFileStreamPath(sTempCropFilename);
1020 path.delete();
1021 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1022 tempStream.write(data);
1023 tempStream.close();
1024 tempUri = Uri.fromFile(path);
1025 } catch (FileNotFoundException ex) {
1026 mActivity.setResultEx(Activity.RESULT_CANCELED);
1027 mActivity.finish();
1028 return;
1029 } catch (IOException ex) {
1030 mActivity.setResultEx(Activity.RESULT_CANCELED);
1031 mActivity.finish();
1032 return;
1033 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001034 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001035 }
1036
1037 Bundle newExtras = new Bundle();
1038 if (mCropValue.equals("circle")) {
1039 newExtras.putString("circleCrop", "true");
1040 }
1041 if (mSaveUri != null) {
1042 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1043 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001044 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001045 }
1046 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001047 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001048 }
1049
Sascha Haeberling37f36112013-08-06 14:31:52 -07001050 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001051 final String CROP_ACTION = "com.android.camera.action.CROP";
1052 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001053
1054 cropIntent.setData(tempUri);
1055 cropIntent.putExtras(newExtras);
1056
1057 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1058 }
1059 }
1060
Michael Kolb8872c232013-01-29 10:33:22 -08001061 @Override
1062 public void onShutterButtonFocus(boolean pressed) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001063 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001064 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1065 || (mCameraState == PREVIEW_STOPPED)) return;
1066
1067 // Do not do focus if there is not enough storage.
1068 if (pressed && !canTakePicture()) return;
1069
1070 if (pressed) {
Michael Kolb8872c232013-01-29 10:33:22 -08001071 mFocusManager.onShutterDown();
1072 } else {
Doris Liuda50e052013-02-07 14:36:32 -08001073 // for countdown mode, we need to postpone the shutter release
1074 // i.e. lock the focus during countdown.
Michael Kolbd6954f32013-03-08 20:43:01 -08001075 if (!mUI.isCountingDown()) {
Doris Liuda50e052013-02-07 14:36:32 -08001076 mFocusManager.onShutterUp();
1077 }
Michael Kolb8872c232013-01-29 10:33:22 -08001078 }
1079 }
1080
1081 @Override
1082 public void onShutterButtonClick() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001083 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001084 || (mCameraState == SWITCHING_CAMERA)
1085 || (mCameraState == PREVIEW_STOPPED)) return;
1086
1087 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001088 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001089 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001090 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001091 return;
1092 }
1093 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1094
Angus Kongb50b5cb2013-08-09 14:55:20 -07001095 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001096 mUI.hideSwitcher();
1097 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001098 }
Michael Kolb8872c232013-01-29 10:33:22 -08001099 // If the user wants to do a snapshot while the previous one is still
1100 // in progress, remember the fact and do it after we finish the previous
1101 // one and re-start the preview. Snapshot in progress also includes the
1102 // state that autofocus is focusing and a picture will be taken when
1103 // focus callback arrives.
1104 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1105 && !mIsImageCaptureIntent) {
1106 mSnapshotOnIdle = true;
1107 return;
1108 }
1109
1110 String timer = mPreferences.getString(
1111 CameraSettings.KEY_TIMER,
1112 mActivity.getString(R.string.pref_camera_timer_default));
1113 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1114 mActivity.getString(R.string.pref_camera_timer_sound_default))
1115 .equals(mActivity.getString(R.string.setting_on_value));
1116
1117 int seconds = Integer.parseInt(timer);
1118 // When shutter button is pressed, check whether the previous countdown is
1119 // finished. If not, cancel the previous countdown and start a new one.
Michael Kolbd6954f32013-03-08 20:43:01 -08001120 if (mUI.isCountingDown()) {
1121 mUI.cancelCountDown();
1122 }
1123 if (seconds > 0) {
1124 mUI.startCountDown(seconds, playSound);
Michael Kolb8872c232013-01-29 10:33:22 -08001125 } else {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001126 mSnapshotOnIdle = false;
1127 mFocusManager.doSnap();
Michael Kolb8872c232013-01-29 10:33:22 -08001128 }
1129 }
1130
1131 @Override
1132 public void installIntentFilter() {
Sascha Haeberling4de78802013-10-06 18:07:07 -07001133 // Do nothing.
Michael Kolb8872c232013-01-29 10:33:22 -08001134 }
1135
1136 @Override
1137 public boolean updateStorageHintOnResume() {
1138 return mFirstTimeInitialized;
1139 }
1140
1141 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001142 public void onResumeBeforeSuper() {
1143 mPaused = false;
1144 }
1145
Angus Kong4f795b82013-09-16 14:25:35 -07001146 /**
1147 * Opens the camera device.
1148 *
1149 * @return Whether the camera was opened successfully.
1150 */
1151 private boolean prepareCamera() {
1152 // We need to check whether the activity is paused before long
1153 // operations to ensure that onPause() can be done ASAP.
1154 mCameraDevice = CameraUtil.openCamera(
1155 mActivity, mCameraId, mHandler,
1156 mActivity.getCameraOpenErrorCallback());
1157 if (mCameraDevice == null) {
1158 Log.e(TAG, "Failed to open camera:" + mCameraId);
1159 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001160 }
Angus Kong4f795b82013-09-16 14:25:35 -07001161 mParameters = mCameraDevice.getParameters();
1162
1163 initializeCapabilities();
1164 if (mFocusManager == null) initializeFocusManager();
1165 setCameraParameters(UPDATE_PARAM_ALL);
1166 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
1167 mCameraPreviewParamsReady = true;
1168 startPreview();
1169 mOnResumeTime = SystemClock.uptimeMillis();
1170 checkDisplayRotation();
1171 return true;
Angus Kongdcccc512013-08-08 17:06:03 -07001172 }
1173
1174
Michael Kolb8872c232013-01-29 10:33:22 -08001175 @Override
1176 public void onResumeAfterSuper() {
1177 if (mOpenCameraFail || mCameraDisabled) return;
1178
1179 mJpegPictureCallbackTime = 0;
1180 mZoomValue = 0;
Angus Kongdcccc512013-08-08 17:06:03 -07001181 resetExposureCompensation();
Angus Kong4f795b82013-09-16 14:25:35 -07001182 if (!prepareCamera()) {
1183 // Camera failure.
1184 return;
1185 }
Michael Kolb8872c232013-01-29 10:33:22 -08001186
1187 // If first time initialization is not finished, put it in the
1188 // message queue.
1189 if (!mFirstTimeInitialized) {
1190 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1191 } else {
1192 initializeSecondTime();
1193 }
Doris Liu59390062013-10-10 17:20:56 -07001194 mUI.initDisplayChangeListener();
Michael Kolb8872c232013-01-29 10:33:22 -08001195 keepScreenOnAwhile();
1196
Bobby Georgescu0a7dd572013-03-12 22:45:17 -07001197 UsageStatistics.onContentViewChanged(
1198 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
Angus Kong0d00a892013-03-26 11:40:40 -07001199
1200 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1201 if (gsensor != null) {
1202 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1203 }
1204
1205 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1206 if (msensor != null) {
1207 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1208 }
Michael Kolb8872c232013-01-29 10:33:22 -08001209 }
1210
Michael Kolb8872c232013-01-29 10:33:22 -08001211 @Override
1212 public void onPauseBeforeSuper() {
1213 mPaused = true;
Angus Kong0d00a892013-03-26 11:40:40 -07001214 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1215 if (gsensor != null) {
1216 mSensorManager.unregisterListener(this, gsensor);
1217 }
1218
1219 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1220 if (msensor != null) {
1221 mSensorManager.unregisterListener(this, msensor);
1222 }
Michael Kolb8872c232013-01-29 10:33:22 -08001223 }
1224
1225 @Override
1226 public void onPauseAfterSuper() {
Michael Kolb8872c232013-01-29 10:33:22 -08001227 // When camera is started from secure lock screen for the first time
1228 // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1229 // To reduce the latency, keep the camera for a short time so it does
1230 // not need to be opened again.
1231 if (mCameraDevice != null && mActivity.isSecureCamera()
Angus Kong6a8e8a12013-07-19 14:55:07 -07001232 && CameraActivity.isFirstStartAfterScreenOn()) {
1233 CameraActivity.resetFirstStartAfterScreenOn();
Michael Kolb8872c232013-01-29 10:33:22 -08001234 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1235 }
1236 // Reset the focus first. Camera CTS does not guarantee that
1237 // cancelAutoFocus is allowed after preview stops.
1238 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1239 mCameraDevice.cancelAutoFocus();
1240 }
1241 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001242
Angus Kongce5480e2013-01-29 17:43:48 -08001243 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001244
1245 if (mLocationManager != null) mLocationManager.recordLocation(false);
1246
1247 // If we are in an image capture intent and has taken
1248 // a picture, we just clear it in onPause.
1249 mJpegImageData = null;
1250
Angus Kongdcccc512013-08-08 17:06:03 -07001251 // Remove the messages and runnables in the queue.
1252 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001253
Michael Kolbd6954f32013-03-08 20:43:01 -08001254 closeCamera();
1255
1256 resetScreenOn();
1257 mUI.onPause();
1258
Michael Kolb8872c232013-01-29 10:33:22 -08001259 mPendingSwitchCameraId = -1;
1260 if (mFocusManager != null) mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001261 MediaSaveService s = mActivity.getMediaSaveService();
1262 if (s != null) {
1263 s.setListener(null);
1264 }
Doris Liu59390062013-10-10 17:20:56 -07001265 mUI.removeDisplayChangeListener();
Michael Kolb8872c232013-01-29 10:33:22 -08001266 }
1267
Michael Kolb8872c232013-01-29 10:33:22 -08001268 /**
1269 * The focus manager is the first UI related element to get initialized,
1270 * and it requires the RenderOverlay, so initialize it here
1271 */
1272 private void initializeFocusManager() {
1273 // Create FocusManager object. startPreview needs it.
Michael Kolb8872c232013-01-29 10:33:22 -08001274 // if mFocusManager not null, reuse it
1275 // otherwise create a new instance
1276 if (mFocusManager != null) {
1277 mFocusManager.removeMessages();
1278 } else {
1279 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Doris Liu29da2db2013-08-28 14:28:45 -07001280 mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
Michael Kolb8872c232013-01-29 10:33:22 -08001281 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1282 R.array.pref_camera_focusmode_default_array);
1283 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
Doris Liu29da2db2013-08-28 14:28:45 -07001284 mInitialParams, this, mMirror,
Michael Kolbd6954f32013-03-08 20:43:01 -08001285 mActivity.getMainLooper(), mUI);
Michael Kolb8872c232013-01-29 10:33:22 -08001286 }
1287 }
1288
Michael Kolb8872c232013-01-29 10:33:22 -08001289 @Override
1290 public void onConfigurationChanged(Configuration newConfig) {
1291 Log.v(TAG, "onConfigurationChanged");
1292 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001293 }
1294
1295 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001296 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001297 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001298 setDisplayOrientation();
1299 }
1300 }
1301
1302 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001303 public void onActivityResult(
1304 int requestCode, int resultCode, Intent data) {
1305 switch (requestCode) {
1306 case REQUEST_CROP: {
1307 Intent intent = new Intent();
1308 if (data != null) {
1309 Bundle extras = data.getExtras();
1310 if (extras != null) {
1311 intent.putExtras(extras);
1312 }
1313 }
1314 mActivity.setResultEx(resultCode, intent);
1315 mActivity.finish();
1316
1317 File path = mActivity.getFileStreamPath(sTempCropFilename);
1318 path.delete();
1319
1320 break;
1321 }
1322 }
1323 }
1324
1325 private boolean canTakePicture() {
Angus Kong2dcc0a92013-09-25 13:00:08 -07001326 return isCameraIdle() && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001327 }
1328
1329 @Override
1330 public void autoFocus() {
1331 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001332 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001333 setCameraState(FOCUSING);
1334 }
1335
1336 @Override
1337 public void cancelAutoFocus() {
1338 mCameraDevice.cancelAutoFocus();
1339 setCameraState(IDLE);
1340 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1341 }
1342
1343 // Preview area is touched. Handle touch focus.
1344 @Override
1345 public void onSingleTapUp(View view, int x, int y) {
1346 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1347 || mCameraState == SNAPSHOT_IN_PROGRESS
1348 || mCameraState == SWITCHING_CAMERA
1349 || mCameraState == PREVIEW_STOPPED) {
1350 return;
1351 }
1352
Michael Kolb8872c232013-01-29 10:33:22 -08001353 // Check if metering area or focus area is supported.
1354 if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1355 mFocusManager.onSingleTapUp(x, y);
1356 }
1357
1358 @Override
1359 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001360 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001361 }
1362
1363 @Override
1364 public boolean onKeyDown(int keyCode, KeyEvent event) {
1365 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001366 case KeyEvent.KEYCODE_VOLUME_UP:
1367 case KeyEvent.KEYCODE_VOLUME_DOWN:
1368 case KeyEvent.KEYCODE_FOCUS:
1369 if (/*TODO: mActivity.isInCameraApp() &&*/ mFirstTimeInitialized) {
1370 if (event.getRepeatCount() == 0) {
1371 onShutterButtonFocus(true);
1372 }
1373 return true;
1374 }
1375 return false;
1376 case KeyEvent.KEYCODE_CAMERA:
1377 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1378 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001379 }
1380 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001381 case KeyEvent.KEYCODE_DPAD_CENTER:
1382 // If we get a dpad center event without any focused view, move
1383 // the focus to the shutter button and press it.
1384 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1385 // Start auto-focus immediately to reduce shutter lag. After
1386 // the shutter button gets the focus, onShutterButtonFocus()
1387 // will be called again but it is fine.
1388 onShutterButtonFocus(true);
1389 mUI.pressShutterButton();
1390 }
1391 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001392 }
1393 return false;
1394 }
1395
1396 @Override
1397 public boolean onKeyUp(int keyCode, KeyEvent event) {
1398 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001399 case KeyEvent.KEYCODE_VOLUME_UP:
1400 case KeyEvent.KEYCODE_VOLUME_DOWN:
1401 if (/*mActivity.isInCameraApp() && */ mFirstTimeInitialized) {
1402 onShutterButtonClick();
1403 return true;
1404 }
1405 return false;
1406 case KeyEvent.KEYCODE_FOCUS:
1407 if (mFirstTimeInitialized) {
1408 onShutterButtonFocus(false);
1409 }
Michael Kolb8872c232013-01-29 10:33:22 -08001410 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001411 }
1412 return false;
1413 }
1414
Michael Kolb8872c232013-01-29 10:33:22 -08001415 private void closeCamera() {
1416 if (mCameraDevice != null) {
1417 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001418 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001419 mCameraDevice.setErrorCallback(null);
1420 CameraHolder.instance().release();
1421 mFaceDetectionStarted = false;
1422 mCameraDevice = null;
1423 setCameraState(PREVIEW_STOPPED);
1424 mFocusManager.onCameraReleased();
1425 }
1426 }
1427
1428 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001429 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1430 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001431 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001432 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001433 if (mFocusManager != null) {
1434 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1435 }
Doris Liu6432cd62013-06-13 17:20:31 -07001436 // Change the camera display orientation
1437 if (mCameraDevice != null) {
1438 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1439 }
Michael Kolb8872c232013-01-29 10:33:22 -08001440 }
1441
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001442 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001443 private void setupPreview() {
1444 mFocusManager.resetTouchFocus();
1445 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001446 }
1447
Angus Kongdcccc512013-08-08 17:06:03 -07001448 // This can only be called by UI Thread.
Michael Kolb8872c232013-01-29 10:33:22 -08001449 private void startPreview() {
Angus Kongdcccc512013-08-08 17:06:03 -07001450 if (mPaused) {
1451 return;
1452 }
1453 SurfaceTexture st = mUI.getSurfaceTexture();
1454 if (st == null) {
1455 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
1456 return;
1457 }
1458 if (!mCameraPreviewParamsReady) {
1459 Log.w(TAG, "startPreview: parameters for preview is not ready.");
1460 return;
1461 }
Michael Kolb8872c232013-01-29 10:33:22 -08001462 mCameraDevice.setErrorCallback(mErrorCallback);
1463
1464 // ICS camera frameworks has a bug. Face detection state is not cleared
1465 // after taking a picture. Stop the preview to work around it. The bug
1466 // was fixed in JB.
1467 if (mCameraState != PREVIEW_STOPPED) stopPreview();
1468
1469 setDisplayOrientation();
1470
1471 if (!mSnapshotOnIdle) {
1472 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1473 // resume it because it may have been paused by autoFocus call.
Angus Kongb50b5cb2013-08-09 14:55:20 -07001474 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
Michael Kolb8872c232013-01-29 10:33:22 -08001475 mCameraDevice.cancelAutoFocus();
1476 }
1477 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1478 }
1479 setCameraParameters(UPDATE_PARAM_ALL);
Doris Liu6432cd62013-06-13 17:20:31 -07001480 // Let UI set its expected aspect ratio
Angus Kongdcccc512013-08-08 17:06:03 -07001481 mCameraDevice.setPreviewTexture(st);
Michael Kolb8872c232013-01-29 10:33:22 -08001482
1483 Log.v(TAG, "startPreview");
Angus Kong9ef99252013-07-18 18:04:19 -07001484 mCameraDevice.startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001485 mFocusManager.onPreviewStarted();
Angus Kongdcccc512013-08-08 17:06:03 -07001486 onPreviewStarted();
Michael Kolb8872c232013-01-29 10:33:22 -08001487
1488 if (mSnapshotOnIdle) {
1489 mHandler.post(mDoSnapRunnable);
1490 }
1491 }
1492
Michael Kolbd6954f32013-03-08 20:43:01 -08001493 @Override
1494 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001495 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1496 Log.v(TAG, "stopPreview");
1497 mCameraDevice.stopPreview();
1498 mFaceDetectionStarted = false;
1499 }
1500 setCameraState(PREVIEW_STOPPED);
1501 if (mFocusManager != null) mFocusManager.onPreviewStopped();
1502 }
1503
1504 @SuppressWarnings("deprecation")
1505 private void updateCameraParametersInitialize() {
1506 // Reset preview frame rate to the maximum because it may be lowered by
1507 // video camera application.
ztenghui16a35202013-09-23 11:35:36 -07001508 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mParameters);
1509 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001510 mParameters.setPreviewFpsRange(
1511 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1512 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001513 }
1514
Angus Kongb50b5cb2013-08-09 14:55:20 -07001515 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001516
1517 // Disable video stabilization. Convenience methods not available in API
1518 // level <= 14
1519 String vstabSupported = mParameters.get("video-stabilization-supported");
1520 if ("true".equals(vstabSupported)) {
1521 mParameters.set("video-stabilization", "false");
1522 }
1523 }
1524
1525 private void updateCameraParametersZoom() {
1526 // Set zoom.
1527 if (mParameters.isZoomSupported()) {
1528 mParameters.setZoom(mZoomValue);
1529 }
1530 }
1531
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001532 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001533 private void setAutoExposureLockIfSupported() {
1534 if (mAeLockSupported) {
1535 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1536 }
1537 }
1538
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001539 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001540 private void setAutoWhiteBalanceLockIfSupported() {
1541 if (mAwbLockSupported) {
1542 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1543 }
1544 }
1545
Michael Kolb8872c232013-01-29 10:33:22 -08001546 private void setFocusAreasIfSupported() {
1547 if (mFocusAreaSupported) {
1548 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1549 }
1550 }
1551
Michael Kolb8872c232013-01-29 10:33:22 -08001552 private void setMeteringAreasIfSupported() {
1553 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001554 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1555 }
1556 }
1557
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001558 private boolean updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08001559 setAutoExposureLockIfSupported();
1560 setAutoWhiteBalanceLockIfSupported();
1561 setFocusAreasIfSupported();
1562 setMeteringAreasIfSupported();
1563
1564 // Set picture size.
1565 String pictureSize = mPreferences.getString(
1566 CameraSettings.KEY_PICTURE_SIZE, null);
1567 if (pictureSize == null) {
1568 CameraSettings.initialCameraPictureSize(mActivity, mParameters);
1569 } else {
1570 List<Size> supported = mParameters.getSupportedPictureSizes();
1571 CameraSettings.setCameraPictureSize(
1572 pictureSize, supported, mParameters);
1573 }
1574 Size size = mParameters.getPictureSize();
1575
1576 // Set a preview size that is closest to the viewfinder height and has
1577 // the right aspect ratio.
1578 List<Size> sizes = mParameters.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001579 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Michael Kolb8872c232013-01-29 10:33:22 -08001580 (double) size.width / size.height);
1581 Size original = mParameters.getPreviewSize();
1582 if (!original.equals(optimalSize)) {
1583 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
Doris Liu6432cd62013-06-13 17:20:31 -07001584
Michael Kolb8872c232013-01-29 10:33:22 -08001585 // Zoom related settings will be changed for different preview
1586 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07001587 if (mHandler.getLooper() == Looper.myLooper()) {
1588 // On UI thread only, not when camera starts up
1589 setupPreview();
1590 } else {
1591 mCameraDevice.setParameters(mParameters);
1592 }
Michael Kolb8872c232013-01-29 10:33:22 -08001593 mParameters = mCameraDevice.getParameters();
1594 }
1595 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1596
1597 // Since changing scene mode may change supported values, set scene mode
1598 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1599 // separate preference.
Sascha Haeberling98f38bb2013-09-24 18:58:34 -07001600 String onValue = mActivity.getString(R.string.setting_on_value);
Michael Kolb8872c232013-01-29 10:33:22 -08001601 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
1602 mActivity.getString(R.string.pref_camera_hdr_default));
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001603 String hdrPlus = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR_PLUS,
1604 mActivity.getString(R.string.pref_camera_hdr_plus_default));
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001605 boolean hdrOn = onValue.equals(hdr);
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001606 boolean hdrPlusOn = onValue.equals(hdrPlus);
Sascha Haeberling98f38bb2013-09-24 18:58:34 -07001607
1608 boolean doGcamModeSwitch = false;
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001609 if (hdrPlusOn && GcamHelper.hasGcamCapture()) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001610 // Kick off mode switch to gcam.
1611 doGcamModeSwitch = true;
Michael Kolb8872c232013-01-29 10:33:22 -08001612 } else {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001613 if (hdrOn) {
1614 mSceneMode = CameraUtil.SCENE_MODE_HDR;
1615 } else {
1616 mSceneMode = mPreferences.getString(
1617 CameraSettings.KEY_SCENE_MODE,
1618 mActivity.getString(R.string.pref_camera_scenemode_default));
1619 }
Michael Kolb8872c232013-01-29 10:33:22 -08001620 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001621 if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
Michael Kolb8872c232013-01-29 10:33:22 -08001622 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1623 mParameters.setSceneMode(mSceneMode);
1624
1625 // Setting scene mode will change the settings of flash mode,
1626 // white balance, and focus mode. Here we read back the
1627 // parameters, so we can know those settings.
1628 mCameraDevice.setParameters(mParameters);
1629 mParameters = mCameraDevice.getParameters();
1630 }
1631 } else {
1632 mSceneMode = mParameters.getSceneMode();
1633 if (mSceneMode == null) {
1634 mSceneMode = Parameters.SCENE_MODE_AUTO;
1635 }
1636 }
1637
1638 // Set JPEG quality.
1639 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1640 CameraProfile.QUALITY_HIGH);
1641 mParameters.setJpegQuality(jpegQuality);
1642
1643 // For the following settings, we need to check if the settings are
1644 // still supported by latest driver, if not, ignore the settings.
1645
1646 // Set exposure compensation
1647 int value = CameraSettings.readExposure(mPreferences);
1648 int max = mParameters.getMaxExposureCompensation();
1649 int min = mParameters.getMinExposureCompensation();
1650 if (value >= min && value <= max) {
1651 mParameters.setExposureCompensation(value);
1652 } else {
1653 Log.w(TAG, "invalid exposure range: " + value);
1654 }
1655
1656 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1657 // Set flash mode.
1658 String flashMode = mPreferences.getString(
1659 CameraSettings.KEY_FLASH_MODE,
1660 mActivity.getString(R.string.pref_camera_flashmode_default));
1661 List<String> supportedFlash = mParameters.getSupportedFlashModes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001662 if (CameraUtil.isSupported(flashMode, supportedFlash)) {
Michael Kolb8872c232013-01-29 10:33:22 -08001663 mParameters.setFlashMode(flashMode);
1664 } else {
1665 flashMode = mParameters.getFlashMode();
1666 if (flashMode == null) {
1667 flashMode = mActivity.getString(
1668 R.string.pref_camera_flashmode_no_flash);
1669 }
1670 }
1671
1672 // Set white balance parameter.
1673 String whiteBalance = mPreferences.getString(
1674 CameraSettings.KEY_WHITE_BALANCE,
1675 mActivity.getString(R.string.pref_camera_whitebalance_default));
Angus Kongb50b5cb2013-08-09 14:55:20 -07001676 if (CameraUtil.isSupported(whiteBalance,
Michael Kolb8872c232013-01-29 10:33:22 -08001677 mParameters.getSupportedWhiteBalance())) {
1678 mParameters.setWhiteBalance(whiteBalance);
1679 } else {
1680 whiteBalance = mParameters.getWhiteBalance();
1681 if (whiteBalance == null) {
1682 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1683 }
1684 }
1685
1686 // Set focus mode.
1687 mFocusManager.overrideFocusMode(null);
1688 mParameters.setFocusMode(mFocusManager.getFocusMode());
1689 } else {
1690 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1691 }
1692
Angus Kongdcccc512013-08-08 17:06:03 -07001693 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
Michael Kolb8872c232013-01-29 10:33:22 -08001694 updateAutoFocusMoveCallback();
1695 }
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001696
1697 return doGcamModeSwitch;
Michael Kolb8872c232013-01-29 10:33:22 -08001698 }
1699
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001700 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001701 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001702 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07001703 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07001704 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001705 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07001706 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001707 }
1708 }
1709
1710 // We separate the parameters into several subsets, so we can update only
1711 // the subsets actually need updating. The PREFERENCE set needs extra
1712 // locking because the preference can be changed from GLThread as well.
1713 private void setCameraParameters(int updateSet) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001714 boolean doModeSwitch = false;
1715
Michael Kolb8872c232013-01-29 10:33:22 -08001716 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1717 updateCameraParametersInitialize();
1718 }
1719
1720 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1721 updateCameraParametersZoom();
1722 }
1723
1724 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001725 doModeSwitch = updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08001726 }
1727
1728 mCameraDevice.setParameters(mParameters);
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001729
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001730 // Switch to gcam module if HDR+ was selected
Angus Kong0fb819b2013-10-08 13:44:19 -07001731 if (doModeSwitch && !mIsImageCaptureIntent) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001732 mActivity.onModuleSelected(ModuleSwitcher.GCAM_MODULE_INDEX);
1733 }
Michael Kolb8872c232013-01-29 10:33:22 -08001734 }
1735
1736 // If the Camera is idle, update the parameters immediately, otherwise
1737 // accumulate them in mUpdateSet and update later.
1738 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1739 mUpdateSet |= additionalUpdateSet;
1740 if (mCameraDevice == null) {
1741 // We will update all the parameters when we open the device, so
1742 // we don't need to do anything now.
1743 mUpdateSet = 0;
1744 return;
1745 } else if (isCameraIdle()) {
1746 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001747 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001748 mUpdateSet = 0;
1749 } else {
1750 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1751 mHandler.sendEmptyMessageDelayed(
1752 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1753 }
1754 }
1755 }
1756
ztenghui7b265a62013-09-09 14:58:44 -07001757 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001758 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001759 return (mCameraState == IDLE) ||
1760 (mCameraState == PREVIEW_STOPPED) ||
1761 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1762 && (mCameraState != SWITCHING_CAMERA));
1763 }
1764
ztenghui7b265a62013-09-09 14:58:44 -07001765 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001766 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001767 String action = mActivity.getIntent().getAction();
1768 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Angus Kong6a8e8a12013-07-19 14:55:07 -07001769 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08001770 }
1771
1772 private void setupCaptureParams() {
1773 Bundle myExtras = mActivity.getIntent().getExtras();
1774 if (myExtras != null) {
1775 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1776 mCropValue = myExtras.getString("crop");
1777 }
1778 }
1779
Michael Kolb8872c232013-01-29 10:33:22 -08001780 @Override
1781 public void onSharedPreferenceChanged() {
1782 // ignore the events after "onPause()"
1783 if (mPaused) return;
1784
1785 boolean recordLocation = RecordLocationPreference.get(
1786 mPreferences, mContentResolver);
1787 mLocationManager.recordLocation(recordLocation);
1788
1789 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
Michael Kolbeb8adc12013-04-26 11:09:29 -07001790 mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, mPreferences);
Michael Kolb8872c232013-01-29 10:33:22 -08001791 }
1792
1793 @Override
1794 public void onCameraPickerClicked(int cameraId) {
1795 if (mPaused || mPendingSwitchCameraId != -1) return;
1796
1797 mPendingSwitchCameraId = cameraId;
Doris Liu6432cd62013-06-13 17:20:31 -07001798
1799 Log.v(TAG, "Start to switch camera. cameraId=" + cameraId);
1800 // We need to keep a preview frame for the animation before
1801 // releasing the camera. This will trigger onPreviewTextureCopied.
1802 //TODO: Need to animate the camera switch
1803 switchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -08001804 }
1805
Michael Kolb8872c232013-01-29 10:33:22 -08001806 // Preview texture has been copied. Now camera can be released and the
1807 // animation can be started.
1808 @Override
1809 public void onPreviewTextureCopied() {
1810 mHandler.sendEmptyMessage(SWITCH_CAMERA);
1811 }
1812
1813 @Override
1814 public void onCaptureTextureCopied() {
1815 }
1816
1817 @Override
1818 public void onUserInteraction() {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001819 if (!mActivity.isFinishing()) keepScreenOnAwhile();
Michael Kolb8872c232013-01-29 10:33:22 -08001820 }
1821
1822 private void resetScreenOn() {
1823 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1824 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1825 }
1826
1827 private void keepScreenOnAwhile() {
1828 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1829 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1830 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1831 }
1832
Michael Kolb8872c232013-01-29 10:33:22 -08001833 @Override
1834 public void onOverriddenPreferencesClicked() {
1835 if (mPaused) return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001836 mUI.showPreferencesToast();
Michael Kolb8872c232013-01-29 10:33:22 -08001837 }
1838
1839 private void showTapToFocusToast() {
1840 // TODO: Use a toast?
1841 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1842 // Clear the preference.
1843 Editor editor = mPreferences.edit();
1844 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
1845 editor.apply();
1846 }
1847
1848 private void initializeCapabilities() {
1849 mInitialParams = mCameraDevice.getParameters();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001850 mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams);
1851 mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams);
1852 mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams);
1853 mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams);
Angus Kongdcccc512013-08-08 17:06:03 -07001854 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07001855 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08001856 }
1857
Michael Kolb8872c232013-01-29 10:33:22 -08001858 @Override
1859 public void onCountDownFinished() {
1860 mSnapshotOnIdle = false;
1861 mFocusManager.doSnap();
Doris Liuda50e052013-02-07 14:36:32 -08001862 mFocusManager.onShutterUp();
Michael Kolb8872c232013-01-29 10:33:22 -08001863 }
1864
Michael Kolb8872c232013-01-29 10:33:22 -08001865 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001866 public void onShowSwitcherPopup() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001867 mUI.onShowSwitcherPopup();
Michael Kolb8872c232013-01-29 10:33:22 -08001868 }
1869
Angus Kongce5480e2013-01-29 17:43:48 -08001870 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001871 public int onZoomChanged(int index) {
1872 // Not useful to change zoom value when the activity is paused.
1873 if (mPaused) return index;
1874 mZoomValue = index;
1875 if (mParameters == null || mCameraDevice == null) return index;
1876 // Set zoom parameters asynchronously
1877 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07001878 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08001879 Parameters p = mCameraDevice.getParameters();
1880 if (p != null) return p.getZoom();
1881 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08001882 }
1883
1884 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001885 public int getCameraState() {
1886 return mCameraState;
1887 }
1888
1889 @Override
1890 public void onQueueStatus(boolean full) {
1891 mUI.enableShutter(!full);
Angus Kongce5480e2013-01-29 17:43:48 -08001892 }
Angus Kong86d36312013-01-31 18:22:44 -08001893
1894 @Override
1895 public void onMediaSaveServiceConnected(MediaSaveService s) {
1896 // We set the listener only when both service and shutterbutton
1897 // are initialized.
Michael Kolbd6954f32013-03-08 20:43:01 -08001898 if (mFirstTimeInitialized) {
1899 s.setListener(this);
1900 }
Angus Kong86d36312013-01-31 18:22:44 -08001901 }
Angus Kong0d00a892013-03-26 11:40:40 -07001902
1903 @Override
1904 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1905 }
1906
1907 @Override
1908 public void onSensorChanged(SensorEvent event) {
1909 int type = event.sensor.getType();
1910 float[] data;
1911 if (type == Sensor.TYPE_ACCELEROMETER) {
1912 data = mGData;
1913 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
1914 data = mMData;
1915 } else {
1916 // we should not be here.
1917 return;
1918 }
1919 for (int i = 0; i < 3 ; i++) {
1920 data[i] = event.values[i];
1921 }
1922 float[] orientation = new float[3];
1923 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
1924 SensorManager.getOrientation(mR, orientation);
1925 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
1926 if (mHeading < 0) {
1927 mHeading += 360;
1928 }
Angus Kong0d00a892013-03-26 11:40:40 -07001929 }
Doris Liu6432cd62013-06-13 17:20:31 -07001930
1931 @Override
ztenghui7b265a62013-09-09 14:58:44 -07001932 public void onPreviewFocusChanged(boolean previewFocused) {
1933 mUI.onPreviewFocusChanged(previewFocused);
Doris Liu6432cd62013-06-13 17:20:31 -07001934 }
1935
Ruben Brunkd217ed02013-10-08 23:31:13 -07001936 // For debugging only.
1937 public void setDebugUri(Uri uri) {
1938 mDebugUri = uri;
1939 }
1940
1941 // For debugging only.
1942 private void saveToDebugUri(byte[] data) {
1943 if (mDebugUri != null) {
1944 OutputStream outputStream = null;
1945 try {
1946 outputStream = mContentResolver.openOutputStream(mDebugUri);
1947 outputStream.write(data);
1948 outputStream.close();
1949 } catch (IOException e) {
1950 Log.e(TAG, "Exception while writing debug jpeg file", e);
1951 } finally {
1952 CameraUtil.closeSilently(outputStream);
1953 }
1954 }
1955 }
1956
Doris Liu6432cd62013-06-13 17:20:31 -07001957/* Below is no longer needed, except to get rid of compile error
1958 * TODO: Remove these
1959 */
1960
1961 // TODO: Delete this function after old camera code is removed
1962 @Override
1963 public void onRestorePreferencesClicked() {}
1964
Michael Kolb8872c232013-01-29 10:33:22 -08001965}