blob: c7301bdc9b7167645c08d6243b9022d42de0f270 [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.MotionEvent;
53import android.view.OrientationEventListener;
54import android.view.SurfaceHolder;
55import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080056import android.view.WindowManager;
Michael Kolb8872c232013-01-29 10:33:22 -080057
58import com.android.camera.CameraManager.CameraProxy;
Michael Kolbd6954f32013-03-08 20:43:01 -080059import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
Michael Kolb8872c232013-01-29 10:33:22 -080060import com.android.camera.ui.PopupManager;
Michael Kolb8872c232013-01-29 10:33:22 -080061import com.android.camera.ui.RotateTextToast;
John Reck54987e82013-02-15 15:51:30 -080062import com.android.gallery3d.R;
Michael Kolb8872c232013-01-29 10:33:22 -080063import com.android.gallery3d.common.ApiHelper;
Angus Kong0d00a892013-03-26 11:40:40 -070064import com.android.gallery3d.exif.ExifInterface;
65import com.android.gallery3d.exif.ExifTag;
66import com.android.gallery3d.exif.Rational;
Michael Kolb8872c232013-01-29 10:33:22 -080067import com.android.gallery3d.filtershow.CropExtras;
68import com.android.gallery3d.filtershow.FilterShowActivity;
Bobby Georgescu0a7dd572013-03-12 22:45:17 -070069import com.android.gallery3d.util.UsageStatistics;
Michael Kolb8872c232013-01-29 10:33:22 -080070
71import java.io.File;
72import java.io.FileNotFoundException;
73import java.io.FileOutputStream;
74import java.io.IOException;
75import java.io.OutputStream;
76import java.util.ArrayList;
77import java.util.Collections;
78import java.util.Formatter;
79import java.util.List;
80
81public class PhotoModule
82 implements CameraModule,
Michael Kolbd6954f32013-03-08 20:43:01 -080083 PhotoController,
Michael Kolb8872c232013-01-29 10:33:22 -080084 FocusOverlayManager.Listener,
85 CameraPreference.OnPreferenceChangedListener,
Michael Kolb8872c232013-01-29 10:33:22 -080086 ShutterButton.OnShutterButtonListener,
Michael Kolbd6954f32013-03-08 20:43:01 -080087 MediaSaveService.Listener,
Angus Kong0d00a892013-03-26 11:40:40 -070088 OnCountDownFinishedListener,
89 SensorEventListener {
Michael Kolb8872c232013-01-29 10:33:22 -080090
91 private static final String TAG = "CAM_PhotoModule";
92
93 // We number the request code from 1000 to avoid collision with Gallery.
94 private static final int REQUEST_CROP = 1000;
95
96 private static final int SETUP_PREVIEW = 1;
97 private static final int FIRST_TIME_INIT = 2;
98 private static final int CLEAR_SCREEN_DELAY = 3;
99 private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
100 private static final int CHECK_DISPLAY_ROTATION = 5;
101 private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
102 private static final int SWITCH_CAMERA = 7;
103 private static final int SWITCH_CAMERA_START_ANIMATION = 8;
104 private static final int CAMERA_OPEN_DONE = 9;
105 private static final int START_PREVIEW_DONE = 10;
106 private static final int OPEN_CAMERA_FAIL = 11;
107 private static final int CAMERA_DISABLED = 12;
Michael Kolb8872c232013-01-29 10:33:22 -0800108
109 // The subset of parameters we need to update in setCameraParameters().
110 private static final int UPDATE_PARAM_INITIALIZE = 1;
111 private static final int UPDATE_PARAM_ZOOM = 2;
112 private static final int UPDATE_PARAM_PREFERENCE = 4;
113 private static final int UPDATE_PARAM_ALL = -1;
114
115 // This is the timeout to keep the camera in onPause for the first time
116 // after screen on if the activity is started from secure lock screen.
117 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
118
119 // copied from Camera hierarchy
120 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800121 private CameraProxy mCameraDevice;
122 private int mCameraId;
123 private Parameters mParameters;
124 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800125
126 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800127
128 // these are only used by Camera
129
130 // The activity is going to switch to the specified camera id. This is
131 // needed because texture copy is done in GL thread. -1 means camera is not
132 // switching.
133 protected int mPendingSwitchCameraId = -1;
134 private boolean mOpenCameraFail;
135 private boolean mCameraDisabled;
136
137 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
138 // needed to be updated in mUpdateSet.
139 private int mUpdateSet;
140
141 private static final int SCREEN_DELAY = 2 * 60 * 1000;
142
143 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800144
145 private Parameters mInitialParams;
146 private boolean mFocusAreaSupported;
147 private boolean mMeteringAreaSupported;
148 private boolean mAeLockSupported;
149 private boolean mAwbLockSupported;
150 private boolean mContinousFocusSupported;
151
152 // The degrees of the device rotated clockwise from its natural orientation.
153 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
154 private ComboPreferences mPreferences;
155
156 private static final String sTempCropFilename = "crop-temp";
157
158 private ContentProviderClient mMediaProviderClient;
Michael Kolb8872c232013-01-29 10:33:22 -0800159 private boolean mFaceDetectionStarted = false;
160
Michael Kolb8872c232013-01-29 10:33:22 -0800161 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
162 private String mCropValue;
163 private Uri mSaveUri;
164
Angus Kongce5480e2013-01-29 17:43:48 -0800165 // We use a queue to generated names of the images to be used later
166 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800167 private NamedImages mNamedImages;
168
169 private Runnable mDoSnapRunnable = new Runnable() {
170 @Override
171 public void run() {
172 onShutterButtonClick();
173 }
174 };
175
Angus Kongdcb0ef12013-03-25 23:11:43 -0700176 private Runnable mFlashRunnable = new Runnable() {
177 @Override
178 public void run() {
179 animateFlash();
180 }
181 };
182
Michael Kolb8872c232013-01-29 10:33:22 -0800183 private final StringBuilder mBuilder = new StringBuilder();
184 private final Formatter mFormatter = new Formatter(mBuilder);
185 private final Object[] mFormatterArgs = new Object[1];
186
187 /**
188 * An unpublished intent flag requesting to return as soon as capturing
189 * is completed.
190 *
191 * TODO: consider publishing by moving into MediaStore.
192 */
193 private static final String EXTRA_QUICK_CAPTURE =
194 "android.intent.extra.quickCapture";
195
196 // The display rotation in degrees. This is only valid when mCameraState is
197 // not PREVIEW_STOPPED.
198 private int mDisplayRotation;
199 // The value for android.hardware.Camera.setDisplayOrientation.
200 private int mCameraDisplayOrientation;
201 // The value for UI components like indicators.
202 private int mDisplayOrientation;
203 // The value for android.hardware.Camera.Parameters.setRotation.
204 private int mJpegRotation;
205 private boolean mFirstTimeInitialized;
206 private boolean mIsImageCaptureIntent;
207
Michael Kolb8872c232013-01-29 10:33:22 -0800208 private int mCameraState = PREVIEW_STOPPED;
209 private boolean mSnapshotOnIdle = false;
210
211 private ContentResolver mContentResolver;
212
213 private LocationManager mLocationManager;
214
Michael Kolb8872c232013-01-29 10:33:22 -0800215 private final PostViewPictureCallback mPostViewPictureCallback =
216 new PostViewPictureCallback();
217 private final RawPictureCallback mRawPictureCallback =
218 new RawPictureCallback();
219 private final AutoFocusCallback mAutoFocusCallback =
220 new AutoFocusCallback();
221 private final Object mAutoFocusMoveCallback =
222 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
223 ? new AutoFocusMoveCallback()
224 : null;
225
226 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
227
228 private long mFocusStartTime;
229 private long mShutterCallbackTime;
230 private long mPostViewPictureCallbackTime;
231 private long mRawPictureCallbackTime;
232 private long mJpegPictureCallbackTime;
233 private long mOnResumeTime;
234 private byte[] mJpegImageData;
235
236 // These latency time are for the CameraLatency test.
237 public long mAutoFocusTime;
238 public long mShutterLag;
239 public long mShutterToPictureDisplayedTime;
240 public long mPictureDisplayedToJpegCallbackTime;
241 public long mJpegCallbackFinishTime;
242 public long mCaptureStartTime;
243
244 // This handles everything about focus.
245 private FocusOverlayManager mFocusManager;
246
Michael Kolb8872c232013-01-29 10:33:22 -0800247 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800248
249 private final Handler mHandler = new MainHandler();
250 private PreferenceGroup mPreferenceGroup;
251
252 private boolean mQuickCapture;
253
254 CameraStartUpThread mCameraStartUpThread;
255 ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
256
Angus Kong0d00a892013-03-26 11:40:40 -0700257 private SensorManager mSensorManager;
258 private float[] mGData = new float[3];
259 private float[] mMData = new float[3];
260 private float[] mR = new float[16];
261 private int mHeading = -1;
262
Angus Kongce5480e2013-01-29 17:43:48 -0800263 private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
264 new MediaSaveService.OnMediaSavedListener() {
265 @Override
266 public void onMediaSaved(Uri uri) {
267 if (uri != null) {
268 mActivity.addSecureAlbumItemIfNeeded(false, uri);
269 Util.broadcastNewPicture(mActivity, uri);
270 }
271 }
272 };
Michael Kolb8872c232013-01-29 10:33:22 -0800273
274 // The purpose is not to block the main thread in onCreate and onResume.
275 private class CameraStartUpThread extends Thread {
276 private volatile boolean mCancelled;
277
278 public void cancel() {
279 mCancelled = true;
280 interrupt();
281 }
282
283 public boolean isCanceled() {
284 return mCancelled;
285 }
286
287 @Override
288 public void run() {
289 try {
290 // We need to check whether the activity is paused before long
291 // operations to ensure that onPause() can be done ASAP.
292 if (mCancelled) return;
293 mCameraDevice = Util.openCamera(mActivity, mCameraId);
294 mParameters = mCameraDevice.getParameters();
295 // Wait until all the initialization needed by startPreview are
296 // done.
297 mStartPreviewPrerequisiteReady.block();
298
299 initializeCapabilities();
300 if (mFocusManager == null) initializeFocusManager();
301 if (mCancelled) return;
302 setCameraParameters(UPDATE_PARAM_ALL);
303 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
304 if (mCancelled) return;
305 startPreview();
306 mHandler.sendEmptyMessage(START_PREVIEW_DONE);
307 mOnResumeTime = SystemClock.uptimeMillis();
308 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
309 } catch (CameraHardwareException e) {
310 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
311 } catch (CameraDisabledException e) {
312 mHandler.sendEmptyMessage(CAMERA_DISABLED);
313 }
314 }
315 }
316
317 /**
318 * This Handler is used to post message back onto the main thread of the
319 * application
320 */
321 private class MainHandler extends Handler {
322 @Override
323 public void handleMessage(Message msg) {
324 switch (msg.what) {
325 case SETUP_PREVIEW: {
326 setupPreview();
327 break;
328 }
329
330 case CLEAR_SCREEN_DELAY: {
331 mActivity.getWindow().clearFlags(
332 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
333 break;
334 }
335
336 case FIRST_TIME_INIT: {
337 initializeFirstTime();
338 break;
339 }
340
341 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
342 setCameraParametersWhenIdle(0);
343 break;
344 }
345
346 case CHECK_DISPLAY_ROTATION: {
347 // Set the display orientation if display rotation has changed.
348 // Sometimes this happens when the device is held upside
349 // down and camera app is opened. Rotation animation will
350 // take some time and the rotation value we have got may be
351 // wrong. Framework does not have a callback for this now.
352 if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
353 setDisplayOrientation();
354 }
355 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
356 mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
357 }
358 break;
359 }
360
361 case SHOW_TAP_TO_FOCUS_TOAST: {
362 showTapToFocusToast();
363 break;
364 }
365
366 case SWITCH_CAMERA: {
367 switchCamera();
368 break;
369 }
370
371 case SWITCH_CAMERA_START_ANIMATION: {
372 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
373 break;
374 }
375
376 case CAMERA_OPEN_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800377 onCameraOpened();
Michael Kolb8872c232013-01-29 10:33:22 -0800378 break;
379 }
380
381 case START_PREVIEW_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800382 onPreviewStarted();
Michael Kolb8872c232013-01-29 10:33:22 -0800383 break;
384 }
385
386 case OPEN_CAMERA_FAIL: {
387 mCameraStartUpThread = null;
388 mOpenCameraFail = true;
389 Util.showErrorAndFinish(mActivity,
390 R.string.cannot_connect_camera);
391 break;
392 }
393
394 case CAMERA_DISABLED: {
395 mCameraStartUpThread = null;
396 mCameraDisabled = true;
397 Util.showErrorAndFinish(mActivity,
398 R.string.camera_disabled);
399 break;
400 }
Michael Kolb8872c232013-01-29 10:33:22 -0800401 }
402 }
403 }
404
405 @Override
406 public void init(CameraActivity activity, View parent, boolean reuseNail) {
407 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
421 // Surface texture is from camera screen nail and startPreview needs it.
422 // This must be done before startPreview.
423 mIsImageCaptureIntent = isImageCaptureIntent();
424 if (reuseNail) {
425 mActivity.reuseCameraScreenNail(!mIsImageCaptureIntent);
426 } else {
427 mActivity.createCameraScreenNail(!mIsImageCaptureIntent);
428 }
429
430 mPreferences.setLocalId(mActivity, mCameraId);
431 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
432 // we need to reset exposure for the preview
433 resetExposureCompensation();
434 // Starting the preview needs preferences, camera screen nail, and
435 // focus area indicator.
436 mStartPreviewPrerequisiteReady.open();
437
438 initializeControlByIntent();
439 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Michael Kolbd6954f32013-03-08 20:43:01 -0800440 mLocationManager = new LocationManager(mActivity, mUI);
Angus Kong0d00a892013-03-26 11:40:40 -0700441 mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
442
Michael Kolbd6954f32013-03-08 20:43:01 -0800443 }
444
445 private void initializeControlByIntent() {
446 mUI.initializeControlByIntent();
447 if (mIsImageCaptureIntent) {
448 setupCaptureParams();
449 }
450 }
451
452 private void onPreviewStarted() {
453 mCameraStartUpThread = null;
454 setCameraState(IDLE);
455 if (!ApiHelper.HAS_SURFACE_TEXTURE) {
456 // This may happen if surfaceCreated has arrived.
457 mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
458 }
459 startFaceDetection();
460 locationFirstRun();
Michael Kolb8872c232013-01-29 10:33:22 -0800461 }
462
463 // Prompt the user to pick to record location for the very first run of
464 // camera only
465 private void locationFirstRun() {
466 if (RecordLocationPreference.isSet(mPreferences)) {
467 return;
468 }
469 if (mActivity.isSecureCamera()) return;
470 // Check if the back camera exists
471 int backCameraId = CameraHolder.instance().getBackCameraId();
472 if (backCameraId == -1) {
473 // If there is no back camera, do not show the prompt.
474 return;
475 }
476
477 new AlertDialog.Builder(mActivity)
478 .setTitle(R.string.remember_location_title)
479 .setMessage(R.string.remember_location_prompt)
480 .setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() {
481 @Override
482 public void onClick(DialogInterface dialog, int arg1) {
483 setLocationPreference(RecordLocationPreference.VALUE_ON);
484 }
485 })
486 .setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() {
487 @Override
488 public void onClick(DialogInterface dialog, int arg1) {
489 dialog.cancel();
490 }
491 })
492 .setOnCancelListener(new DialogInterface.OnCancelListener() {
493 @Override
494 public void onCancel(DialogInterface dialog) {
495 setLocationPreference(RecordLocationPreference.VALUE_OFF);
496 }
497 })
498 .show();
499 }
500
501 private void setLocationPreference(String value) {
502 mPreferences.edit()
503 .putString(CameraSettings.KEY_RECORD_LOCATION, value)
504 .apply();
505 // TODO: Fix this to use the actual onSharedPreferencesChanged listener
506 // instead of invoking manually
507 onSharedPreferenceChanged();
508 }
509
Michael Kolbd6954f32013-03-08 20:43:01 -0800510 private void onCameraOpened() {
511 View root = mUI.getRootView();
Michael Kolb8872c232013-01-29 10:33:22 -0800512 // These depend on camera parameters.
Michael Kolbd6954f32013-03-08 20:43:01 -0800513
514 int width = root.getWidth();
515 int height = root.getHeight();
Doris Liu6a0de792013-02-26 10:54:25 -0800516 mFocusManager.setPreviewSize(width, height);
517 // Full-screen screennail
518 if (Util.getDisplayRotation(mActivity) % 180 == 0) {
519 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(width, height);
520 } else {
521 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(height, width);
522 }
523 // Set touch focus listener.
Michael Kolbd6954f32013-03-08 20:43:01 -0800524 mActivity.setSingleTapUpListener(root);
525 openCameraCommon();
Michael Kolb8872c232013-01-29 10:33:22 -0800526 onFullScreenChanged(mActivity.isInCameraApp());
527 }
528
Michael Kolbd6954f32013-03-08 20:43:01 -0800529 private void switchCamera() {
530 if (mPaused) return;
531
532 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
533 mCameraId = mPendingSwitchCameraId;
534 mPendingSwitchCameraId = -1;
535 setCameraId(mCameraId);
536
537 // from onPause
538 closeCamera();
539 mUI.collapseCameraControls();
540 mUI.clearFaces();
541 if (mFocusManager != null) mFocusManager.removeMessages();
542
543 // Restart the camera and initialize the UI. From onCreate.
544 mPreferences.setLocalId(mActivity, mCameraId);
545 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
546 try {
547 mCameraDevice = Util.openCamera(mActivity, mCameraId);
548 mParameters = mCameraDevice.getParameters();
549 } catch (CameraHardwareException e) {
550 Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
551 return;
552 } catch (CameraDisabledException e) {
553 Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
554 return;
Doris Liu09106a42013-03-05 09:54:25 -0800555 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800556 initializeCapabilities();
557 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
558 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
559 mFocusManager.setMirror(mirror);
560 mFocusManager.setParameters(mInitialParams);
561 setupPreview();
562
563 openCameraCommon();
564
565 if (ApiHelper.HAS_SURFACE_TEXTURE) {
566 // Start switch camera animation. Post a message because
567 // onFrameAvailable from the old camera may already exist.
568 mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
Doris Liu48239f42013-03-04 22:19:10 -0800569 }
570 }
571
Michael Kolbd6954f32013-03-08 20:43:01 -0800572 protected void setCameraId(int cameraId) {
573 ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
574 pref.setValue("" + cameraId);
575 }
576
577 // either open a new camera or switch cameras
578 private void openCameraCommon() {
Michael Kolb8872c232013-01-29 10:33:22 -0800579 loadCameraPreferences();
Michael Kolbd6954f32013-03-08 20:43:01 -0800580
581 mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
582 updateSceneMode();
583 showTapToFocusToastIfNeeded();
584
585
Michael Kolb8872c232013-01-29 10:33:22 -0800586 }
587
Michael Kolbd6954f32013-03-08 20:43:01 -0800588 public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight) {
589 Log.d(TAG, "Preview size changed.");
590 if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
591 ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(
592 previewWidth, previewHeight);
593 }
Michael Kolb8872c232013-01-29 10:33:22 -0800594
595 private void resetExposureCompensation() {
596 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
597 CameraSettings.EXPOSURE_DEFAULT_VALUE);
598 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
599 Editor editor = mPreferences.edit();
600 editor.putString(CameraSettings.KEY_EXPOSURE, "0");
601 editor.apply();
602 }
603 }
604
605 private void keepMediaProviderInstance() {
606 // We want to keep a reference to MediaProvider in camera's lifecycle.
607 // TODO: Utilize mMediaProviderClient instance to replace
608 // ContentResolver calls.
609 if (mMediaProviderClient == null) {
610 mMediaProviderClient = mContentResolver
611 .acquireContentProviderClient(MediaStore.AUTHORITY);
612 }
613 }
614
615 // Snapshots can only be taken after this is called. It should be called
616 // once only. We could have done these things in onCreate() but we want to
617 // make preview screen appear as soon as possible.
618 private void initializeFirstTime() {
619 if (mFirstTimeInitialized) return;
620
621 // Initialize location service.
622 boolean recordLocation = RecordLocationPreference.get(
623 mPreferences, mContentResolver);
624 mLocationManager.recordLocation(recordLocation);
625
626 keepMediaProviderInstance();
627
Michael Kolbd6954f32013-03-08 20:43:01 -0800628 mUI.initializeFirstTime();
Angus Kong86d36312013-01-31 18:22:44 -0800629 MediaSaveService s = mActivity.getMediaSaveService();
630 // We set the listener only when both service and shutterbutton
631 // are initialized.
632 if (s != null) {
633 s.setListener(this);
634 }
Michael Kolb8872c232013-01-29 10:33:22 -0800635
Michael Kolb8872c232013-01-29 10:33:22 -0800636 mNamedImages = new NamedImages();
637
638 mFirstTimeInitialized = true;
639 addIdleHandler();
640
641 mActivity.updateStorageSpaceAndHint();
642 }
643
Michael Kolbd6954f32013-03-08 20:43:01 -0800644 // If the activity is paused and resumed, this method will be called in
645 // onResume.
646 private void initializeSecondTime() {
647 // Start location update if needed.
648 boolean recordLocation = RecordLocationPreference.get(
649 mPreferences, mContentResolver);
650 mLocationManager.recordLocation(recordLocation);
651 MediaSaveService s = mActivity.getMediaSaveService();
652 if (s != null) {
653 s.setListener(this);
654 }
655 mNamedImages = new NamedImages();
656 mUI.initializeSecondTime(mParameters);
657 keepMediaProviderInstance();
658 }
659
660 @Override
661 public void onSurfaceCreated(SurfaceHolder holder) {
662 // Do not access the camera if camera start up thread is not finished.
663 if (mCameraDevice == null || mCameraStartUpThread != null)
664 return;
665
666 mCameraDevice.setPreviewDisplayAsync(holder);
667 // This happens when onConfigurationChanged arrives, surface has been
668 // destroyed, and there is no onFullScreenChanged.
669 if (mCameraState == PREVIEW_STOPPED) {
670 setupPreview();
671 }
672 }
673
Michael Kolb8872c232013-01-29 10:33:22 -0800674 private void showTapToFocusToastIfNeeded() {
675 // Show the tap to focus toast if this is the first start.
676 if (mFocusAreaSupported &&
677 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
678 // Delay the toast for one second to wait for orientation.
679 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
680 }
681 }
682
683 private void addIdleHandler() {
684 MessageQueue queue = Looper.myQueue();
685 queue.addIdleHandler(new MessageQueue.IdleHandler() {
686 @Override
687 public boolean queueIdle() {
688 Storage.ensureOSXCompatible();
689 return false;
690 }
691 });
692 }
693
Michael Kolb8872c232013-01-29 10:33:22 -0800694 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
695 @Override
696 public void startFaceDetection() {
697 if (!ApiHelper.HAS_FACE_DETECTION) return;
698 if (mFaceDetectionStarted) return;
699 if (mParameters.getMaxNumDetectedFaces() > 0) {
700 mFaceDetectionStarted = true;
Michael Kolb8872c232013-01-29 10:33:22 -0800701 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Michael Kolbd6954f32013-03-08 20:43:01 -0800702 mUI.onStartFaceDetection(mDisplayOrientation,
703 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
704 mCameraDevice.setFaceDetectionListener(mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800705 mCameraDevice.startFaceDetection();
706 }
707 }
708
709 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
710 @Override
711 public void stopFaceDetection() {
712 if (!ApiHelper.HAS_FACE_DETECTION) return;
713 if (!mFaceDetectionStarted) return;
714 if (mParameters.getMaxNumDetectedFaces() > 0) {
715 mFaceDetectionStarted = false;
716 mCameraDevice.setFaceDetectionListener(null);
717 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800718 mUI.clearFaces();
Michael Kolb8872c232013-01-29 10:33:22 -0800719 }
720 }
721
722 @Override
723 public boolean dispatchTouchEvent(MotionEvent m) {
724 if (mCameraState == SWITCHING_CAMERA) return true;
Michael Kolbd6954f32013-03-08 20:43:01 -0800725 return mUI.dispatchTouchEvent(m);
Michael Kolb8872c232013-01-29 10:33:22 -0800726 }
727
728 private final class ShutterCallback
729 implements android.hardware.Camera.ShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700730
731 private boolean mAnimateFlash;
732
733 public ShutterCallback(boolean animateFlash) {
734 mAnimateFlash = animateFlash;
735 }
736
Michael Kolb8872c232013-01-29 10:33:22 -0800737 @Override
738 public void onShutter() {
739 mShutterCallbackTime = System.currentTimeMillis();
740 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
741 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Angus Kongdcb0ef12013-03-25 23:11:43 -0700742 if (mAnimateFlash) {
743 mActivity.runOnUiThread(mFlashRunnable);
744 }
Michael Kolb8872c232013-01-29 10:33:22 -0800745 }
746 }
747
748 private final class PostViewPictureCallback implements PictureCallback {
749 @Override
750 public void onPictureTaken(
751 byte [] data, android.hardware.Camera camera) {
752 mPostViewPictureCallbackTime = System.currentTimeMillis();
753 Log.v(TAG, "mShutterToPostViewCallbackTime = "
754 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
755 + "ms");
756 }
757 }
758
759 private final class RawPictureCallback implements PictureCallback {
760 @Override
761 public void onPictureTaken(
762 byte [] rawData, android.hardware.Camera camera) {
763 mRawPictureCallbackTime = System.currentTimeMillis();
764 Log.v(TAG, "mShutterToRawCallbackTime = "
765 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
766 }
767 }
768
769 private final class JpegPictureCallback implements PictureCallback {
770 Location mLocation;
771
772 public JpegPictureCallback(Location loc) {
773 mLocation = loc;
774 }
775
776 @Override
777 public void onPictureTaken(
778 final byte [] jpegData, final android.hardware.Camera camera) {
779 if (mPaused) {
780 return;
781 }
782 if (mSceneMode == Util.SCENE_MODE_HDR) {
783 mActivity.showSwitcher();
784 mActivity.setSwipingEnabled(true);
785 }
786
787 mJpegPictureCallbackTime = System.currentTimeMillis();
788 // If postview callback has arrived, the captured image is displayed
789 // in postview callback. If not, the captured image is displayed in
790 // raw picture callback.
791 if (mPostViewPictureCallbackTime != 0) {
792 mShutterToPictureDisplayedTime =
793 mPostViewPictureCallbackTime - mShutterCallbackTime;
794 mPictureDisplayedToJpegCallbackTime =
795 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
796 } else {
797 mShutterToPictureDisplayedTime =
798 mRawPictureCallbackTime - mShutterCallbackTime;
799 mPictureDisplayedToJpegCallbackTime =
800 mJpegPictureCallbackTime - mRawPictureCallbackTime;
801 }
802 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
803 + mPictureDisplayedToJpegCallbackTime + "ms");
804
805 // Only animate when in full screen capture mode
806 // i.e. If monkey/a user swipes to the gallery during picture taking,
807 // don't show animation
808 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
809 && mActivity.mShowCameraAppView) {
810 // Finish capture animation
811 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSlide();
812 }
813 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
814 if (!mIsImageCaptureIntent) {
815 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
816 setupPreview();
817 } else {
818 // Camera HAL of some devices have a bug. Starting preview
819 // immediately after taking a picture will fail. Wait some
820 // time before starting the preview.
821 mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
822 }
823 }
824
825 if (!mIsImageCaptureIntent) {
826 // Calculate the width and the height of the jpeg.
827 Size s = mParameters.getPictureSize();
Angus Kong0d00a892013-03-26 11:40:40 -0700828 ExifInterface exif = Exif.getExif(jpegData);
829 int orientation = Exif.getOrientation(exif);
Michael Kolb8872c232013-01-29 10:33:22 -0800830 int width, height;
831 if ((mJpegRotation + orientation) % 180 == 0) {
832 width = s.width;
833 height = s.height;
834 } else {
835 width = s.height;
836 height = s.width;
837 }
838 String title = mNamedImages.getTitle();
839 long date = mNamedImages.getDate();
840 if (title == null) {
841 Log.e(TAG, "Unbalanced name/data pair");
842 } else {
843 if (date == -1) date = mCaptureStartTime;
Angus Kong0d00a892013-03-26 11:40:40 -0700844 if (mHeading >= 0) {
845 // heading direction has been updated by the sensor.
846 ExifTag directionRefTag = exif.buildTag(
847 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
848 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
849 ExifTag directionTag = exif.buildTag(
850 ExifInterface.TAG_GPS_IMG_DIRECTION,
851 new Rational(mHeading, 1));
852 exif.setTag(directionRefTag);
853 exif.setTag(directionTag);
854 }
Angus Kong86d36312013-01-31 18:22:44 -0800855 mActivity.getMediaSaveService().addImage(
856 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700857 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800858 }
859 } else {
860 mJpegImageData = jpegData;
861 if (!mQuickCapture) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800862 mUI.showPostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -0800863 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800864 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800865 }
866 }
867
868 // Check this in advance of each shot so we don't add to shutter
869 // latency. It's true that someone else could write to the SD card in
870 // the mean time and fill it, but that could have happened between the
871 // shutter press and saving the JPEG too.
872 mActivity.updateStorageSpaceAndHint();
873
874 long now = System.currentTimeMillis();
875 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
876 Log.v(TAG, "mJpegCallbackFinishTime = "
877 + mJpegCallbackFinishTime + "ms");
878 mJpegPictureCallbackTime = 0;
879 }
880 }
881
882 private final class AutoFocusCallback
883 implements android.hardware.Camera.AutoFocusCallback {
884 @Override
885 public void onAutoFocus(
886 boolean focused, android.hardware.Camera camera) {
887 if (mPaused) return;
888
889 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
890 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
891 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800892 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb8872c232013-01-29 10:33:22 -0800893 }
894 }
895
896 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
897 private final class AutoFocusMoveCallback
898 implements android.hardware.Camera.AutoFocusMoveCallback {
899 @Override
900 public void onAutoFocusMoving(
901 boolean moving, android.hardware.Camera camera) {
902 mFocusManager.onAutoFocusMoving(moving);
903 }
904 }
905
906 private static class NamedImages {
907 private ArrayList<NamedEntity> mQueue;
908 private boolean mStop;
909 private NamedEntity mNamedEntity;
910
911 public NamedImages() {
912 mQueue = new ArrayList<NamedEntity>();
913 }
914
915 public void nameNewImage(ContentResolver resolver, long date) {
916 NamedEntity r = new NamedEntity();
917 r.title = Util.createJpegName(date);
918 r.date = date;
919 mQueue.add(r);
920 }
921
922 public String getTitle() {
923 if (mQueue.isEmpty()) {
924 mNamedEntity = null;
925 return null;
926 }
927 mNamedEntity = mQueue.get(0);
928 mQueue.remove(0);
929
930 return mNamedEntity.title;
931 }
932
933 // Must be called after getTitle().
934 public long getDate() {
935 if (mNamedEntity == null) return -1;
936 return mNamedEntity.date;
937 }
938
939 private static class NamedEntity {
940 String title;
941 long date;
942 }
943 }
944
945 private void setCameraState(int state) {
946 mCameraState = state;
947 switch (state) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800948 case PhotoController.PREVIEW_STOPPED:
949 case PhotoController.SNAPSHOT_IN_PROGRESS:
950 case PhotoController.FOCUSING:
951 case PhotoController.SWITCHING_CAMERA:
952 mUI.enableGestures(false);
953 break;
954 case PhotoController.IDLE:
955 if (mActivity.isInCameraApp()) {
956 mUI.enableGestures(true);
957 }
958 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800959 }
960 }
961
962 private void animateFlash() {
963 // Only animate when in full screen capture mode
964 // i.e. If monkey/a user swipes to the gallery during picture taking,
965 // don't show animation
966 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
967 && mActivity.mShowCameraAppView) {
968 // Start capture animation.
969 ((CameraScreenNail) mActivity.mCameraScreenNail).animateFlash(mDisplayRotation);
970 }
971 }
972
973 @Override
974 public boolean capture() {
975 // If we are already in the middle of taking a snapshot or the image save request
976 // is full then ignore.
977 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Angus Kong86d36312013-01-31 18:22:44 -0800978 || mCameraState == SWITCHING_CAMERA
979 || mActivity.getMediaSaveService().isQueueFull()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800980 return false;
981 }
982 mCaptureStartTime = System.currentTimeMillis();
983 mPostViewPictureCallbackTime = 0;
984 mJpegImageData = null;
985
986 final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR);
987
988 if (animateBefore) {
989 animateFlash();
990 }
991
992 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800993 int orientation;
994 // We need to be consistent with the framework orientation (i.e. the
995 // orientation of the UI.) when the auto-rotate screen setting is on.
996 if (mActivity.isAutoRotateScreen()) {
997 orientation = (360 - mDisplayRotation) % 360;
998 } else {
999 orientation = mOrientation;
1000 }
1001 mJpegRotation = Util.getJpegRotation(mCameraId, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001002 mParameters.setRotation(mJpegRotation);
1003 Location loc = mLocationManager.getCurrentLocation();
1004 Util.setGpsParameters(mParameters, loc);
1005 mCameraDevice.setParameters(mParameters);
1006
Angus Kongdcb0ef12013-03-25 23:11:43 -07001007 mCameraDevice.takePicture2(new ShutterCallback(!animateBefore),
1008 mRawPictureCallback, mPostViewPictureCallback,
1009 new JpegPictureCallback(loc), mCameraState,
1010 mFocusManager.getFocusState());
Michael Kolb8872c232013-01-29 10:33:22 -08001011
1012 mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime);
1013
1014 mFaceDetectionStarted = false;
1015 setCameraState(SNAPSHOT_IN_PROGRESS);
1016 return true;
1017 }
1018
1019 @Override
1020 public void setFocusParameters() {
1021 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1022 }
1023
1024 private int getPreferredCameraId(ComboPreferences preferences) {
1025 int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
1026 if (intentCameraId != -1) {
1027 // Testing purpose. Launch a specific camera through the intent
1028 // extras.
1029 return intentCameraId;
1030 } else {
1031 return CameraSettings.readPreferredCameraId(preferences);
1032 }
1033 }
1034
Michael Kolb8872c232013-01-29 10:33:22 -08001035 @Override
1036 public void onFullScreenChanged(boolean full) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001037 mUI.onFullScreenChanged(full);
Michael Kolb8872c232013-01-29 10:33:22 -08001038 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1039 if (mActivity.mCameraScreenNail != null) {
1040 ((CameraScreenNail) mActivity.mCameraScreenNail).setFullScreen(full);
1041 }
1042 return;
1043 }
Michael Kolb8872c232013-01-29 10:33:22 -08001044 }
1045
Michael Kolbd6954f32013-03-08 20:43:01 -08001046 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001047 // If scene mode is set, we cannot set flash mode, white balance, and
1048 // focus mode, instead, we read it from driver
1049 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1050 overrideCameraSettings(mParameters.getFlashMode(),
1051 mParameters.getWhiteBalance(), mParameters.getFocusMode());
1052 } else {
1053 overrideCameraSettings(null, null, null);
1054 }
1055 }
1056
1057 private void overrideCameraSettings(final String flashMode,
1058 final String whiteBalance, final String focusMode) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001059 mUI.overrideSettings(
1060 CameraSettings.KEY_FLASH_MODE, flashMode,
1061 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
1062 CameraSettings.KEY_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001063 }
1064
1065 private void loadCameraPreferences() {
1066 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
1067 mCameraId, CameraHolder.instance().getCameraInfo());
1068 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
1069 }
1070
1071 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001072 public void onOrientationChanged(int orientation) {
1073 // We keep the last known orientation. So if the user first orient
1074 // the camera then point the camera to floor or sky, we still have
1075 // the correct orientation.
1076 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
1077 mOrientation = Util.roundOrientation(orientation, mOrientation);
1078
1079 // Show the toast after getting the first orientation changed.
1080 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1081 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1082 showTapToFocusToast();
1083 }
1084 }
1085
1086 @Override
1087 public void onStop() {
1088 if (mMediaProviderClient != null) {
1089 mMediaProviderClient.release();
1090 mMediaProviderClient = null;
1091 }
1092 }
1093
Michael Kolbd6954f32013-03-08 20:43:01 -08001094 @Override
1095 public void onCaptureCancelled() {
1096 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1097 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001098 }
1099
Michael Kolbd6954f32013-03-08 20:43:01 -08001100 @Override
1101 public void onCaptureRetake() {
Michael Kolb8872c232013-01-29 10:33:22 -08001102 if (mPaused)
1103 return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001104 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001105 setupPreview();
1106 }
1107
Michael Kolbd6954f32013-03-08 20:43:01 -08001108 @Override
1109 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001110 if (mPaused) {
1111 return;
1112 }
1113
1114 byte[] data = mJpegImageData;
1115
1116 if (mCropValue == null) {
1117 // First handle the no crop case -- just return the value. If the
1118 // caller specifies a "save uri" then write the data to its
1119 // stream. Otherwise, pass back a scaled down version of the bitmap
1120 // directly in the extras.
1121 if (mSaveUri != null) {
1122 OutputStream outputStream = null;
1123 try {
1124 outputStream = mContentResolver.openOutputStream(mSaveUri);
1125 outputStream.write(data);
1126 outputStream.close();
1127
1128 mActivity.setResultEx(Activity.RESULT_OK);
1129 mActivity.finish();
1130 } catch (IOException ex) {
1131 // ignore exception
1132 } finally {
1133 Util.closeSilently(outputStream);
1134 }
1135 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001136 ExifInterface exif = Exif.getExif(data);
1137 int orientation = Exif.getOrientation(exif);
Michael Kolb8872c232013-01-29 10:33:22 -08001138 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
1139 bitmap = Util.rotate(bitmap, orientation);
1140 mActivity.setResultEx(Activity.RESULT_OK,
1141 new Intent("inline-data").putExtra("data", bitmap));
1142 mActivity.finish();
1143 }
1144 } else {
1145 // Save the image to a temp file and invoke the cropper
1146 Uri tempUri = null;
1147 FileOutputStream tempStream = null;
1148 try {
1149 File path = mActivity.getFileStreamPath(sTempCropFilename);
1150 path.delete();
1151 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1152 tempStream.write(data);
1153 tempStream.close();
1154 tempUri = Uri.fromFile(path);
1155 } catch (FileNotFoundException ex) {
1156 mActivity.setResultEx(Activity.RESULT_CANCELED);
1157 mActivity.finish();
1158 return;
1159 } catch (IOException ex) {
1160 mActivity.setResultEx(Activity.RESULT_CANCELED);
1161 mActivity.finish();
1162 return;
1163 } finally {
1164 Util.closeSilently(tempStream);
1165 }
1166
1167 Bundle newExtras = new Bundle();
1168 if (mCropValue.equals("circle")) {
1169 newExtras.putString("circleCrop", "true");
1170 }
1171 if (mSaveUri != null) {
1172 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1173 } else {
1174 newExtras.putBoolean(CropExtras.KEY_RETURN_DATA, true);
1175 }
1176 if (mActivity.isSecureCamera()) {
1177 newExtras.putBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, true);
1178 }
1179
1180 Intent cropIntent = new Intent(FilterShowActivity.CROP_ACTION);
1181
1182 cropIntent.setData(tempUri);
1183 cropIntent.putExtras(newExtras);
1184
1185 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1186 }
1187 }
1188
Michael Kolb8872c232013-01-29 10:33:22 -08001189 @Override
1190 public void onShutterButtonFocus(boolean pressed) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001191 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001192 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1193 || (mCameraState == PREVIEW_STOPPED)) return;
1194
1195 // Do not do focus if there is not enough storage.
1196 if (pressed && !canTakePicture()) return;
1197
1198 if (pressed) {
1199 if (mSceneMode == Util.SCENE_MODE_HDR) {
1200 mActivity.hideSwitcher();
1201 mActivity.setSwipingEnabled(false);
1202 }
1203 mFocusManager.onShutterDown();
1204 } else {
Doris Liuda50e052013-02-07 14:36:32 -08001205 // for countdown mode, we need to postpone the shutter release
1206 // i.e. lock the focus during countdown.
Michael Kolbd6954f32013-03-08 20:43:01 -08001207 if (!mUI.isCountingDown()) {
Doris Liuda50e052013-02-07 14:36:32 -08001208 mFocusManager.onShutterUp();
1209 }
Michael Kolb8872c232013-01-29 10:33:22 -08001210 }
1211 }
1212
1213 @Override
1214 public void onShutterButtonClick() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001215 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001216 || (mCameraState == SWITCHING_CAMERA)
1217 || (mCameraState == PREVIEW_STOPPED)) return;
1218
1219 // Do not take the picture if there is not enough storage.
1220 if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
1221 Log.i(TAG, "Not enough space or storage not ready. remaining="
1222 + mActivity.getStorageSpace());
1223 return;
1224 }
1225 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1226
1227 // If the user wants to do a snapshot while the previous one is still
1228 // in progress, remember the fact and do it after we finish the previous
1229 // one and re-start the preview. Snapshot in progress also includes the
1230 // state that autofocus is focusing and a picture will be taken when
1231 // focus callback arrives.
1232 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1233 && !mIsImageCaptureIntent) {
1234 mSnapshotOnIdle = true;
1235 return;
1236 }
1237
1238 String timer = mPreferences.getString(
1239 CameraSettings.KEY_TIMER,
1240 mActivity.getString(R.string.pref_camera_timer_default));
1241 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1242 mActivity.getString(R.string.pref_camera_timer_sound_default))
1243 .equals(mActivity.getString(R.string.setting_on_value));
1244
1245 int seconds = Integer.parseInt(timer);
1246 // When shutter button is pressed, check whether the previous countdown is
1247 // finished. If not, cancel the previous countdown and start a new one.
Michael Kolbd6954f32013-03-08 20:43:01 -08001248 if (mUI.isCountingDown()) {
1249 mUI.cancelCountDown();
1250 }
1251 if (seconds > 0) {
1252 mUI.startCountDown(seconds, playSound);
Michael Kolb8872c232013-01-29 10:33:22 -08001253 } else {
1254 mSnapshotOnIdle = false;
1255 mFocusManager.doSnap();
1256 }
1257 }
1258
1259 @Override
1260 public void installIntentFilter() {
1261 }
1262
1263 @Override
1264 public boolean updateStorageHintOnResume() {
1265 return mFirstTimeInitialized;
1266 }
1267
1268 @Override
1269 public void updateCameraAppView() {
1270 }
1271
1272 @Override
1273 public void onResumeBeforeSuper() {
1274 mPaused = false;
1275 }
1276
1277 @Override
1278 public void onResumeAfterSuper() {
1279 if (mOpenCameraFail || mCameraDisabled) return;
1280
1281 mJpegPictureCallbackTime = 0;
1282 mZoomValue = 0;
1283
1284 // Start the preview if it is not started.
1285 if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
1286 resetExposureCompensation();
1287 mCameraStartUpThread = new CameraStartUpThread();
1288 mCameraStartUpThread.start();
1289 }
1290
1291 // If first time initialization is not finished, put it in the
1292 // message queue.
1293 if (!mFirstTimeInitialized) {
1294 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1295 } else {
1296 initializeSecondTime();
1297 }
1298 keepScreenOnAwhile();
1299
1300 // Dismiss open menu if exists.
1301 PopupManager.getInstance(mActivity).notifyShowPopup(null);
Bobby Georgescu0a7dd572013-03-12 22:45:17 -07001302 UsageStatistics.onContentViewChanged(
1303 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
Angus Kong0d00a892013-03-26 11:40:40 -07001304
1305 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1306 if (gsensor != null) {
1307 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1308 }
1309
1310 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1311 if (msensor != null) {
1312 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1313 }
Michael Kolb8872c232013-01-29 10:33:22 -08001314 }
1315
1316 void waitCameraStartUpThread() {
1317 try {
1318 if (mCameraStartUpThread != null) {
1319 mCameraStartUpThread.cancel();
1320 mCameraStartUpThread.join();
1321 mCameraStartUpThread = null;
1322 setCameraState(IDLE);
1323 }
1324 } catch (InterruptedException e) {
1325 // ignore
1326 }
1327 }
1328
1329 @Override
1330 public void onPauseBeforeSuper() {
1331 mPaused = true;
Angus Kong0d00a892013-03-26 11:40:40 -07001332 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1333 if (gsensor != null) {
1334 mSensorManager.unregisterListener(this, gsensor);
1335 }
1336
1337 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1338 if (msensor != null) {
1339 mSensorManager.unregisterListener(this, msensor);
1340 }
Michael Kolb8872c232013-01-29 10:33:22 -08001341 }
1342
1343 @Override
1344 public void onPauseAfterSuper() {
1345 // Wait the camera start up thread to finish.
1346 waitCameraStartUpThread();
1347
1348 // When camera is started from secure lock screen for the first time
1349 // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1350 // To reduce the latency, keep the camera for a short time so it does
1351 // not need to be opened again.
1352 if (mCameraDevice != null && mActivity.isSecureCamera()
1353 && ActivityBase.isFirstStartAfterScreenOn()) {
1354 ActivityBase.resetFirstStartAfterScreenOn();
1355 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1356 }
1357 // Reset the focus first. Camera CTS does not guarantee that
1358 // cancelAutoFocus is allowed after preview stops.
1359 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1360 mCameraDevice.cancelAutoFocus();
1361 }
1362 stopPreview();
Doris Liub8cf2502013-02-22 14:31:17 -08001363 // Release surface texture.
1364 ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture();
Michael Kolb8872c232013-01-29 10:33:22 -08001365
Angus Kongce5480e2013-01-29 17:43:48 -08001366 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001367
1368 if (mLocationManager != null) mLocationManager.recordLocation(false);
1369
1370 // If we are in an image capture intent and has taken
1371 // a picture, we just clear it in onPause.
1372 mJpegImageData = null;
1373
1374 // Remove the messages in the event queue.
1375 mHandler.removeMessages(SETUP_PREVIEW);
1376 mHandler.removeMessages(FIRST_TIME_INIT);
1377 mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
1378 mHandler.removeMessages(SWITCH_CAMERA);
1379 mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
1380 mHandler.removeMessages(CAMERA_OPEN_DONE);
1381 mHandler.removeMessages(START_PREVIEW_DONE);
1382 mHandler.removeMessages(OPEN_CAMERA_FAIL);
1383 mHandler.removeMessages(CAMERA_DISABLED);
1384
Michael Kolbd6954f32013-03-08 20:43:01 -08001385 closeCamera();
1386
1387 resetScreenOn();
1388 mUI.onPause();
1389
Michael Kolb8872c232013-01-29 10:33:22 -08001390 mPendingSwitchCameraId = -1;
1391 if (mFocusManager != null) mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001392 MediaSaveService s = mActivity.getMediaSaveService();
1393 if (s != null) {
1394 s.setListener(null);
1395 }
Michael Kolb8872c232013-01-29 10:33:22 -08001396 }
1397
Michael Kolb8872c232013-01-29 10:33:22 -08001398 /**
1399 * The focus manager is the first UI related element to get initialized,
1400 * and it requires the RenderOverlay, so initialize it here
1401 */
1402 private void initializeFocusManager() {
1403 // Create FocusManager object. startPreview needs it.
Michael Kolb8872c232013-01-29 10:33:22 -08001404 // if mFocusManager not null, reuse it
1405 // otherwise create a new instance
1406 if (mFocusManager != null) {
1407 mFocusManager.removeMessages();
1408 } else {
1409 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1410 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
1411 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1412 R.array.pref_camera_focusmode_default_array);
1413 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
1414 mInitialParams, this, mirror,
Michael Kolbd6954f32013-03-08 20:43:01 -08001415 mActivity.getMainLooper(), mUI);
Michael Kolb8872c232013-01-29 10:33:22 -08001416 }
1417 }
1418
Michael Kolb8872c232013-01-29 10:33:22 -08001419 @Override
1420 public void onConfigurationChanged(Configuration newConfig) {
1421 Log.v(TAG, "onConfigurationChanged");
1422 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001423 }
1424
1425 @Override
1426 public void onActivityResult(
1427 int requestCode, int resultCode, Intent data) {
1428 switch (requestCode) {
1429 case REQUEST_CROP: {
1430 Intent intent = new Intent();
1431 if (data != null) {
1432 Bundle extras = data.getExtras();
1433 if (extras != null) {
1434 intent.putExtras(extras);
1435 }
1436 }
1437 mActivity.setResultEx(resultCode, intent);
1438 mActivity.finish();
1439
1440 File path = mActivity.getFileStreamPath(sTempCropFilename);
1441 path.delete();
1442
1443 break;
1444 }
1445 }
1446 }
1447
1448 private boolean canTakePicture() {
1449 return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
1450 }
1451
1452 @Override
1453 public void autoFocus() {
1454 mFocusStartTime = System.currentTimeMillis();
1455 mCameraDevice.autoFocus(mAutoFocusCallback);
1456 setCameraState(FOCUSING);
1457 }
1458
1459 @Override
1460 public void cancelAutoFocus() {
1461 mCameraDevice.cancelAutoFocus();
1462 setCameraState(IDLE);
1463 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1464 }
1465
1466 // Preview area is touched. Handle touch focus.
1467 @Override
1468 public void onSingleTapUp(View view, int x, int y) {
1469 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1470 || mCameraState == SNAPSHOT_IN_PROGRESS
1471 || mCameraState == SWITCHING_CAMERA
1472 || mCameraState == PREVIEW_STOPPED) {
1473 return;
1474 }
1475
1476 // Do not trigger touch focus if popup window is opened.
Michael Kolbd6954f32013-03-08 20:43:01 -08001477 if (mUI.removeTopLevelPopup()) return;
Michael Kolb8872c232013-01-29 10:33:22 -08001478
1479 // Check if metering area or focus area is supported.
1480 if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1481 mFocusManager.onSingleTapUp(x, y);
1482 }
1483
1484 @Override
1485 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001486 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001487 }
1488
1489 @Override
1490 public boolean onKeyDown(int keyCode, KeyEvent event) {
1491 switch (keyCode) {
1492 case KeyEvent.KEYCODE_VOLUME_UP:
1493 case KeyEvent.KEYCODE_VOLUME_DOWN:
1494 case KeyEvent.KEYCODE_FOCUS:
1495 if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
1496 if (event.getRepeatCount() == 0) {
1497 onShutterButtonFocus(true);
1498 }
1499 return true;
1500 }
1501 return false;
1502 case KeyEvent.KEYCODE_CAMERA:
1503 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1504 onShutterButtonClick();
1505 }
1506 return true;
1507 case KeyEvent.KEYCODE_DPAD_CENTER:
1508 // If we get a dpad center event without any focused view, move
1509 // the focus to the shutter button and press it.
1510 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1511 // Start auto-focus immediately to reduce shutter lag. After
1512 // the shutter button gets the focus, onShutterButtonFocus()
1513 // will be called again but it is fine.
Michael Kolbd6954f32013-03-08 20:43:01 -08001514 if (mUI.removeTopLevelPopup()) return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001515 onShutterButtonFocus(true);
Michael Kolbd6954f32013-03-08 20:43:01 -08001516 mUI.pressShutterButton();
Michael Kolb8872c232013-01-29 10:33:22 -08001517 }
1518 return true;
1519 }
1520 return false;
1521 }
1522
1523 @Override
1524 public boolean onKeyUp(int keyCode, KeyEvent event) {
1525 switch (keyCode) {
1526 case KeyEvent.KEYCODE_VOLUME_UP:
1527 case KeyEvent.KEYCODE_VOLUME_DOWN:
1528 if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
1529 onShutterButtonClick();
1530 return true;
1531 }
1532 return false;
1533 case KeyEvent.KEYCODE_FOCUS:
1534 if (mFirstTimeInitialized) {
1535 onShutterButtonFocus(false);
1536 }
1537 return true;
1538 }
1539 return false;
1540 }
1541
1542 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1543 private void closeCamera() {
1544 if (mCameraDevice != null) {
1545 mCameraDevice.setZoomChangeListener(null);
1546 if(ApiHelper.HAS_FACE_DETECTION) {
1547 mCameraDevice.setFaceDetectionListener(null);
1548 }
1549 mCameraDevice.setErrorCallback(null);
1550 CameraHolder.instance().release();
1551 mFaceDetectionStarted = false;
1552 mCameraDevice = null;
1553 setCameraState(PREVIEW_STOPPED);
1554 mFocusManager.onCameraReleased();
1555 }
1556 }
1557
1558 private void setDisplayOrientation() {
1559 mDisplayRotation = Util.getDisplayRotation(mActivity);
1560 mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
1561 mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -08001562 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001563 if (mFocusManager != null) {
1564 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1565 }
1566 // GLRoot also uses the DisplayRotation, and needs to be told to layout to update
1567 mActivity.getGLRoot().requestLayoutContentPane();
1568 }
1569
1570 // Only called by UI thread.
1571 private void setupPreview() {
1572 mFocusManager.resetTouchFocus();
1573 startPreview();
1574 setCameraState(IDLE);
1575 startFaceDetection();
1576 }
1577
1578 // This can be called by UI Thread or CameraStartUpThread. So this should
1579 // not modify the views.
1580 private void startPreview() {
1581 mCameraDevice.setErrorCallback(mErrorCallback);
1582
1583 // ICS camera frameworks has a bug. Face detection state is not cleared
1584 // after taking a picture. Stop the preview to work around it. The bug
1585 // was fixed in JB.
1586 if (mCameraState != PREVIEW_STOPPED) stopPreview();
1587
1588 setDisplayOrientation();
1589
1590 if (!mSnapshotOnIdle) {
1591 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1592 // resume it because it may have been paused by autoFocus call.
1593 if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
1594 mCameraDevice.cancelAutoFocus();
1595 }
1596 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1597 }
1598 setCameraParameters(UPDATE_PARAM_ALL);
1599
1600 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1601 CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail;
Michael Kolbd6954f32013-03-08 20:43:01 -08001602 if (mUI.getSurfaceTexture() == null) {
Michael Kolb8872c232013-01-29 10:33:22 -08001603 Size size = mParameters.getPreviewSize();
1604 if (mCameraDisplayOrientation % 180 == 0) {
1605 screenNail.setSize(size.width, size.height);
1606 } else {
1607 screenNail.setSize(size.height, size.width);
1608 }
1609 screenNail.enableAspectRatioClamping();
1610 mActivity.notifyScreenNailChanged();
1611 screenNail.acquireSurfaceTexture();
1612 CameraStartUpThread t = mCameraStartUpThread;
1613 if (t != null && t.isCanceled()) {
1614 return; // Exiting, so no need to get the surface texture.
1615 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001616 mUI.setSurfaceTexture(screenNail.getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001617 }
1618 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
Michael Kolbd6954f32013-03-08 20:43:01 -08001619 Object st = mUI.getSurfaceTexture();
1620 if (st != null) {
1621 mCameraDevice.setPreviewTextureAsync((SurfaceTexture) st);
Michael Kolb8872c232013-01-29 10:33:22 -08001622 }
1623 } else {
1624 mCameraDevice.setDisplayOrientation(mDisplayOrientation);
Michael Kolbd6954f32013-03-08 20:43:01 -08001625 mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
Michael Kolb8872c232013-01-29 10:33:22 -08001626 }
1627
1628 Log.v(TAG, "startPreview");
1629 mCameraDevice.startPreviewAsync();
1630
1631 mFocusManager.onPreviewStarted();
1632
1633 if (mSnapshotOnIdle) {
1634 mHandler.post(mDoSnapRunnable);
1635 }
1636 }
1637
Michael Kolbd6954f32013-03-08 20:43:01 -08001638 @Override
1639 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001640 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1641 Log.v(TAG, "stopPreview");
1642 mCameraDevice.stopPreview();
1643 mFaceDetectionStarted = false;
1644 }
1645 setCameraState(PREVIEW_STOPPED);
1646 if (mFocusManager != null) mFocusManager.onPreviewStopped();
1647 }
1648
1649 @SuppressWarnings("deprecation")
1650 private void updateCameraParametersInitialize() {
1651 // Reset preview frame rate to the maximum because it may be lowered by
1652 // video camera application.
1653 List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates();
1654 if (frameRates != null) {
1655 Integer max = Collections.max(frameRates);
1656 mParameters.setPreviewFrameRate(max);
1657 }
1658
1659 mParameters.set(Util.RECORDING_HINT, Util.FALSE);
1660
1661 // Disable video stabilization. Convenience methods not available in API
1662 // level <= 14
1663 String vstabSupported = mParameters.get("video-stabilization-supported");
1664 if ("true".equals(vstabSupported)) {
1665 mParameters.set("video-stabilization", "false");
1666 }
1667 }
1668
1669 private void updateCameraParametersZoom() {
1670 // Set zoom.
1671 if (mParameters.isZoomSupported()) {
1672 mParameters.setZoom(mZoomValue);
1673 }
1674 }
1675
1676 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1677 private void setAutoExposureLockIfSupported() {
1678 if (mAeLockSupported) {
1679 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1680 }
1681 }
1682
1683 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1684 private void setAutoWhiteBalanceLockIfSupported() {
1685 if (mAwbLockSupported) {
1686 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1687 }
1688 }
1689
1690 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1691 private void setFocusAreasIfSupported() {
1692 if (mFocusAreaSupported) {
1693 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1694 }
1695 }
1696
1697 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1698 private void setMeteringAreasIfSupported() {
1699 if (mMeteringAreaSupported) {
1700 // Use the same area for focus and metering.
1701 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1702 }
1703 }
1704
1705 private void updateCameraParametersPreference() {
1706 setAutoExposureLockIfSupported();
1707 setAutoWhiteBalanceLockIfSupported();
1708 setFocusAreasIfSupported();
1709 setMeteringAreasIfSupported();
1710
1711 // Set picture size.
1712 String pictureSize = mPreferences.getString(
1713 CameraSettings.KEY_PICTURE_SIZE, null);
1714 if (pictureSize == null) {
1715 CameraSettings.initialCameraPictureSize(mActivity, mParameters);
1716 } else {
1717 List<Size> supported = mParameters.getSupportedPictureSizes();
1718 CameraSettings.setCameraPictureSize(
1719 pictureSize, supported, mParameters);
1720 }
1721 Size size = mParameters.getPictureSize();
1722
1723 // Set a preview size that is closest to the viewfinder height and has
1724 // the right aspect ratio.
1725 List<Size> sizes = mParameters.getSupportedPreviewSizes();
1726 Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes,
1727 (double) size.width / size.height);
1728 Size original = mParameters.getPreviewSize();
1729 if (!original.equals(optimalSize)) {
1730 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
1731
1732 // Zoom related settings will be changed for different preview
1733 // sizes, so set and read the parameters to get latest values
1734 mCameraDevice.setParameters(mParameters);
1735 mParameters = mCameraDevice.getParameters();
1736 }
1737 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1738
1739 // Since changing scene mode may change supported values, set scene mode
1740 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1741 // separate preference.
1742 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
1743 mActivity.getString(R.string.pref_camera_hdr_default));
1744 if (mActivity.getString(R.string.setting_on_value).equals(hdr)) {
1745 mSceneMode = Util.SCENE_MODE_HDR;
1746 } else {
1747 mSceneMode = mPreferences.getString(
1748 CameraSettings.KEY_SCENE_MODE,
1749 mActivity.getString(R.string.pref_camera_scenemode_default));
1750 }
1751 if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
1752 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1753 mParameters.setSceneMode(mSceneMode);
1754
1755 // Setting scene mode will change the settings of flash mode,
1756 // white balance, and focus mode. Here we read back the
1757 // parameters, so we can know those settings.
1758 mCameraDevice.setParameters(mParameters);
1759 mParameters = mCameraDevice.getParameters();
1760 }
1761 } else {
1762 mSceneMode = mParameters.getSceneMode();
1763 if (mSceneMode == null) {
1764 mSceneMode = Parameters.SCENE_MODE_AUTO;
1765 }
1766 }
1767
1768 // Set JPEG quality.
1769 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1770 CameraProfile.QUALITY_HIGH);
1771 mParameters.setJpegQuality(jpegQuality);
1772
1773 // For the following settings, we need to check if the settings are
1774 // still supported by latest driver, if not, ignore the settings.
1775
1776 // Set exposure compensation
1777 int value = CameraSettings.readExposure(mPreferences);
1778 int max = mParameters.getMaxExposureCompensation();
1779 int min = mParameters.getMinExposureCompensation();
1780 if (value >= min && value <= max) {
1781 mParameters.setExposureCompensation(value);
1782 } else {
1783 Log.w(TAG, "invalid exposure range: " + value);
1784 }
1785
1786 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1787 // Set flash mode.
1788 String flashMode = mPreferences.getString(
1789 CameraSettings.KEY_FLASH_MODE,
1790 mActivity.getString(R.string.pref_camera_flashmode_default));
1791 List<String> supportedFlash = mParameters.getSupportedFlashModes();
1792 if (Util.isSupported(flashMode, supportedFlash)) {
1793 mParameters.setFlashMode(flashMode);
1794 } else {
1795 flashMode = mParameters.getFlashMode();
1796 if (flashMode == null) {
1797 flashMode = mActivity.getString(
1798 R.string.pref_camera_flashmode_no_flash);
1799 }
1800 }
1801
1802 // Set white balance parameter.
1803 String whiteBalance = mPreferences.getString(
1804 CameraSettings.KEY_WHITE_BALANCE,
1805 mActivity.getString(R.string.pref_camera_whitebalance_default));
1806 if (Util.isSupported(whiteBalance,
1807 mParameters.getSupportedWhiteBalance())) {
1808 mParameters.setWhiteBalance(whiteBalance);
1809 } else {
1810 whiteBalance = mParameters.getWhiteBalance();
1811 if (whiteBalance == null) {
1812 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1813 }
1814 }
1815
1816 // Set focus mode.
1817 mFocusManager.overrideFocusMode(null);
1818 mParameters.setFocusMode(mFocusManager.getFocusMode());
1819 } else {
1820 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1821 }
1822
1823 if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1824 updateAutoFocusMoveCallback();
1825 }
1826 }
1827
1828 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1829 private void updateAutoFocusMoveCallback() {
1830 if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
1831 mCameraDevice.setAutoFocusMoveCallback(
1832 (AutoFocusMoveCallback) mAutoFocusMoveCallback);
1833 } else {
1834 mCameraDevice.setAutoFocusMoveCallback(null);
1835 }
1836 }
1837
1838 // We separate the parameters into several subsets, so we can update only
1839 // the subsets actually need updating. The PREFERENCE set needs extra
1840 // locking because the preference can be changed from GLThread as well.
1841 private void setCameraParameters(int updateSet) {
1842 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1843 updateCameraParametersInitialize();
1844 }
1845
1846 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1847 updateCameraParametersZoom();
1848 }
1849
1850 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
1851 updateCameraParametersPreference();
1852 }
1853
1854 mCameraDevice.setParameters(mParameters);
1855 }
1856
1857 // If the Camera is idle, update the parameters immediately, otherwise
1858 // accumulate them in mUpdateSet and update later.
1859 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1860 mUpdateSet |= additionalUpdateSet;
1861 if (mCameraDevice == null) {
1862 // We will update all the parameters when we open the device, so
1863 // we don't need to do anything now.
1864 mUpdateSet = 0;
1865 return;
1866 } else if (isCameraIdle()) {
1867 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001868 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001869 mUpdateSet = 0;
1870 } else {
1871 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1872 mHandler.sendEmptyMessageDelayed(
1873 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1874 }
1875 }
1876 }
1877
Michael Kolbd6954f32013-03-08 20:43:01 -08001878 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001879 return (mCameraState == IDLE) ||
1880 (mCameraState == PREVIEW_STOPPED) ||
1881 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1882 && (mCameraState != SWITCHING_CAMERA));
1883 }
1884
Michael Kolbd6954f32013-03-08 20:43:01 -08001885 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001886 String action = mActivity.getIntent().getAction();
1887 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
1888 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
1889 }
1890
1891 private void setupCaptureParams() {
1892 Bundle myExtras = mActivity.getIntent().getExtras();
1893 if (myExtras != null) {
1894 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1895 mCropValue = myExtras.getString("crop");
1896 }
1897 }
1898
Michael Kolb8872c232013-01-29 10:33:22 -08001899 @Override
1900 public void onSharedPreferenceChanged() {
1901 // ignore the events after "onPause()"
1902 if (mPaused) return;
1903
1904 boolean recordLocation = RecordLocationPreference.get(
1905 mPreferences, mContentResolver);
1906 mLocationManager.recordLocation(recordLocation);
1907
1908 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
Michael Kolbd6954f32013-03-08 20:43:01 -08001909 mUI.updateOnScreenIndicators(mParameters, mPreferences);
Michael Kolb8872c232013-01-29 10:33:22 -08001910 }
1911
1912 @Override
1913 public void onCameraPickerClicked(int cameraId) {
1914 if (mPaused || mPendingSwitchCameraId != -1) return;
1915
1916 mPendingSwitchCameraId = cameraId;
1917 if (ApiHelper.HAS_SURFACE_TEXTURE) {
1918 Log.v(TAG, "Start to copy texture. cameraId=" + cameraId);
1919 // We need to keep a preview frame for the animation before
1920 // releasing the camera. This will trigger onPreviewTextureCopied.
1921 ((CameraScreenNail) mActivity.mCameraScreenNail).copyTexture();
1922 // Disable all camera controls.
1923 setCameraState(SWITCHING_CAMERA);
1924 } else {
1925 switchCamera();
1926 }
1927 }
1928
Michael Kolb8872c232013-01-29 10:33:22 -08001929 // Preview texture has been copied. Now camera can be released and the
1930 // animation can be started.
1931 @Override
1932 public void onPreviewTextureCopied() {
1933 mHandler.sendEmptyMessage(SWITCH_CAMERA);
1934 }
1935
1936 @Override
1937 public void onCaptureTextureCopied() {
1938 }
1939
1940 @Override
1941 public void onUserInteraction() {
1942 if (!mActivity.isFinishing()) keepScreenOnAwhile();
1943 }
1944
1945 private void resetScreenOn() {
1946 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1947 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1948 }
1949
1950 private void keepScreenOnAwhile() {
1951 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1952 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1953 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1954 }
1955
1956 // TODO: Delete this function after old camera code is removed
1957 @Override
1958 public void onRestorePreferencesClicked() {
1959 }
1960
1961 @Override
1962 public void onOverriddenPreferencesClicked() {
1963 if (mPaused) return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001964 mUI.showPreferencesToast();
Michael Kolb8872c232013-01-29 10:33:22 -08001965 }
1966
1967 private void showTapToFocusToast() {
1968 // TODO: Use a toast?
1969 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1970 // Clear the preference.
1971 Editor editor = mPreferences.edit();
1972 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
1973 editor.apply();
1974 }
1975
1976 private void initializeCapabilities() {
1977 mInitialParams = mCameraDevice.getParameters();
1978 mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
1979 mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
1980 mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
1981 mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams);
1982 mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
1983 Util.FOCUS_MODE_CONTINUOUS_PICTURE);
1984 }
1985
Michael Kolb8872c232013-01-29 10:33:22 -08001986 @Override
1987 public void onCountDownFinished() {
1988 mSnapshotOnIdle = false;
1989 mFocusManager.doSnap();
Doris Liuda50e052013-02-07 14:36:32 -08001990 mFocusManager.onShutterUp();
Michael Kolb8872c232013-01-29 10:33:22 -08001991 }
1992
Michael Kolb8872c232013-01-29 10:33:22 -08001993 @Override
1994 public boolean needsSwitcher() {
1995 return !mIsImageCaptureIntent;
1996 }
1997
Doris Liu6a0de792013-02-26 10:54:25 -08001998 @Override
1999 public boolean needsPieMenu() {
2000 return true;
2001 }
2002
Michael Kolb8872c232013-01-29 10:33:22 -08002003 @Override
2004 public void onShowSwitcherPopup() {
Michael Kolbd6954f32013-03-08 20:43:01 -08002005 mUI.onShowSwitcherPopup();
Michael Kolb8872c232013-01-29 10:33:22 -08002006 }
2007
Angus Kongce5480e2013-01-29 17:43:48 -08002008 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002009 public int onZoomChanged(int index) {
2010 // Not useful to change zoom value when the activity is paused.
2011 if (mPaused) return index;
2012 mZoomValue = index;
2013 if (mParameters == null || mCameraDevice == null) return index;
2014 // Set zoom parameters asynchronously
2015 mParameters.setZoom(mZoomValue);
2016 mCameraDevice.setParametersAsync(mParameters);
2017 Parameters p = mCameraDevice.getParameters();
2018 if (p != null) return p.getZoom();
2019 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08002020 }
2021
2022 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002023 public int getCameraState() {
2024 return mCameraState;
2025 }
2026
2027 @Override
2028 public void onQueueStatus(boolean full) {
2029 mUI.enableShutter(!full);
Angus Kongce5480e2013-01-29 17:43:48 -08002030 }
Angus Kong86d36312013-01-31 18:22:44 -08002031
2032 @Override
2033 public void onMediaSaveServiceConnected(MediaSaveService s) {
2034 // We set the listener only when both service and shutterbutton
2035 // are initialized.
Michael Kolbd6954f32013-03-08 20:43:01 -08002036 if (mFirstTimeInitialized) {
2037 s.setListener(this);
2038 }
Angus Kong86d36312013-01-31 18:22:44 -08002039 }
Angus Kong0d00a892013-03-26 11:40:40 -07002040
2041 @Override
2042 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2043 }
2044
2045 @Override
2046 public void onSensorChanged(SensorEvent event) {
2047 int type = event.sensor.getType();
2048 float[] data;
2049 if (type == Sensor.TYPE_ACCELEROMETER) {
2050 data = mGData;
2051 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2052 data = mMData;
2053 } else {
2054 // we should not be here.
2055 return;
2056 }
2057 for (int i = 0; i < 3 ; i++) {
2058 data[i] = event.values[i];
2059 }
2060 float[] orientation = new float[3];
2061 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2062 SensorManager.getOrientation(mR, orientation);
2063 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2064 if (mHeading < 0) {
2065 mHeading += 360;
2066 }
2067 Log.v(TAG, "heading:" + mHeading);
2068 }
Michael Kolb8872c232013-01-29 10:33:22 -08002069}