blob: 7c4487b906c8f2601b723907b45a27be590c5536 [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.annotation.TargetApi;
20import android.app.Activity;
21import android.app.AlertDialog;
22import android.content.ContentProviderClient;
23import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070024import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080025import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.SharedPreferences.Editor;
28import android.content.res.Configuration;
29import android.graphics.Bitmap;
30import android.graphics.SurfaceTexture;
31import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080032import android.hardware.Camera.Parameters;
33import android.hardware.Camera.PictureCallback;
34import android.hardware.Camera.Size;
Angus Kong0d00a892013-03-26 11:40:40 -070035import android.hardware.Sensor;
36import android.hardware.SensorEvent;
37import android.hardware.SensorEventListener;
38import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080039import android.location.Location;
40import android.media.CameraProfile;
41import android.net.Uri;
42import android.os.Bundle;
43import android.os.ConditionVariable;
44import android.os.Handler;
45import android.os.Looper;
46import android.os.Message;
47import android.os.MessageQueue;
48import android.os.SystemClock;
49import android.provider.MediaStore;
50import android.util.Log;
Michael Kolb8872c232013-01-29 10:33:22 -080051import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080052import android.view.OrientationEventListener;
53import android.view.SurfaceHolder;
54import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080055import android.view.WindowManager;
Michael Kolb8872c232013-01-29 10:33:22 -080056
57import com.android.camera.CameraManager.CameraProxy;
Michael Kolbd6954f32013-03-08 20:43:01 -080058import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
Michael Kolb8872c232013-01-29 10:33:22 -080059import com.android.camera.ui.PopupManager;
Michael Kolb8872c232013-01-29 10:33:22 -080060import com.android.camera.ui.RotateTextToast;
John Reck54987e82013-02-15 15:51:30 -080061import com.android.gallery3d.R;
Michael Kolb8872c232013-01-29 10:33:22 -080062import com.android.gallery3d.common.ApiHelper;
Angus Kong0d00a892013-03-26 11:40:40 -070063import com.android.gallery3d.exif.ExifInterface;
64import com.android.gallery3d.exif.ExifTag;
65import com.android.gallery3d.exif.Rational;
Ruben Brunk88741b42013-05-28 12:34:14 -070066import com.android.gallery3d.filtershow.crop.CropActivity;
Ruben Brunk1a664452013-04-04 17:20:42 -070067import com.android.gallery3d.filtershow.crop.CropExtras;
Bobby Georgescu0a7dd572013-03-12 22:45:17 -070068import com.android.gallery3d.util.UsageStatistics;
Michael Kolb8872c232013-01-29 10:33:22 -080069
70import java.io.File;
71import java.io.FileNotFoundException;
72import java.io.FileOutputStream;
73import java.io.IOException;
74import java.io.OutputStream;
75import java.util.ArrayList;
Michael Kolb8872c232013-01-29 10:33:22 -080076import java.util.Formatter;
77import java.util.List;
78
79public class PhotoModule
80 implements CameraModule,
Michael Kolbd6954f32013-03-08 20:43:01 -080081 PhotoController,
Michael Kolb8872c232013-01-29 10:33:22 -080082 FocusOverlayManager.Listener,
83 CameraPreference.OnPreferenceChangedListener,
Michael Kolb8872c232013-01-29 10:33:22 -080084 ShutterButton.OnShutterButtonListener,
Michael Kolbd6954f32013-03-08 20:43:01 -080085 MediaSaveService.Listener,
Angus Kong0d00a892013-03-26 11:40:40 -070086 OnCountDownFinishedListener,
87 SensorEventListener {
Michael Kolb8872c232013-01-29 10:33:22 -080088
89 private static final String TAG = "CAM_PhotoModule";
90
91 // We number the request code from 1000 to avoid collision with Gallery.
92 private static final int REQUEST_CROP = 1000;
93
94 private static final int SETUP_PREVIEW = 1;
95 private static final int FIRST_TIME_INIT = 2;
96 private static final int CLEAR_SCREEN_DELAY = 3;
97 private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
98 private static final int CHECK_DISPLAY_ROTATION = 5;
99 private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
100 private static final int SWITCH_CAMERA = 7;
101 private static final int SWITCH_CAMERA_START_ANIMATION = 8;
102 private static final int CAMERA_OPEN_DONE = 9;
103 private static final int START_PREVIEW_DONE = 10;
104 private static final int OPEN_CAMERA_FAIL = 11;
105 private static final int CAMERA_DISABLED = 12;
Michael Kolb78a5b222013-04-30 15:34:19 -0700106 private static final int CAPTURE_ANIMATION_DONE = 13;
Michael Kolb8872c232013-01-29 10:33:22 -0800107
108 // The subset of parameters we need to update in setCameraParameters().
109 private static final int UPDATE_PARAM_INITIALIZE = 1;
110 private static final int UPDATE_PARAM_ZOOM = 2;
111 private static final int UPDATE_PARAM_PREFERENCE = 4;
112 private static final int UPDATE_PARAM_ALL = -1;
113
114 // This is the timeout to keep the camera in onPause for the first time
115 // after screen on if the activity is started from secure lock screen.
116 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
117
118 // copied from Camera hierarchy
119 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800120 private CameraProxy mCameraDevice;
121 private int mCameraId;
122 private Parameters mParameters;
123 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800124
125 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800126
Michael Kolb8872c232013-01-29 10:33:22 -0800127 // The activity is going to switch to the specified camera id. This is
128 // needed because texture copy is done in GL thread. -1 means camera is not
129 // switching.
130 protected int mPendingSwitchCameraId = -1;
131 private boolean mOpenCameraFail;
132 private boolean mCameraDisabled;
133
134 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
135 // needed to be updated in mUpdateSet.
136 private int mUpdateSet;
137
138 private static final int SCREEN_DELAY = 2 * 60 * 1000;
139
140 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800141
142 private Parameters mInitialParams;
143 private boolean mFocusAreaSupported;
144 private boolean mMeteringAreaSupported;
145 private boolean mAeLockSupported;
146 private boolean mAwbLockSupported;
147 private boolean mContinousFocusSupported;
148
149 // The degrees of the device rotated clockwise from its natural orientation.
150 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
151 private ComboPreferences mPreferences;
152
153 private static final String sTempCropFilename = "crop-temp";
154
155 private ContentProviderClient mMediaProviderClient;
Michael Kolb8872c232013-01-29 10:33:22 -0800156 private boolean mFaceDetectionStarted = false;
157
Michael Kolb8872c232013-01-29 10:33:22 -0800158 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
159 private String mCropValue;
160 private Uri mSaveUri;
161
Angus Kongce5480e2013-01-29 17:43:48 -0800162 // We use a queue to generated names of the images to be used later
163 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800164 private NamedImages mNamedImages;
165
166 private Runnable mDoSnapRunnable = new Runnable() {
167 @Override
168 public void run() {
169 onShutterButtonClick();
170 }
171 };
172
Angus Kongdcb0ef12013-03-25 23:11:43 -0700173 private Runnable mFlashRunnable = new Runnable() {
174 @Override
175 public void run() {
176 animateFlash();
177 }
178 };
179
Michael Kolb8872c232013-01-29 10:33:22 -0800180 private final StringBuilder mBuilder = new StringBuilder();
181 private final Formatter mFormatter = new Formatter(mBuilder);
182 private final Object[] mFormatterArgs = new Object[1];
183
184 /**
185 * An unpublished intent flag requesting to return as soon as capturing
186 * is completed.
187 *
188 * TODO: consider publishing by moving into MediaStore.
189 */
190 private static final String EXTRA_QUICK_CAPTURE =
191 "android.intent.extra.quickCapture";
192
193 // The display rotation in degrees. This is only valid when mCameraState is
194 // not PREVIEW_STOPPED.
195 private int mDisplayRotation;
196 // The value for android.hardware.Camera.setDisplayOrientation.
197 private int mCameraDisplayOrientation;
198 // The value for UI components like indicators.
199 private int mDisplayOrientation;
200 // The value for android.hardware.Camera.Parameters.setRotation.
201 private int mJpegRotation;
202 private boolean mFirstTimeInitialized;
203 private boolean mIsImageCaptureIntent;
204
Michael Kolb8872c232013-01-29 10:33:22 -0800205 private int mCameraState = PREVIEW_STOPPED;
206 private boolean mSnapshotOnIdle = false;
207
208 private ContentResolver mContentResolver;
209
210 private LocationManager mLocationManager;
211
Michael Kolb8872c232013-01-29 10:33:22 -0800212 private final PostViewPictureCallback mPostViewPictureCallback =
213 new PostViewPictureCallback();
214 private final RawPictureCallback mRawPictureCallback =
215 new RawPictureCallback();
216 private final AutoFocusCallback mAutoFocusCallback =
217 new AutoFocusCallback();
218 private final Object mAutoFocusMoveCallback =
219 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
220 ? new AutoFocusMoveCallback()
221 : null;
222
223 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
224
225 private long mFocusStartTime;
226 private long mShutterCallbackTime;
227 private long mPostViewPictureCallbackTime;
228 private long mRawPictureCallbackTime;
229 private long mJpegPictureCallbackTime;
230 private long mOnResumeTime;
231 private byte[] mJpegImageData;
232
233 // These latency time are for the CameraLatency test.
234 public long mAutoFocusTime;
235 public long mShutterLag;
236 public long mShutterToPictureDisplayedTime;
237 public long mPictureDisplayedToJpegCallbackTime;
238 public long mJpegCallbackFinishTime;
239 public long mCaptureStartTime;
240
241 // This handles everything about focus.
242 private FocusOverlayManager mFocusManager;
243
Michael Kolb8872c232013-01-29 10:33:22 -0800244 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800245
246 private final Handler mHandler = new MainHandler();
247 private PreferenceGroup mPreferenceGroup;
248
249 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700250 private SensorManager mSensorManager;
251 private float[] mGData = new float[3];
252 private float[] mMData = new float[3];
253 private float[] mR = new float[16];
254 private int mHeading = -1;
255
Doris Liu6432cd62013-06-13 17:20:31 -0700256 CameraStartUpThread mCameraStartUpThread;
257 ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
258
Angus Kongce5480e2013-01-29 17:43:48 -0800259 private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
260 new MediaSaveService.OnMediaSavedListener() {
261 @Override
262 public void onMediaSaved(Uri uri) {
263 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700264 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800265 }
266 }
267 };
Michael Kolb8872c232013-01-29 10:33:22 -0800268
269 // The purpose is not to block the main thread in onCreate and onResume.
270 private class CameraStartUpThread extends Thread {
271 private volatile boolean mCancelled;
272
273 public void cancel() {
274 mCancelled = true;
275 interrupt();
276 }
277
278 public boolean isCanceled() {
279 return mCancelled;
280 }
281
282 @Override
283 public void run() {
284 try {
285 // We need to check whether the activity is paused before long
286 // operations to ensure that onPause() can be done ASAP.
287 if (mCancelled) return;
288 mCameraDevice = Util.openCamera(mActivity, mCameraId);
289 mParameters = mCameraDevice.getParameters();
290 // Wait until all the initialization needed by startPreview are
291 // done.
292 mStartPreviewPrerequisiteReady.block();
293
294 initializeCapabilities();
295 if (mFocusManager == null) initializeFocusManager();
296 if (mCancelled) return;
297 setCameraParameters(UPDATE_PARAM_ALL);
298 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
299 if (mCancelled) return;
300 startPreview();
301 mHandler.sendEmptyMessage(START_PREVIEW_DONE);
302 mOnResumeTime = SystemClock.uptimeMillis();
303 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
304 } catch (CameraHardwareException e) {
305 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
306 } catch (CameraDisabledException e) {
307 mHandler.sendEmptyMessage(CAMERA_DISABLED);
308 }
309 }
310 }
311
312 /**
313 * This Handler is used to post message back onto the main thread of the
314 * application
315 */
316 private class MainHandler extends Handler {
317 @Override
318 public void handleMessage(Message msg) {
319 switch (msg.what) {
320 case SETUP_PREVIEW: {
321 setupPreview();
322 break;
323 }
324
325 case CLEAR_SCREEN_DELAY: {
326 mActivity.getWindow().clearFlags(
327 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
328 break;
329 }
330
331 case FIRST_TIME_INIT: {
332 initializeFirstTime();
333 break;
334 }
335
336 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
337 setCameraParametersWhenIdle(0);
338 break;
339 }
340
341 case CHECK_DISPLAY_ROTATION: {
342 // Set the display orientation if display rotation has changed.
343 // Sometimes this happens when the device is held upside
344 // down and camera app is opened. Rotation animation will
345 // take some time and the rotation value we have got may be
346 // wrong. Framework does not have a callback for this now.
347 if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
348 setDisplayOrientation();
349 }
350 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
351 mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
352 }
353 break;
354 }
355
356 case SHOW_TAP_TO_FOCUS_TOAST: {
357 showTapToFocusToast();
358 break;
359 }
360
361 case SWITCH_CAMERA: {
362 switchCamera();
363 break;
364 }
365
366 case SWITCH_CAMERA_START_ANIMATION: {
Doris Liu6432cd62013-06-13 17:20:31 -0700367 // TODO: Need to revisit
368 // ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -0800369 break;
370 }
371
372 case CAMERA_OPEN_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800373 onCameraOpened();
Michael Kolb8872c232013-01-29 10:33:22 -0800374 break;
375 }
376
377 case START_PREVIEW_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800378 onPreviewStarted();
Michael Kolb8872c232013-01-29 10:33:22 -0800379 break;
380 }
381
382 case OPEN_CAMERA_FAIL: {
383 mCameraStartUpThread = null;
384 mOpenCameraFail = true;
385 Util.showErrorAndFinish(mActivity,
386 R.string.cannot_connect_camera);
387 break;
388 }
389
390 case CAMERA_DISABLED: {
391 mCameraStartUpThread = null;
392 mCameraDisabled = true;
393 Util.showErrorAndFinish(mActivity,
394 R.string.camera_disabled);
395 break;
396 }
Michael Kolb78a5b222013-04-30 15:34:19 -0700397 case CAPTURE_ANIMATION_DONE: {
398 mUI.enablePreviewThumb(false);
399 break;
400 }
Michael Kolb8872c232013-01-29 10:33:22 -0800401 }
402 }
403 }
404
405 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700406 public void init(CameraActivity activity, View parent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800407 mActivity = activity;
Michael Kolbd6954f32013-03-08 20:43:01 -0800408 mUI = new PhotoUI(activity, this, parent);
Michael Kolb8872c232013-01-29 10:33:22 -0800409 mPreferences = new ComboPreferences(mActivity);
410 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
411 mCameraId = getPreferredCameraId(mPreferences);
412
413 mContentResolver = mActivity.getContentResolver();
414
415 // To reduce startup time, open the camera and start the preview in
416 // another thread.
417 mCameraStartUpThread = new CameraStartUpThread();
418 mCameraStartUpThread.start();
419
Michael Kolb8872c232013-01-29 10:33:22 -0800420 // Surface texture is from camera screen nail and startPreview needs it.
421 // This must be done before startPreview.
422 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800423
424 mPreferences.setLocalId(mActivity, mCameraId);
425 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
426 // we need to reset exposure for the preview
427 resetExposureCompensation();
428 // Starting the preview needs preferences, camera screen nail, and
429 // focus area indicator.
430 mStartPreviewPrerequisiteReady.open();
431
432 initializeControlByIntent();
433 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Michael Kolbd6954f32013-03-08 20:43:01 -0800434 mLocationManager = new LocationManager(mActivity, mUI);
Angus Kong0d00a892013-03-26 11:40:40 -0700435 mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
Michael Kolbd6954f32013-03-08 20:43:01 -0800436 }
437
438 private void initializeControlByIntent() {
439 mUI.initializeControlByIntent();
440 if (mIsImageCaptureIntent) {
441 setupCaptureParams();
442 }
443 }
444
445 private void onPreviewStarted() {
446 mCameraStartUpThread = null;
447 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800448 startFaceDetection();
449 locationFirstRun();
Michael Kolb8872c232013-01-29 10:33:22 -0800450 }
451
452 // Prompt the user to pick to record location for the very first run of
453 // camera only
454 private void locationFirstRun() {
455 if (RecordLocationPreference.isSet(mPreferences)) {
456 return;
457 }
458 if (mActivity.isSecureCamera()) return;
459 // Check if the back camera exists
460 int backCameraId = CameraHolder.instance().getBackCameraId();
461 if (backCameraId == -1) {
462 // If there is no back camera, do not show the prompt.
463 return;
464 }
465
466 new AlertDialog.Builder(mActivity)
467 .setTitle(R.string.remember_location_title)
468 .setMessage(R.string.remember_location_prompt)
469 .setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() {
470 @Override
471 public void onClick(DialogInterface dialog, int arg1) {
472 setLocationPreference(RecordLocationPreference.VALUE_ON);
473 }
474 })
475 .setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() {
476 @Override
477 public void onClick(DialogInterface dialog, int arg1) {
478 dialog.cancel();
479 }
480 })
481 .setOnCancelListener(new DialogInterface.OnCancelListener() {
482 @Override
483 public void onCancel(DialogInterface dialog) {
484 setLocationPreference(RecordLocationPreference.VALUE_OFF);
485 }
486 })
487 .show();
488 }
489
490 private void setLocationPreference(String value) {
491 mPreferences.edit()
492 .putString(CameraSettings.KEY_RECORD_LOCATION, value)
493 .apply();
494 // TODO: Fix this to use the actual onSharedPreferencesChanged listener
495 // instead of invoking manually
496 onSharedPreferenceChanged();
497 }
498
Michael Kolbd6954f32013-03-08 20:43:01 -0800499 private void onCameraOpened() {
500 View root = mUI.getRootView();
Michael Kolb8872c232013-01-29 10:33:22 -0800501 // These depend on camera parameters.
Michael Kolbd6954f32013-03-08 20:43:01 -0800502
503 int width = root.getWidth();
504 int height = root.getHeight();
Doris Liu6a0de792013-02-26 10:54:25 -0800505 mFocusManager.setPreviewSize(width, height);
Michael Kolbd6954f32013-03-08 20:43:01 -0800506 openCameraCommon();
Michael Kolb8872c232013-01-29 10:33:22 -0800507 }
508
Michael Kolbd6954f32013-03-08 20:43:01 -0800509 private void switchCamera() {
510 if (mPaused) return;
511
512 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
513 mCameraId = mPendingSwitchCameraId;
514 mPendingSwitchCameraId = -1;
515 setCameraId(mCameraId);
516
517 // from onPause
518 closeCamera();
519 mUI.collapseCameraControls();
520 mUI.clearFaces();
521 if (mFocusManager != null) mFocusManager.removeMessages();
522
523 // Restart the camera and initialize the UI. From onCreate.
524 mPreferences.setLocalId(mActivity, mCameraId);
525 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
526 try {
527 mCameraDevice = Util.openCamera(mActivity, mCameraId);
528 mParameters = mCameraDevice.getParameters();
529 } catch (CameraHardwareException e) {
530 Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
531 return;
532 } catch (CameraDisabledException e) {
533 Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
534 return;
Doris Liu09106a42013-03-05 09:54:25 -0800535 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800536 initializeCapabilities();
537 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
538 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
539 mFocusManager.setMirror(mirror);
540 mFocusManager.setParameters(mInitialParams);
541 setupPreview();
542
Doris Liu6432cd62013-06-13 17:20:31 -0700543 // reset zoom value index
544 mZoomValue = 0;
Michael Kolbd6954f32013-03-08 20:43:01 -0800545 openCameraCommon();
546
547 if (ApiHelper.HAS_SURFACE_TEXTURE) {
548 // Start switch camera animation. Post a message because
549 // onFrameAvailable from the old camera may already exist.
550 mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
Doris Liu48239f42013-03-04 22:19:10 -0800551 }
552 }
553
Michael Kolbd6954f32013-03-08 20:43:01 -0800554 protected void setCameraId(int cameraId) {
555 ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
556 pref.setValue("" + cameraId);
557 }
558
559 // either open a new camera or switch cameras
560 private void openCameraCommon() {
Michael Kolb8872c232013-01-29 10:33:22 -0800561 loadCameraPreferences();
Michael Kolbd6954f32013-03-08 20:43:01 -0800562
563 mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
564 updateSceneMode();
565 showTapToFocusToastIfNeeded();
566
567
Michael Kolb8872c232013-01-29 10:33:22 -0800568 }
569
Michael Kolbd6954f32013-03-08 20:43:01 -0800570 public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800571 if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
Michael Kolbd6954f32013-03-08 20:43:01 -0800572 }
Michael Kolb8872c232013-01-29 10:33:22 -0800573
574 private void resetExposureCompensation() {
575 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
576 CameraSettings.EXPOSURE_DEFAULT_VALUE);
577 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
578 Editor editor = mPreferences.edit();
579 editor.putString(CameraSettings.KEY_EXPOSURE, "0");
580 editor.apply();
581 }
582 }
583
584 private void keepMediaProviderInstance() {
585 // We want to keep a reference to MediaProvider in camera's lifecycle.
586 // TODO: Utilize mMediaProviderClient instance to replace
587 // ContentResolver calls.
588 if (mMediaProviderClient == null) {
589 mMediaProviderClient = mContentResolver
590 .acquireContentProviderClient(MediaStore.AUTHORITY);
591 }
592 }
593
594 // Snapshots can only be taken after this is called. It should be called
595 // once only. We could have done these things in onCreate() but we want to
596 // make preview screen appear as soon as possible.
597 private void initializeFirstTime() {
598 if (mFirstTimeInitialized) return;
599
600 // Initialize location service.
601 boolean recordLocation = RecordLocationPreference.get(
602 mPreferences, mContentResolver);
603 mLocationManager.recordLocation(recordLocation);
604
605 keepMediaProviderInstance();
606
Michael Kolbd6954f32013-03-08 20:43:01 -0800607 mUI.initializeFirstTime();
Angus Kong86d36312013-01-31 18:22:44 -0800608 MediaSaveService s = mActivity.getMediaSaveService();
609 // We set the listener only when both service and shutterbutton
610 // are initialized.
611 if (s != null) {
612 s.setListener(this);
613 }
Michael Kolb8872c232013-01-29 10:33:22 -0800614
Michael Kolb8872c232013-01-29 10:33:22 -0800615 mNamedImages = new NamedImages();
616
617 mFirstTimeInitialized = true;
618 addIdleHandler();
619
620 mActivity.updateStorageSpaceAndHint();
621 }
622
Michael Kolbd6954f32013-03-08 20:43:01 -0800623 // If the activity is paused and resumed, this method will be called in
624 // onResume.
625 private void initializeSecondTime() {
626 // Start location update if needed.
627 boolean recordLocation = RecordLocationPreference.get(
628 mPreferences, mContentResolver);
629 mLocationManager.recordLocation(recordLocation);
630 MediaSaveService s = mActivity.getMediaSaveService();
631 if (s != null) {
632 s.setListener(this);
633 }
634 mNamedImages = new NamedImages();
635 mUI.initializeSecondTime(mParameters);
636 keepMediaProviderInstance();
637 }
638
639 @Override
640 public void onSurfaceCreated(SurfaceHolder holder) {
641 // Do not access the camera if camera start up thread is not finished.
642 if (mCameraDevice == null || mCameraStartUpThread != null)
643 return;
644
645 mCameraDevice.setPreviewDisplayAsync(holder);
646 // This happens when onConfigurationChanged arrives, surface has been
647 // destroyed, and there is no onFullScreenChanged.
648 if (mCameraState == PREVIEW_STOPPED) {
649 setupPreview();
650 }
651 }
652
Michael Kolb8872c232013-01-29 10:33:22 -0800653 private void showTapToFocusToastIfNeeded() {
654 // Show the tap to focus toast if this is the first start.
655 if (mFocusAreaSupported &&
656 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
657 // Delay the toast for one second to wait for orientation.
658 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
659 }
660 }
661
662 private void addIdleHandler() {
663 MessageQueue queue = Looper.myQueue();
664 queue.addIdleHandler(new MessageQueue.IdleHandler() {
665 @Override
666 public boolean queueIdle() {
667 Storage.ensureOSXCompatible();
668 return false;
669 }
670 });
671 }
672
Michael Kolb8872c232013-01-29 10:33:22 -0800673 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
674 @Override
675 public void startFaceDetection() {
676 if (!ApiHelper.HAS_FACE_DETECTION) return;
677 if (mFaceDetectionStarted) return;
678 if (mParameters.getMaxNumDetectedFaces() > 0) {
679 mFaceDetectionStarted = true;
Michael Kolb8872c232013-01-29 10:33:22 -0800680 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Michael Kolbd6954f32013-03-08 20:43:01 -0800681 mUI.onStartFaceDetection(mDisplayOrientation,
682 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
683 mCameraDevice.setFaceDetectionListener(mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800684 mCameraDevice.startFaceDetection();
685 }
686 }
687
688 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
689 @Override
690 public void stopFaceDetection() {
691 if (!ApiHelper.HAS_FACE_DETECTION) return;
692 if (!mFaceDetectionStarted) return;
693 if (mParameters.getMaxNumDetectedFaces() > 0) {
694 mFaceDetectionStarted = false;
695 mCameraDevice.setFaceDetectionListener(null);
696 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800697 mUI.clearFaces();
Michael Kolb8872c232013-01-29 10:33:22 -0800698 }
699 }
700
Michael Kolb8872c232013-01-29 10:33:22 -0800701 private final class ShutterCallback
702 implements android.hardware.Camera.ShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700703
704 private boolean mAnimateFlash;
705
706 public ShutterCallback(boolean animateFlash) {
707 mAnimateFlash = animateFlash;
708 }
709
Michael Kolb8872c232013-01-29 10:33:22 -0800710 @Override
711 public void onShutter() {
712 mShutterCallbackTime = System.currentTimeMillis();
713 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
714 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Angus Kongdcb0ef12013-03-25 23:11:43 -0700715 if (mAnimateFlash) {
716 mActivity.runOnUiThread(mFlashRunnable);
717 }
Michael Kolb8872c232013-01-29 10:33:22 -0800718 }
719 }
720
721 private final class PostViewPictureCallback implements PictureCallback {
722 @Override
723 public void onPictureTaken(
724 byte [] data, android.hardware.Camera camera) {
725 mPostViewPictureCallbackTime = System.currentTimeMillis();
726 Log.v(TAG, "mShutterToPostViewCallbackTime = "
727 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
728 + "ms");
729 }
730 }
731
732 private final class RawPictureCallback implements PictureCallback {
733 @Override
734 public void onPictureTaken(
735 byte [] rawData, android.hardware.Camera camera) {
736 mRawPictureCallbackTime = System.currentTimeMillis();
737 Log.v(TAG, "mShutterToRawCallbackTime = "
738 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
739 }
740 }
741
742 private final class JpegPictureCallback implements PictureCallback {
743 Location mLocation;
744
745 public JpegPictureCallback(Location loc) {
746 mLocation = loc;
747 }
748
749 @Override
750 public void onPictureTaken(
751 final byte [] jpegData, final android.hardware.Camera camera) {
752 if (mPaused) {
753 return;
754 }
Doris Liu6432cd62013-06-13 17:20:31 -0700755 //TODO: We should show the picture taken rather than frozen preview here
756 if (mIsImageCaptureIntent) {
757 stopPreview();
758 }
Michael Kolb8872c232013-01-29 10:33:22 -0800759 if (mSceneMode == Util.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700760 mUI.showSwitcher();
761 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800762 }
763
764 mJpegPictureCallbackTime = System.currentTimeMillis();
765 // If postview callback has arrived, the captured image is displayed
766 // in postview callback. If not, the captured image is displayed in
767 // raw picture callback.
768 if (mPostViewPictureCallbackTime != 0) {
769 mShutterToPictureDisplayedTime =
770 mPostViewPictureCallbackTime - mShutterCallbackTime;
771 mPictureDisplayedToJpegCallbackTime =
772 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
773 } else {
774 mShutterToPictureDisplayedTime =
775 mRawPictureCallbackTime - mShutterCallbackTime;
776 mPictureDisplayedToJpegCallbackTime =
777 mJpegPictureCallbackTime - mRawPictureCallbackTime;
778 }
779 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
780 + mPictureDisplayedToJpegCallbackTime + "ms");
781
Doris Liu6432cd62013-06-13 17:20:31 -0700782 /*TODO:
Michael Kolb8872c232013-01-29 10:33:22 -0800783 // Only animate when in full screen capture mode
784 // i.e. If monkey/a user swipes to the gallery during picture taking,
785 // don't show animation
786 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
787 && mActivity.mShowCameraAppView) {
788 // Finish capture animation
Michael Kolb78a5b222013-04-30 15:34:19 -0700789 mHandler.removeMessages(CAPTURE_ANIMATION_DONE);
Michael Kolb8872c232013-01-29 10:33:22 -0800790 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSlide();
Michael Kolb78a5b222013-04-30 15:34:19 -0700791 mHandler.sendEmptyMessageDelayed(CAPTURE_ANIMATION_DONE,
792 CaptureAnimManager.getAnimationDuration());
Doris Liu6432cd62013-06-13 17:20:31 -0700793 } */
Michael Kolb8872c232013-01-29 10:33:22 -0800794 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
795 if (!mIsImageCaptureIntent) {
796 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
797 setupPreview();
798 } else {
799 // Camera HAL of some devices have a bug. Starting preview
800 // immediately after taking a picture will fail. Wait some
801 // time before starting the preview.
802 mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
803 }
804 }
805
806 if (!mIsImageCaptureIntent) {
807 // Calculate the width and the height of the jpeg.
808 Size s = mParameters.getPictureSize();
Angus Kong0d00a892013-03-26 11:40:40 -0700809 ExifInterface exif = Exif.getExif(jpegData);
810 int orientation = Exif.getOrientation(exif);
Michael Kolb8872c232013-01-29 10:33:22 -0800811 int width, height;
812 if ((mJpegRotation + orientation) % 180 == 0) {
813 width = s.width;
814 height = s.height;
815 } else {
816 width = s.height;
817 height = s.width;
818 }
819 String title = mNamedImages.getTitle();
820 long date = mNamedImages.getDate();
821 if (title == null) {
822 Log.e(TAG, "Unbalanced name/data pair");
823 } else {
824 if (date == -1) date = mCaptureStartTime;
Angus Kong0d00a892013-03-26 11:40:40 -0700825 if (mHeading >= 0) {
826 // heading direction has been updated by the sensor.
827 ExifTag directionRefTag = exif.buildTag(
828 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
829 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
830 ExifTag directionTag = exif.buildTag(
831 ExifInterface.TAG_GPS_IMG_DIRECTION,
832 new Rational(mHeading, 1));
833 exif.setTag(directionRefTag);
834 exif.setTag(directionTag);
835 }
Angus Kong86d36312013-01-31 18:22:44 -0800836 mActivity.getMediaSaveService().addImage(
837 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700838 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800839 }
840 } else {
841 mJpegImageData = jpegData;
842 if (!mQuickCapture) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800843 mUI.showPostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -0800844 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800845 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800846 }
847 }
848
849 // Check this in advance of each shot so we don't add to shutter
850 // latency. It's true that someone else could write to the SD card in
851 // the mean time and fill it, but that could have happened between the
852 // shutter press and saving the JPEG too.
853 mActivity.updateStorageSpaceAndHint();
854
855 long now = System.currentTimeMillis();
856 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
857 Log.v(TAG, "mJpegCallbackFinishTime = "
858 + mJpegCallbackFinishTime + "ms");
859 mJpegPictureCallbackTime = 0;
860 }
861 }
862
863 private final class AutoFocusCallback
864 implements android.hardware.Camera.AutoFocusCallback {
865 @Override
866 public void onAutoFocus(
867 boolean focused, android.hardware.Camera camera) {
868 if (mPaused) return;
869
870 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
871 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
872 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800873 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb8872c232013-01-29 10:33:22 -0800874 }
875 }
876
877 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
878 private final class AutoFocusMoveCallback
879 implements android.hardware.Camera.AutoFocusMoveCallback {
880 @Override
881 public void onAutoFocusMoving(
882 boolean moving, android.hardware.Camera camera) {
883 mFocusManager.onAutoFocusMoving(moving);
884 }
885 }
886
887 private static class NamedImages {
888 private ArrayList<NamedEntity> mQueue;
889 private boolean mStop;
890 private NamedEntity mNamedEntity;
891
892 public NamedImages() {
893 mQueue = new ArrayList<NamedEntity>();
894 }
895
896 public void nameNewImage(ContentResolver resolver, long date) {
897 NamedEntity r = new NamedEntity();
898 r.title = Util.createJpegName(date);
899 r.date = date;
900 mQueue.add(r);
901 }
902
903 public String getTitle() {
904 if (mQueue.isEmpty()) {
905 mNamedEntity = null;
906 return null;
907 }
908 mNamedEntity = mQueue.get(0);
909 mQueue.remove(0);
910
911 return mNamedEntity.title;
912 }
913
914 // Must be called after getTitle().
915 public long getDate() {
916 if (mNamedEntity == null) return -1;
917 return mNamedEntity.date;
918 }
919
920 private static class NamedEntity {
921 String title;
922 long date;
923 }
924 }
925
926 private void setCameraState(int state) {
927 mCameraState = state;
928 switch (state) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800929 case PhotoController.PREVIEW_STOPPED:
930 case PhotoController.SNAPSHOT_IN_PROGRESS:
Michael Kolbd6954f32013-03-08 20:43:01 -0800931 case PhotoController.SWITCHING_CAMERA:
932 mUI.enableGestures(false);
933 break;
934 case PhotoController.IDLE:
Doris Liu6432cd62013-06-13 17:20:31 -0700935 mUI.enableGestures(true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800936 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800937 }
938 }
939
940 private void animateFlash() {
941 // Only animate when in full screen capture mode
942 // i.e. If monkey/a user swipes to the gallery during picture taking,
943 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -0700944 if (!mIsImageCaptureIntent) {
945 mUI.animateFlash();
946
947 // TODO: mUI.enablePreviewThumb(true);
948 // mHandler.sendEmptyMessageDelayed(CAPTURE_ANIMATION_DONE,
949 // CaptureAnimManager.getAnimationDuration());
950 }
Michael Kolb8872c232013-01-29 10:33:22 -0800951 }
952
953 @Override
954 public boolean capture() {
955 // If we are already in the middle of taking a snapshot or the image save request
956 // is full then ignore.
957 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Angus Kong86d36312013-01-31 18:22:44 -0800958 || mCameraState == SWITCHING_CAMERA
959 || mActivity.getMediaSaveService().isQueueFull()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800960 return false;
961 }
962 mCaptureStartTime = System.currentTimeMillis();
963 mPostViewPictureCallbackTime = 0;
964 mJpegImageData = null;
965
966 final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR);
967
968 if (animateBefore) {
969 animateFlash();
970 }
971
972 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800973 int orientation;
974 // We need to be consistent with the framework orientation (i.e. the
975 // orientation of the UI.) when the auto-rotate screen setting is on.
976 if (mActivity.isAutoRotateScreen()) {
977 orientation = (360 - mDisplayRotation) % 360;
978 } else {
979 orientation = mOrientation;
980 }
981 mJpegRotation = Util.getJpegRotation(mCameraId, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800982 mParameters.setRotation(mJpegRotation);
983 Location loc = mLocationManager.getCurrentLocation();
984 Util.setGpsParameters(mParameters, loc);
985 mCameraDevice.setParameters(mParameters);
986
Angus Kongdcb0ef12013-03-25 23:11:43 -0700987 mCameraDevice.takePicture2(new ShutterCallback(!animateBefore),
988 mRawPictureCallback, mPostViewPictureCallback,
989 new JpegPictureCallback(loc), mCameraState,
990 mFocusManager.getFocusState());
Michael Kolb8872c232013-01-29 10:33:22 -0800991
992 mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime);
993
994 mFaceDetectionStarted = false;
995 setCameraState(SNAPSHOT_IN_PROGRESS);
Bobby Georgescu301b6462013-04-01 15:33:17 -0700996 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
997 UsageStatistics.ACTION_CAPTURE_DONE, "Photo");
Michael Kolb8872c232013-01-29 10:33:22 -0800998 return true;
999 }
1000
1001 @Override
1002 public void setFocusParameters() {
1003 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1004 }
1005
1006 private int getPreferredCameraId(ComboPreferences preferences) {
1007 int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
1008 if (intentCameraId != -1) {
1009 // Testing purpose. Launch a specific camera through the intent
1010 // extras.
1011 return intentCameraId;
1012 } else {
1013 return CameraSettings.readPreferredCameraId(preferences);
1014 }
1015 }
1016
Michael Kolbd6954f32013-03-08 20:43:01 -08001017 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001018 // If scene mode is set, we cannot set flash mode, white balance, and
1019 // focus mode, instead, we read it from driver
1020 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1021 overrideCameraSettings(mParameters.getFlashMode(),
1022 mParameters.getWhiteBalance(), mParameters.getFocusMode());
1023 } else {
1024 overrideCameraSettings(null, null, null);
1025 }
1026 }
1027
1028 private void overrideCameraSettings(final String flashMode,
1029 final String whiteBalance, final String focusMode) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001030 mUI.overrideSettings(
1031 CameraSettings.KEY_FLASH_MODE, flashMode,
1032 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
1033 CameraSettings.KEY_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001034 }
1035
1036 private void loadCameraPreferences() {
1037 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
1038 mCameraId, CameraHolder.instance().getCameraInfo());
1039 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
1040 }
1041
1042 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001043 public void onOrientationChanged(int orientation) {
1044 // We keep the last known orientation. So if the user first orient
1045 // the camera then point the camera to floor or sky, we still have
1046 // the correct orientation.
1047 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
1048 mOrientation = Util.roundOrientation(orientation, mOrientation);
1049
1050 // Show the toast after getting the first orientation changed.
1051 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1052 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1053 showTapToFocusToast();
1054 }
1055 }
1056
1057 @Override
1058 public void onStop() {
1059 if (mMediaProviderClient != null) {
1060 mMediaProviderClient.release();
1061 mMediaProviderClient = null;
1062 }
1063 }
1064
Michael Kolbd6954f32013-03-08 20:43:01 -08001065 @Override
1066 public void onCaptureCancelled() {
1067 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1068 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001069 }
1070
Michael Kolbd6954f32013-03-08 20:43:01 -08001071 @Override
1072 public void onCaptureRetake() {
Michael Kolb8872c232013-01-29 10:33:22 -08001073 if (mPaused)
1074 return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001075 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001076 setupPreview();
1077 }
1078
Michael Kolbd6954f32013-03-08 20:43:01 -08001079 @Override
1080 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001081 if (mPaused) {
1082 return;
1083 }
1084
1085 byte[] data = mJpegImageData;
1086
1087 if (mCropValue == null) {
1088 // First handle the no crop case -- just return the value. If the
1089 // caller specifies a "save uri" then write the data to its
1090 // stream. Otherwise, pass back a scaled down version of the bitmap
1091 // directly in the extras.
1092 if (mSaveUri != null) {
1093 OutputStream outputStream = null;
1094 try {
1095 outputStream = mContentResolver.openOutputStream(mSaveUri);
1096 outputStream.write(data);
1097 outputStream.close();
1098
1099 mActivity.setResultEx(Activity.RESULT_OK);
1100 mActivity.finish();
1101 } catch (IOException ex) {
1102 // ignore exception
1103 } finally {
1104 Util.closeSilently(outputStream);
1105 }
1106 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001107 ExifInterface exif = Exif.getExif(data);
1108 int orientation = Exif.getOrientation(exif);
Michael Kolb8872c232013-01-29 10:33:22 -08001109 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
1110 bitmap = Util.rotate(bitmap, orientation);
1111 mActivity.setResultEx(Activity.RESULT_OK,
1112 new Intent("inline-data").putExtra("data", bitmap));
1113 mActivity.finish();
1114 }
1115 } else {
1116 // Save the image to a temp file and invoke the cropper
1117 Uri tempUri = null;
1118 FileOutputStream tempStream = null;
1119 try {
1120 File path = mActivity.getFileStreamPath(sTempCropFilename);
1121 path.delete();
1122 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1123 tempStream.write(data);
1124 tempStream.close();
1125 tempUri = Uri.fromFile(path);
1126 } catch (FileNotFoundException ex) {
1127 mActivity.setResultEx(Activity.RESULT_CANCELED);
1128 mActivity.finish();
1129 return;
1130 } catch (IOException ex) {
1131 mActivity.setResultEx(Activity.RESULT_CANCELED);
1132 mActivity.finish();
1133 return;
1134 } finally {
1135 Util.closeSilently(tempStream);
1136 }
1137
1138 Bundle newExtras = new Bundle();
1139 if (mCropValue.equals("circle")) {
1140 newExtras.putString("circleCrop", "true");
1141 }
1142 if (mSaveUri != null) {
1143 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1144 } else {
1145 newExtras.putBoolean(CropExtras.KEY_RETURN_DATA, true);
1146 }
1147 if (mActivity.isSecureCamera()) {
1148 newExtras.putBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, true);
1149 }
1150
Ruben Brunk88741b42013-05-28 12:34:14 -07001151 Intent cropIntent = new Intent(CropActivity.CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001152
1153 cropIntent.setData(tempUri);
1154 cropIntent.putExtras(newExtras);
1155
1156 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1157 }
1158 }
1159
Michael Kolb8872c232013-01-29 10:33:22 -08001160 @Override
1161 public void onShutterButtonFocus(boolean pressed) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001162 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001163 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1164 || (mCameraState == PREVIEW_STOPPED)) return;
1165
1166 // Do not do focus if there is not enough storage.
1167 if (pressed && !canTakePicture()) return;
1168
1169 if (pressed) {
Michael Kolb8872c232013-01-29 10:33:22 -08001170 mFocusManager.onShutterDown();
1171 } else {
Doris Liuda50e052013-02-07 14:36:32 -08001172 // for countdown mode, we need to postpone the shutter release
1173 // i.e. lock the focus during countdown.
Michael Kolbd6954f32013-03-08 20:43:01 -08001174 if (!mUI.isCountingDown()) {
Doris Liuda50e052013-02-07 14:36:32 -08001175 mFocusManager.onShutterUp();
1176 }
Michael Kolb8872c232013-01-29 10:33:22 -08001177 }
1178 }
1179
1180 @Override
1181 public void onShutterButtonClick() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001182 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001183 || (mCameraState == SWITCHING_CAMERA)
1184 || (mCameraState == PREVIEW_STOPPED)) return;
1185
1186 // Do not take the picture if there is not enough storage.
1187 if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
1188 Log.i(TAG, "Not enough space or storage not ready. remaining="
1189 + mActivity.getStorageSpace());
1190 return;
1191 }
1192 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1193
Doris Liu9cdfe002013-04-16 09:50:56 -07001194 if (mSceneMode == Util.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001195 mUI.hideSwitcher();
1196 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001197 }
Michael Kolb8872c232013-01-29 10:33:22 -08001198 // If the user wants to do a snapshot while the previous one is still
1199 // in progress, remember the fact and do it after we finish the previous
1200 // one and re-start the preview. Snapshot in progress also includes the
1201 // state that autofocus is focusing and a picture will be taken when
1202 // focus callback arrives.
1203 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1204 && !mIsImageCaptureIntent) {
1205 mSnapshotOnIdle = true;
1206 return;
1207 }
1208
1209 String timer = mPreferences.getString(
1210 CameraSettings.KEY_TIMER,
1211 mActivity.getString(R.string.pref_camera_timer_default));
1212 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1213 mActivity.getString(R.string.pref_camera_timer_sound_default))
1214 .equals(mActivity.getString(R.string.setting_on_value));
1215
1216 int seconds = Integer.parseInt(timer);
1217 // When shutter button is pressed, check whether the previous countdown is
1218 // finished. If not, cancel the previous countdown and start a new one.
Michael Kolbd6954f32013-03-08 20:43:01 -08001219 if (mUI.isCountingDown()) {
1220 mUI.cancelCountDown();
1221 }
1222 if (seconds > 0) {
1223 mUI.startCountDown(seconds, playSound);
Michael Kolb8872c232013-01-29 10:33:22 -08001224 } else {
1225 mSnapshotOnIdle = false;
1226 mFocusManager.doSnap();
1227 }
1228 }
1229
1230 @Override
1231 public void installIntentFilter() {
1232 }
1233
1234 @Override
1235 public boolean updateStorageHintOnResume() {
1236 return mFirstTimeInitialized;
1237 }
1238
1239 @Override
1240 public void updateCameraAppView() {
1241 }
1242
1243 @Override
1244 public void onResumeBeforeSuper() {
1245 mPaused = false;
1246 }
1247
1248 @Override
1249 public void onResumeAfterSuper() {
1250 if (mOpenCameraFail || mCameraDisabled) return;
1251
1252 mJpegPictureCallbackTime = 0;
1253 mZoomValue = 0;
Michael Kolb8872c232013-01-29 10:33:22 -08001254 // Start the preview if it is not started.
1255 if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
1256 resetExposureCompensation();
1257 mCameraStartUpThread = new CameraStartUpThread();
1258 mCameraStartUpThread.start();
1259 }
1260
1261 // If first time initialization is not finished, put it in the
1262 // message queue.
1263 if (!mFirstTimeInitialized) {
1264 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1265 } else {
1266 initializeSecondTime();
1267 }
1268 keepScreenOnAwhile();
1269
1270 // Dismiss open menu if exists.
1271 PopupManager.getInstance(mActivity).notifyShowPopup(null);
Bobby Georgescu0a7dd572013-03-12 22:45:17 -07001272 UsageStatistics.onContentViewChanged(
1273 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
Angus Kong0d00a892013-03-26 11:40:40 -07001274
1275 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1276 if (gsensor != null) {
1277 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1278 }
1279
1280 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1281 if (msensor != null) {
1282 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1283 }
Michael Kolb8872c232013-01-29 10:33:22 -08001284 }
1285
1286 void waitCameraStartUpThread() {
1287 try {
1288 if (mCameraStartUpThread != null) {
1289 mCameraStartUpThread.cancel();
1290 mCameraStartUpThread.join();
1291 mCameraStartUpThread = null;
1292 setCameraState(IDLE);
1293 }
1294 } catch (InterruptedException e) {
1295 // ignore
1296 }
1297 }
1298
1299 @Override
1300 public void onPauseBeforeSuper() {
1301 mPaused = true;
Angus Kong0d00a892013-03-26 11:40:40 -07001302 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1303 if (gsensor != null) {
1304 mSensorManager.unregisterListener(this, gsensor);
1305 }
1306
1307 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1308 if (msensor != null) {
1309 mSensorManager.unregisterListener(this, msensor);
1310 }
Michael Kolb8872c232013-01-29 10:33:22 -08001311 }
1312
1313 @Override
1314 public void onPauseAfterSuper() {
1315 // Wait the camera start up thread to finish.
1316 waitCameraStartUpThread();
1317
1318 // When camera is started from secure lock screen for the first time
1319 // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1320 // To reduce the latency, keep the camera for a short time so it does
1321 // not need to be opened again.
1322 if (mCameraDevice != null && mActivity.isSecureCamera()
1323 && ActivityBase.isFirstStartAfterScreenOn()) {
1324 ActivityBase.resetFirstStartAfterScreenOn();
1325 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1326 }
1327 // Reset the focus first. Camera CTS does not guarantee that
1328 // cancelAutoFocus is allowed after preview stops.
1329 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1330 mCameraDevice.cancelAutoFocus();
1331 }
1332 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001333
Angus Kongce5480e2013-01-29 17:43:48 -08001334 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001335
1336 if (mLocationManager != null) mLocationManager.recordLocation(false);
1337
1338 // If we are in an image capture intent and has taken
1339 // a picture, we just clear it in onPause.
1340 mJpegImageData = null;
1341
1342 // Remove the messages in the event queue.
1343 mHandler.removeMessages(SETUP_PREVIEW);
1344 mHandler.removeMessages(FIRST_TIME_INIT);
1345 mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
1346 mHandler.removeMessages(SWITCH_CAMERA);
1347 mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
1348 mHandler.removeMessages(CAMERA_OPEN_DONE);
1349 mHandler.removeMessages(START_PREVIEW_DONE);
1350 mHandler.removeMessages(OPEN_CAMERA_FAIL);
1351 mHandler.removeMessages(CAMERA_DISABLED);
1352
Michael Kolbd6954f32013-03-08 20:43:01 -08001353 closeCamera();
1354
1355 resetScreenOn();
1356 mUI.onPause();
1357
Michael Kolb8872c232013-01-29 10:33:22 -08001358 mPendingSwitchCameraId = -1;
1359 if (mFocusManager != null) mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001360 MediaSaveService s = mActivity.getMediaSaveService();
1361 if (s != null) {
1362 s.setListener(null);
1363 }
Michael Kolb8872c232013-01-29 10:33:22 -08001364 }
1365
Michael Kolb8872c232013-01-29 10:33:22 -08001366 /**
1367 * The focus manager is the first UI related element to get initialized,
1368 * and it requires the RenderOverlay, so initialize it here
1369 */
1370 private void initializeFocusManager() {
1371 // Create FocusManager object. startPreview needs it.
Michael Kolb8872c232013-01-29 10:33:22 -08001372 // if mFocusManager not null, reuse it
1373 // otherwise create a new instance
1374 if (mFocusManager != null) {
1375 mFocusManager.removeMessages();
1376 } else {
1377 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1378 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
1379 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1380 R.array.pref_camera_focusmode_default_array);
1381 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
1382 mInitialParams, this, mirror,
Michael Kolbd6954f32013-03-08 20:43:01 -08001383 mActivity.getMainLooper(), mUI);
Michael Kolb8872c232013-01-29 10:33:22 -08001384 }
1385 }
1386
Michael Kolb8872c232013-01-29 10:33:22 -08001387 @Override
1388 public void onConfigurationChanged(Configuration newConfig) {
1389 Log.v(TAG, "onConfigurationChanged");
1390 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001391 }
1392
1393 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001394 public void updateCameraOrientation() {
1395 if (mDisplayRotation != Util.getDisplayRotation(mActivity)) {
1396 setDisplayOrientation();
1397 }
1398 }
1399
1400 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001401 public void onActivityResult(
1402 int requestCode, int resultCode, Intent data) {
1403 switch (requestCode) {
1404 case REQUEST_CROP: {
1405 Intent intent = new Intent();
1406 if (data != null) {
1407 Bundle extras = data.getExtras();
1408 if (extras != null) {
1409 intent.putExtras(extras);
1410 }
1411 }
1412 mActivity.setResultEx(resultCode, intent);
1413 mActivity.finish();
1414
1415 File path = mActivity.getFileStreamPath(sTempCropFilename);
1416 path.delete();
1417
1418 break;
1419 }
1420 }
1421 }
1422
1423 private boolean canTakePicture() {
1424 return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
1425 }
1426
1427 @Override
1428 public void autoFocus() {
1429 mFocusStartTime = System.currentTimeMillis();
1430 mCameraDevice.autoFocus(mAutoFocusCallback);
1431 setCameraState(FOCUSING);
1432 }
1433
1434 @Override
1435 public void cancelAutoFocus() {
1436 mCameraDevice.cancelAutoFocus();
1437 setCameraState(IDLE);
1438 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1439 }
1440
1441 // Preview area is touched. Handle touch focus.
1442 @Override
1443 public void onSingleTapUp(View view, int x, int y) {
1444 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1445 || mCameraState == SNAPSHOT_IN_PROGRESS
1446 || mCameraState == SWITCHING_CAMERA
1447 || mCameraState == PREVIEW_STOPPED) {
1448 return;
1449 }
1450
1451 // Do not trigger touch focus if popup window is opened.
Michael Kolbd6954f32013-03-08 20:43:01 -08001452 if (mUI.removeTopLevelPopup()) return;
Michael Kolb8872c232013-01-29 10:33:22 -08001453
1454 // Check if metering area or focus area is supported.
1455 if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1456 mFocusManager.onSingleTapUp(x, y);
1457 }
1458
1459 @Override
1460 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001461 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001462 }
1463
1464 @Override
1465 public boolean onKeyDown(int keyCode, KeyEvent event) {
1466 switch (keyCode) {
1467 case KeyEvent.KEYCODE_VOLUME_UP:
1468 case KeyEvent.KEYCODE_VOLUME_DOWN:
1469 case KeyEvent.KEYCODE_FOCUS:
Doris Liu6432cd62013-06-13 17:20:31 -07001470 if (/*TODO: mActivity.isInCameraApp() &&*/ mFirstTimeInitialized) {
Michael Kolb8872c232013-01-29 10:33:22 -08001471 if (event.getRepeatCount() == 0) {
1472 onShutterButtonFocus(true);
1473 }
1474 return true;
1475 }
1476 return false;
1477 case KeyEvent.KEYCODE_CAMERA:
1478 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1479 onShutterButtonClick();
1480 }
1481 return true;
1482 case KeyEvent.KEYCODE_DPAD_CENTER:
1483 // If we get a dpad center event without any focused view, move
1484 // the focus to the shutter button and press it.
1485 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1486 // Start auto-focus immediately to reduce shutter lag. After
1487 // the shutter button gets the focus, onShutterButtonFocus()
1488 // will be called again but it is fine.
Michael Kolbd6954f32013-03-08 20:43:01 -08001489 if (mUI.removeTopLevelPopup()) return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001490 onShutterButtonFocus(true);
Michael Kolbd6954f32013-03-08 20:43:01 -08001491 mUI.pressShutterButton();
Michael Kolb8872c232013-01-29 10:33:22 -08001492 }
1493 return true;
1494 }
1495 return false;
1496 }
1497
1498 @Override
1499 public boolean onKeyUp(int keyCode, KeyEvent event) {
1500 switch (keyCode) {
1501 case KeyEvent.KEYCODE_VOLUME_UP:
1502 case KeyEvent.KEYCODE_VOLUME_DOWN:
Doris Liu6432cd62013-06-13 17:20:31 -07001503 if (/*mActivity.isInCameraApp() && */ mFirstTimeInitialized) {
Michael Kolb8872c232013-01-29 10:33:22 -08001504 onShutterButtonClick();
1505 return true;
1506 }
1507 return false;
1508 case KeyEvent.KEYCODE_FOCUS:
1509 if (mFirstTimeInitialized) {
1510 onShutterButtonFocus(false);
1511 }
1512 return true;
1513 }
1514 return false;
1515 }
1516
1517 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1518 private void closeCamera() {
1519 if (mCameraDevice != null) {
1520 mCameraDevice.setZoomChangeListener(null);
1521 if(ApiHelper.HAS_FACE_DETECTION) {
1522 mCameraDevice.setFaceDetectionListener(null);
1523 }
1524 mCameraDevice.setErrorCallback(null);
1525 CameraHolder.instance().release();
1526 mFaceDetectionStarted = false;
1527 mCameraDevice = null;
1528 setCameraState(PREVIEW_STOPPED);
1529 mFocusManager.onCameraReleased();
1530 }
1531 }
1532
1533 private void setDisplayOrientation() {
1534 mDisplayRotation = Util.getDisplayRotation(mActivity);
1535 mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001536 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001537 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001538 if (mFocusManager != null) {
1539 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1540 }
Doris Liu6432cd62013-06-13 17:20:31 -07001541 // Change the camera display orientation
1542 if (mCameraDevice != null) {
1543 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1544 }
Michael Kolb8872c232013-01-29 10:33:22 -08001545 }
1546
1547 // Only called by UI thread.
1548 private void setupPreview() {
1549 mFocusManager.resetTouchFocus();
1550 startPreview();
1551 setCameraState(IDLE);
1552 startFaceDetection();
1553 }
1554
1555 // This can be called by UI Thread or CameraStartUpThread. So this should
1556 // not modify the views.
1557 private void startPreview() {
1558 mCameraDevice.setErrorCallback(mErrorCallback);
1559
1560 // ICS camera frameworks has a bug. Face detection state is not cleared
1561 // after taking a picture. Stop the preview to work around it. The bug
1562 // was fixed in JB.
1563 if (mCameraState != PREVIEW_STOPPED) stopPreview();
1564
1565 setDisplayOrientation();
1566
1567 if (!mSnapshotOnIdle) {
1568 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1569 // resume it because it may have been paused by autoFocus call.
1570 if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
1571 mCameraDevice.cancelAutoFocus();
1572 }
1573 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1574 }
1575 setCameraParameters(UPDATE_PARAM_ALL);
Doris Liu6432cd62013-06-13 17:20:31 -07001576 // Let UI set its expected aspect ratio
1577 mUI.setPreviewSize(mParameters.getPreviewSize());
1578 Object st = mUI.getSurfaceTexture();
1579 if (st != null) {
1580 mCameraDevice.setPreviewTextureAsync((SurfaceTexture) st);
Michael Kolb8872c232013-01-29 10:33:22 -08001581 }
1582
1583 Log.v(TAG, "startPreview");
1584 mCameraDevice.startPreviewAsync();
Michael Kolb8872c232013-01-29 10:33:22 -08001585 mFocusManager.onPreviewStarted();
1586
1587 if (mSnapshotOnIdle) {
1588 mHandler.post(mDoSnapRunnable);
1589 }
1590 }
1591
Michael Kolbd6954f32013-03-08 20:43:01 -08001592 @Override
1593 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001594 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1595 Log.v(TAG, "stopPreview");
1596 mCameraDevice.stopPreview();
1597 mFaceDetectionStarted = false;
1598 }
1599 setCameraState(PREVIEW_STOPPED);
1600 if (mFocusManager != null) mFocusManager.onPreviewStopped();
1601 }
1602
1603 @SuppressWarnings("deprecation")
1604 private void updateCameraParametersInitialize() {
1605 // Reset preview frame rate to the maximum because it may be lowered by
1606 // video camera application.
Doris Liu6432cd62013-06-13 17:20:31 -07001607 int[] fpsRange = Util.getMaxPreviewFpsRange(mParameters);
1608 if (fpsRange.length > 0) {
1609 mParameters.setPreviewFpsRange(
1610 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1611 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001612 }
1613
1614 mParameters.set(Util.RECORDING_HINT, Util.FALSE);
1615
1616 // Disable video stabilization. Convenience methods not available in API
1617 // level <= 14
1618 String vstabSupported = mParameters.get("video-stabilization-supported");
1619 if ("true".equals(vstabSupported)) {
1620 mParameters.set("video-stabilization", "false");
1621 }
1622 }
1623
1624 private void updateCameraParametersZoom() {
1625 // Set zoom.
1626 if (mParameters.isZoomSupported()) {
1627 mParameters.setZoom(mZoomValue);
1628 }
1629 }
1630
1631 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1632 private void setAutoExposureLockIfSupported() {
1633 if (mAeLockSupported) {
1634 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1635 }
1636 }
1637
1638 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1639 private void setAutoWhiteBalanceLockIfSupported() {
1640 if (mAwbLockSupported) {
1641 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1642 }
1643 }
1644
1645 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1646 private void setFocusAreasIfSupported() {
1647 if (mFocusAreaSupported) {
1648 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1649 }
1650 }
1651
1652 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1653 private void setMeteringAreasIfSupported() {
1654 if (mMeteringAreaSupported) {
1655 // Use the same area for focus and metering.
1656 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1657 }
1658 }
1659
1660 private void updateCameraParametersPreference() {
1661 setAutoExposureLockIfSupported();
1662 setAutoWhiteBalanceLockIfSupported();
1663 setFocusAreasIfSupported();
1664 setMeteringAreasIfSupported();
1665
1666 // Set picture size.
1667 String pictureSize = mPreferences.getString(
1668 CameraSettings.KEY_PICTURE_SIZE, null);
1669 if (pictureSize == null) {
1670 CameraSettings.initialCameraPictureSize(mActivity, mParameters);
1671 } else {
1672 List<Size> supported = mParameters.getSupportedPictureSizes();
1673 CameraSettings.setCameraPictureSize(
1674 pictureSize, supported, mParameters);
1675 }
1676 Size size = mParameters.getPictureSize();
1677
1678 // Set a preview size that is closest to the viewfinder height and has
1679 // the right aspect ratio.
1680 List<Size> sizes = mParameters.getSupportedPreviewSizes();
1681 Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes,
1682 (double) size.width / size.height);
1683 Size original = mParameters.getPreviewSize();
1684 if (!original.equals(optimalSize)) {
1685 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
Doris Liu6432cd62013-06-13 17:20:31 -07001686
Michael Kolb8872c232013-01-29 10:33:22 -08001687 // Zoom related settings will be changed for different preview
1688 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07001689 if (mHandler.getLooper() == Looper.myLooper()) {
1690 // On UI thread only, not when camera starts up
1691 setupPreview();
1692 } else {
1693 mCameraDevice.setParameters(mParameters);
1694 }
Michael Kolb8872c232013-01-29 10:33:22 -08001695 mParameters = mCameraDevice.getParameters();
1696 }
1697 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1698
1699 // Since changing scene mode may change supported values, set scene mode
1700 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1701 // separate preference.
1702 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
1703 mActivity.getString(R.string.pref_camera_hdr_default));
1704 if (mActivity.getString(R.string.setting_on_value).equals(hdr)) {
1705 mSceneMode = Util.SCENE_MODE_HDR;
1706 } else {
1707 mSceneMode = mPreferences.getString(
1708 CameraSettings.KEY_SCENE_MODE,
1709 mActivity.getString(R.string.pref_camera_scenemode_default));
1710 }
1711 if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
1712 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1713 mParameters.setSceneMode(mSceneMode);
1714
1715 // Setting scene mode will change the settings of flash mode,
1716 // white balance, and focus mode. Here we read back the
1717 // parameters, so we can know those settings.
1718 mCameraDevice.setParameters(mParameters);
1719 mParameters = mCameraDevice.getParameters();
1720 }
1721 } else {
1722 mSceneMode = mParameters.getSceneMode();
1723 if (mSceneMode == null) {
1724 mSceneMode = Parameters.SCENE_MODE_AUTO;
1725 }
1726 }
1727
1728 // Set JPEG quality.
1729 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1730 CameraProfile.QUALITY_HIGH);
1731 mParameters.setJpegQuality(jpegQuality);
1732
1733 // For the following settings, we need to check if the settings are
1734 // still supported by latest driver, if not, ignore the settings.
1735
1736 // Set exposure compensation
1737 int value = CameraSettings.readExposure(mPreferences);
1738 int max = mParameters.getMaxExposureCompensation();
1739 int min = mParameters.getMinExposureCompensation();
1740 if (value >= min && value <= max) {
1741 mParameters.setExposureCompensation(value);
1742 } else {
1743 Log.w(TAG, "invalid exposure range: " + value);
1744 }
1745
1746 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1747 // Set flash mode.
1748 String flashMode = mPreferences.getString(
1749 CameraSettings.KEY_FLASH_MODE,
1750 mActivity.getString(R.string.pref_camera_flashmode_default));
1751 List<String> supportedFlash = mParameters.getSupportedFlashModes();
1752 if (Util.isSupported(flashMode, supportedFlash)) {
1753 mParameters.setFlashMode(flashMode);
1754 } else {
1755 flashMode = mParameters.getFlashMode();
1756 if (flashMode == null) {
1757 flashMode = mActivity.getString(
1758 R.string.pref_camera_flashmode_no_flash);
1759 }
1760 }
1761
1762 // Set white balance parameter.
1763 String whiteBalance = mPreferences.getString(
1764 CameraSettings.KEY_WHITE_BALANCE,
1765 mActivity.getString(R.string.pref_camera_whitebalance_default));
1766 if (Util.isSupported(whiteBalance,
1767 mParameters.getSupportedWhiteBalance())) {
1768 mParameters.setWhiteBalance(whiteBalance);
1769 } else {
1770 whiteBalance = mParameters.getWhiteBalance();
1771 if (whiteBalance == null) {
1772 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1773 }
1774 }
1775
1776 // Set focus mode.
1777 mFocusManager.overrideFocusMode(null);
1778 mParameters.setFocusMode(mFocusManager.getFocusMode());
1779 } else {
1780 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1781 }
1782
1783 if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1784 updateAutoFocusMoveCallback();
1785 }
1786 }
1787
1788 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1789 private void updateAutoFocusMoveCallback() {
1790 if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
1791 mCameraDevice.setAutoFocusMoveCallback(
1792 (AutoFocusMoveCallback) mAutoFocusMoveCallback);
1793 } else {
1794 mCameraDevice.setAutoFocusMoveCallback(null);
1795 }
1796 }
1797
1798 // We separate the parameters into several subsets, so we can update only
1799 // the subsets actually need updating. The PREFERENCE set needs extra
1800 // locking because the preference can be changed from GLThread as well.
1801 private void setCameraParameters(int updateSet) {
1802 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1803 updateCameraParametersInitialize();
1804 }
1805
1806 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1807 updateCameraParametersZoom();
1808 }
1809
1810 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
1811 updateCameraParametersPreference();
1812 }
1813
1814 mCameraDevice.setParameters(mParameters);
1815 }
1816
1817 // If the Camera is idle, update the parameters immediately, otherwise
1818 // accumulate them in mUpdateSet and update later.
1819 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1820 mUpdateSet |= additionalUpdateSet;
1821 if (mCameraDevice == null) {
1822 // We will update all the parameters when we open the device, so
1823 // we don't need to do anything now.
1824 mUpdateSet = 0;
1825 return;
1826 } else if (isCameraIdle()) {
1827 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001828 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001829 mUpdateSet = 0;
1830 } else {
1831 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1832 mHandler.sendEmptyMessageDelayed(
1833 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1834 }
1835 }
1836 }
1837
Michael Kolbd6954f32013-03-08 20:43:01 -08001838 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001839 return (mCameraState == IDLE) ||
1840 (mCameraState == PREVIEW_STOPPED) ||
1841 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1842 && (mCameraState != SWITCHING_CAMERA));
1843 }
1844
Michael Kolbd6954f32013-03-08 20:43:01 -08001845 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001846 String action = mActivity.getIntent().getAction();
1847 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
1848 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
1849 }
1850
1851 private void setupCaptureParams() {
1852 Bundle myExtras = mActivity.getIntent().getExtras();
1853 if (myExtras != null) {
1854 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1855 mCropValue = myExtras.getString("crop");
1856 }
1857 }
1858
Michael Kolb8872c232013-01-29 10:33:22 -08001859 @Override
1860 public void onSharedPreferenceChanged() {
1861 // ignore the events after "onPause()"
1862 if (mPaused) return;
1863
1864 boolean recordLocation = RecordLocationPreference.get(
1865 mPreferences, mContentResolver);
1866 mLocationManager.recordLocation(recordLocation);
1867
1868 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
Michael Kolbeb8adc12013-04-26 11:09:29 -07001869 mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, mPreferences);
Michael Kolb8872c232013-01-29 10:33:22 -08001870 }
1871
1872 @Override
1873 public void onCameraPickerClicked(int cameraId) {
1874 if (mPaused || mPendingSwitchCameraId != -1) return;
1875
1876 mPendingSwitchCameraId = cameraId;
Doris Liu6432cd62013-06-13 17:20:31 -07001877
1878 Log.v(TAG, "Start to switch camera. cameraId=" + cameraId);
1879 // We need to keep a preview frame for the animation before
1880 // releasing the camera. This will trigger onPreviewTextureCopied.
1881 //TODO: Need to animate the camera switch
1882 switchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -08001883 }
1884
Michael Kolb8872c232013-01-29 10:33:22 -08001885 // Preview texture has been copied. Now camera can be released and the
1886 // animation can be started.
1887 @Override
1888 public void onPreviewTextureCopied() {
1889 mHandler.sendEmptyMessage(SWITCH_CAMERA);
1890 }
1891
1892 @Override
1893 public void onCaptureTextureCopied() {
1894 }
1895
1896 @Override
1897 public void onUserInteraction() {
Doris Liu6432cd62013-06-13 17:20:31 -07001898 if (!mActivity.isFinishing()) keepScreenOnAwhile();
Michael Kolb8872c232013-01-29 10:33:22 -08001899 }
1900
1901 private void resetScreenOn() {
1902 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1903 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1904 }
1905
1906 private void keepScreenOnAwhile() {
1907 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1908 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1909 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1910 }
1911
Michael Kolb8872c232013-01-29 10:33:22 -08001912 @Override
1913 public void onOverriddenPreferencesClicked() {
1914 if (mPaused) return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001915 mUI.showPreferencesToast();
Michael Kolb8872c232013-01-29 10:33:22 -08001916 }
1917
1918 private void showTapToFocusToast() {
1919 // TODO: Use a toast?
1920 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1921 // Clear the preference.
1922 Editor editor = mPreferences.edit();
1923 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
1924 editor.apply();
1925 }
1926
1927 private void initializeCapabilities() {
1928 mInitialParams = mCameraDevice.getParameters();
1929 mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
1930 mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
1931 mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
1932 mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams);
1933 mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
1934 Util.FOCUS_MODE_CONTINUOUS_PICTURE);
1935 }
1936
Michael Kolb8872c232013-01-29 10:33:22 -08001937 @Override
1938 public void onCountDownFinished() {
1939 mSnapshotOnIdle = false;
1940 mFocusManager.doSnap();
Doris Liuda50e052013-02-07 14:36:32 -08001941 mFocusManager.onShutterUp();
Michael Kolb8872c232013-01-29 10:33:22 -08001942 }
1943
Michael Kolb8872c232013-01-29 10:33:22 -08001944 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001945 public void onShowSwitcherPopup() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001946 mUI.onShowSwitcherPopup();
Michael Kolb8872c232013-01-29 10:33:22 -08001947 }
1948
Angus Kongce5480e2013-01-29 17:43:48 -08001949 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001950 public int onZoomChanged(int index) {
1951 // Not useful to change zoom value when the activity is paused.
1952 if (mPaused) return index;
1953 mZoomValue = index;
1954 if (mParameters == null || mCameraDevice == null) return index;
1955 // Set zoom parameters asynchronously
1956 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07001957 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08001958 Parameters p = mCameraDevice.getParameters();
1959 if (p != null) return p.getZoom();
1960 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08001961 }
1962
1963 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001964 public int getCameraState() {
1965 return mCameraState;
1966 }
1967
1968 @Override
1969 public void onQueueStatus(boolean full) {
1970 mUI.enableShutter(!full);
Angus Kongce5480e2013-01-29 17:43:48 -08001971 }
Angus Kong86d36312013-01-31 18:22:44 -08001972
1973 @Override
1974 public void onMediaSaveServiceConnected(MediaSaveService s) {
1975 // We set the listener only when both service and shutterbutton
1976 // are initialized.
Michael Kolbd6954f32013-03-08 20:43:01 -08001977 if (mFirstTimeInitialized) {
1978 s.setListener(this);
1979 }
Angus Kong86d36312013-01-31 18:22:44 -08001980 }
Angus Kong0d00a892013-03-26 11:40:40 -07001981
1982 @Override
1983 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1984 }
1985
1986 @Override
1987 public void onSensorChanged(SensorEvent event) {
1988 int type = event.sensor.getType();
1989 float[] data;
1990 if (type == Sensor.TYPE_ACCELEROMETER) {
1991 data = mGData;
1992 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
1993 data = mMData;
1994 } else {
1995 // we should not be here.
1996 return;
1997 }
1998 for (int i = 0; i < 3 ; i++) {
1999 data[i] = event.values[i];
2000 }
2001 float[] orientation = new float[3];
2002 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2003 SensorManager.getOrientation(mR, orientation);
2004 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2005 if (mHeading < 0) {
2006 mHeading += 360;
2007 }
Angus Kong0d00a892013-03-26 11:40:40 -07002008 }
Doris Liu6432cd62013-06-13 17:20:31 -07002009
2010 @Override
2011 public void onSwitchMode(boolean toCamera) {
2012 mUI.onSwitchMode(toCamera);
2013 }
2014
2015/* Below is no longer needed, except to get rid of compile error
2016 * TODO: Remove these
2017 */
2018
2019 // TODO: Delete this function after old camera code is removed
2020 @Override
2021 public void onRestorePreferencesClicked() {}
2022
Michael Kolb8872c232013-01-29 10:33:22 -08002023}