blob: 76bcbbee54dd6e88efbd6a5c5ebab3168db0e492 [file] [log] [blame]
Doris Liu7234f4d2013-05-14 14:37:46 -07001/*
2 * Copyright (C) 2013 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;
24import android.content.Context;
25import 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;
32import android.hardware.Camera.Parameters;
33import android.hardware.Camera.PictureCallback;
34import android.hardware.Camera.Size;
35import android.hardware.Sensor;
36import android.hardware.SensorEvent;
37import android.hardware.SensorEventListener;
38import android.hardware.SensorManager;
39import 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;
51import android.view.KeyEvent;
52import android.view.MotionEvent;
53import android.view.OrientationEventListener;
54import android.view.SurfaceHolder;
55import android.view.View;
56import android.view.WindowManager;
57
58import com.android.camera.CameraManager.CameraProxy;
59import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
60import com.android.camera.ui.PopupManager;
61import com.android.camera.ui.RotateTextToast;
62import com.android.gallery3d.R;
63import com.android.gallery3d.common.ApiHelper;
64import com.android.gallery3d.exif.ExifInterface;
65import com.android.gallery3d.exif.ExifTag;
66import com.android.gallery3d.exif.Rational;
Ruben Brunk88741b42013-05-28 12:34:14 -070067import com.android.gallery3d.filtershow.crop.CropActivity;
Doris Liu7234f4d2013-05-14 14:37:46 -070068import com.android.gallery3d.filtershow.crop.CropExtras;
Doris Liu7234f4d2013-05-14 14:37:46 -070069import com.android.gallery3d.util.UsageStatistics;
70
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 NewPhotoModule
82 implements NewCameraModule,
83 PhotoController,
84 FocusOverlayManager.Listener,
85 CameraPreference.OnPreferenceChangedListener,
86 ShutterButton.OnShutterButtonListener,
87 MediaSaveService.Listener,
88 OnCountDownFinishedListener,
89 SensorEventListener {
90
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;
Doris Liu4da59672013-05-31 17:26:12 -0700108 private static final int CAPTURE_ANIMATION_DONE = 13;
Doris Liu7234f4d2013-05-14 14:37:46 -0700109
110 // The subset of parameters we need to update in setCameraParameters().
111 private static final int UPDATE_PARAM_INITIALIZE = 1;
112 private static final int UPDATE_PARAM_ZOOM = 2;
113 private static final int UPDATE_PARAM_PREFERENCE = 4;
114 private static final int UPDATE_PARAM_ALL = -1;
115
116 // This is the timeout to keep the camera in onPause for the first time
117 // after screen on if the activity is started from secure lock screen.
118 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
119
120 // copied from Camera hierarchy
121 private NewCameraActivity mActivity;
122 private CameraProxy mCameraDevice;
123 private int mCameraId;
124 private Parameters mParameters;
125 private boolean mPaused;
126
127 private NewPhotoUI mUI;
128
Doris Liu4da59672013-05-31 17:26:12 -0700129 // The activity is going to switch to the specified camera id. This is
130 // needed because texture copy is done in GL thread. -1 means camera is not
131 // switching.
Doris Liu7234f4d2013-05-14 14:37:46 -0700132 protected int mPendingSwitchCameraId = -1;
133 private boolean mOpenCameraFail;
134 private boolean mCameraDisabled;
135
136 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
137 // needed to be updated in mUpdateSet.
138 private int mUpdateSet;
139
140 private static final int SCREEN_DELAY = 2 * 60 * 1000;
141
142 private int mZoomValue; // The current zoom value.
143
144 private Parameters mInitialParams;
145 private boolean mFocusAreaSupported;
146 private boolean mMeteringAreaSupported;
147 private boolean mAeLockSupported;
148 private boolean mAwbLockSupported;
149 private boolean mContinousFocusSupported;
150
151 // The degrees of the device rotated clockwise from its natural orientation.
152 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
153 private ComboPreferences mPreferences;
154
155 private static final String sTempCropFilename = "crop-temp";
156
157 private ContentProviderClient mMediaProviderClient;
158 private boolean mFaceDetectionStarted = false;
159
160 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
161 private String mCropValue;
162 private Uri mSaveUri;
163
164 // We use a queue to generated names of the images to be used later
165 // when the image is ready to be saved.
166 private NamedImages mNamedImages;
167
168 private Runnable mDoSnapRunnable = new Runnable() {
169 @Override
170 public void run() {
171 onShutterButtonClick();
172 }
173 };
174
Doris Liu4da59672013-05-31 17:26:12 -0700175 private Runnable mFlashRunnable = new Runnable() {
176 @Override
177 public void run() {
178 animateFlash();
179 }
180 };
181
Doris Liu7234f4d2013-05-14 14:37:46 -0700182 private final StringBuilder mBuilder = new StringBuilder();
183 private final Formatter mFormatter = new Formatter(mBuilder);
184 private final Object[] mFormatterArgs = new Object[1];
185
186 /**
187 * An unpublished intent flag requesting to return as soon as capturing
188 * is completed.
189 *
190 * TODO: consider publishing by moving into MediaStore.
191 */
192 private static final String EXTRA_QUICK_CAPTURE =
193 "android.intent.extra.quickCapture";
194
195 // The display rotation in degrees. This is only valid when mCameraState is
196 // not PREVIEW_STOPPED.
197 private int mDisplayRotation;
198 // The value for android.hardware.Camera.setDisplayOrientation.
199 private int mCameraDisplayOrientation;
200 // The value for UI components like indicators.
201 private int mDisplayOrientation;
202 // The value for android.hardware.Camera.Parameters.setRotation.
203 private int mJpegRotation;
204 private boolean mFirstTimeInitialized;
205 private boolean mIsImageCaptureIntent;
206
207 private int mCameraState = PREVIEW_STOPPED;
208 private boolean mSnapshotOnIdle = false;
209
210 private ContentResolver mContentResolver;
211
212 private LocationManager mLocationManager;
213
Doris Liu7234f4d2013-05-14 14:37:46 -0700214 private final PostViewPictureCallback mPostViewPictureCallback =
215 new PostViewPictureCallback();
216 private final RawPictureCallback mRawPictureCallback =
217 new RawPictureCallback();
218 private final AutoFocusCallback mAutoFocusCallback =
219 new AutoFocusCallback();
220 private final Object mAutoFocusMoveCallback =
221 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
222 ? new AutoFocusMoveCallback()
223 : null;
224
225 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
226
227 private long mFocusStartTime;
228 private long mShutterCallbackTime;
229 private long mPostViewPictureCallbackTime;
230 private long mRawPictureCallbackTime;
231 private long mJpegPictureCallbackTime;
232 private long mOnResumeTime;
233 private byte[] mJpegImageData;
234
235 // These latency time are for the CameraLatency test.
236 public long mAutoFocusTime;
237 public long mShutterLag;
238 public long mShutterToPictureDisplayedTime;
239 public long mPictureDisplayedToJpegCallbackTime;
240 public long mJpegCallbackFinishTime;
241 public long mCaptureStartTime;
242
243 // This handles everything about focus.
244 private FocusOverlayManager mFocusManager;
245
246 private String mSceneMode;
247
248 private final Handler mHandler = new MainHandler();
249 private PreferenceGroup mPreferenceGroup;
250
251 private boolean mQuickCapture;
252 private SensorManager mSensorManager;
253 private float[] mGData = new float[3];
254 private float[] mMData = new float[3];
255 private float[] mR = new float[16];
256 private int mHeading = -1;
257
258 CameraStartUpThread mCameraStartUpThread;
259 ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
260
261 private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
262 new MediaSaveService.OnMediaSavedListener() {
263 @Override
264 public void onMediaSaved(Uri uri) {
265 if (uri != null) {
Angus Kong885367c2013-05-31 16:30:32 -0700266 mActivity.notifyNewMedia(uri);
Doris Liu7234f4d2013-05-14 14:37:46 -0700267 }
268 }
269 };
270
271 // The purpose is not to block the main thread in onCreate and onResume.
272 private class CameraStartUpThread extends Thread {
273 private volatile boolean mCancelled;
274
275 public void cancel() {
276 mCancelled = true;
277 interrupt();
278 }
279
280 public boolean isCanceled() {
281 return mCancelled;
282 }
283
284 @Override
285 public void run() {
286 try {
287 // We need to check whether the activity is paused before long
288 // operations to ensure that onPause() can be done ASAP.
289 if (mCancelled) return;
290 mCameraDevice = Util.openCamera(mActivity, mCameraId);
291 mParameters = mCameraDevice.getParameters();
292 // Wait until all the initialization needed by startPreview are
293 // done.
294 mStartPreviewPrerequisiteReady.block();
295
296 initializeCapabilities();
297 if (mFocusManager == null) initializeFocusManager();
298 if (mCancelled) return;
299 setCameraParameters(UPDATE_PARAM_ALL);
300 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
301 if (mCancelled) return;
302 startPreview();
303 mHandler.sendEmptyMessage(START_PREVIEW_DONE);
304 mOnResumeTime = SystemClock.uptimeMillis();
305 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
306 } catch (CameraHardwareException e) {
307 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
308 } catch (CameraDisabledException e) {
309 mHandler.sendEmptyMessage(CAMERA_DISABLED);
310 }
311 }
312 }
313
314 /**
315 * This Handler is used to post message back onto the main thread of the
316 * application
317 */
318 private class MainHandler extends Handler {
319 @Override
320 public void handleMessage(Message msg) {
321 switch (msg.what) {
322 case SETUP_PREVIEW: {
323 setupPreview();
324 break;
325 }
326
327 case CLEAR_SCREEN_DELAY: {
328 mActivity.getWindow().clearFlags(
329 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
330 break;
331 }
332
333 case FIRST_TIME_INIT: {
334 initializeFirstTime();
335 break;
336 }
337
338 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
339 setCameraParametersWhenIdle(0);
340 break;
341 }
342
343 case CHECK_DISPLAY_ROTATION: {
344 // Set the display orientation if display rotation has changed.
345 // Sometimes this happens when the device is held upside
346 // down and camera app is opened. Rotation animation will
347 // take some time and the rotation value we have got may be
348 // wrong. Framework does not have a callback for this now.
349 if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
350 setDisplayOrientation();
351 }
352 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
353 mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
354 }
355 break;
356 }
357
358 case SHOW_TAP_TO_FOCUS_TOAST: {
359 showTapToFocusToast();
360 break;
361 }
362
363 case SWITCH_CAMERA: {
364 switchCamera();
365 break;
366 }
367
368 case SWITCH_CAMERA_START_ANIMATION: {
369 // TODO: Need to revisit
370 // ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
371 break;
372 }
373
374 case CAMERA_OPEN_DONE: {
375 onCameraOpened();
376 break;
377 }
378
379 case START_PREVIEW_DONE: {
380 onPreviewStarted();
381 break;
382 }
383
384 case OPEN_CAMERA_FAIL: {
385 mCameraStartUpThread = null;
386 mOpenCameraFail = true;
387 Util.showErrorAndFinish(mActivity,
388 R.string.cannot_connect_camera);
389 break;
390 }
391
392 case CAMERA_DISABLED: {
393 mCameraStartUpThread = null;
394 mCameraDisabled = true;
395 Util.showErrorAndFinish(mActivity,
396 R.string.camera_disabled);
397 break;
398 }
Doris Liu4da59672013-05-31 17:26:12 -0700399 case CAPTURE_ANIMATION_DONE: {
400 mUI.enablePreviewThumb(false);
401 break;
402 }
Doris Liu7234f4d2013-05-14 14:37:46 -0700403 }
404 }
405 }
406
407 @Override
408 public void init(NewCameraActivity activity, View parent) {
409 mActivity = activity;
410 mUI = new NewPhotoUI(activity, this, parent);
411 mPreferences = new ComboPreferences(mActivity);
412 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
413 mCameraId = getPreferredCameraId(mPreferences);
414
415 mContentResolver = mActivity.getContentResolver();
416
417 // To reduce startup time, open the camera and start the preview in
418 // another thread.
419 mCameraStartUpThread = new CameraStartUpThread();
420 mCameraStartUpThread.start();
421
422 // Surface texture is from camera screen nail and startPreview needs it.
423 // This must be done before startPreview.
424 mIsImageCaptureIntent = isImageCaptureIntent();
425
426 mPreferences.setLocalId(mActivity, mCameraId);
427 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
428 // we need to reset exposure for the preview
429 resetExposureCompensation();
430 // Starting the preview needs preferences, camera screen nail, and
431 // focus area indicator.
432 mStartPreviewPrerequisiteReady.open();
433
434 initializeControlByIntent();
435 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
436 mLocationManager = new LocationManager(mActivity, mUI);
437 mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
438 }
439
440 private void initializeControlByIntent() {
441 mUI.initializeControlByIntent();
442 if (mIsImageCaptureIntent) {
443 setupCaptureParams();
444 }
445 }
446
447 private void onPreviewStarted() {
448 mCameraStartUpThread = null;
449 setCameraState(IDLE);
450 startFaceDetection();
451 locationFirstRun();
452 }
453
454 // Prompt the user to pick to record location for the very first run of
455 // camera only
456 private void locationFirstRun() {
457 if (RecordLocationPreference.isSet(mPreferences)) {
458 return;
459 }
460 if (mActivity.isSecureCamera()) return;
461 // Check if the back camera exists
462 int backCameraId = CameraHolder.instance().getBackCameraId();
463 if (backCameraId == -1) {
464 // If there is no back camera, do not show the prompt.
465 return;
466 }
467
468 new AlertDialog.Builder(mActivity)
469 .setTitle(R.string.remember_location_title)
470 .setMessage(R.string.remember_location_prompt)
471 .setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() {
472 @Override
473 public void onClick(DialogInterface dialog, int arg1) {
474 setLocationPreference(RecordLocationPreference.VALUE_ON);
475 }
476 })
477 .setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() {
478 @Override
479 public void onClick(DialogInterface dialog, int arg1) {
480 dialog.cancel();
481 }
482 })
483 .setOnCancelListener(new DialogInterface.OnCancelListener() {
484 @Override
485 public void onCancel(DialogInterface dialog) {
486 setLocationPreference(RecordLocationPreference.VALUE_OFF);
487 }
488 })
489 .show();
490 }
491
492 private void setLocationPreference(String value) {
493 mPreferences.edit()
494 .putString(CameraSettings.KEY_RECORD_LOCATION, value)
495 .apply();
496 // TODO: Fix this to use the actual onSharedPreferencesChanged listener
497 // instead of invoking manually
498 onSharedPreferenceChanged();
499 }
500
501 private void onCameraOpened() {
502 View root = mUI.getRootView();
503 // These depend on camera parameters.
504
505 int width = root.getWidth();
506 int height = root.getHeight();
507 mFocusManager.setPreviewSize(width, height);
508 openCameraCommon();
509 }
510
511 private void switchCamera() {
512 if (mPaused) return;
513
514 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
515 mCameraId = mPendingSwitchCameraId;
516 mPendingSwitchCameraId = -1;
517 setCameraId(mCameraId);
518
519 // from onPause
520 closeCamera();
521 mUI.collapseCameraControls();
522 mUI.clearFaces();
523 if (mFocusManager != null) mFocusManager.removeMessages();
524
525 // Restart the camera and initialize the UI. From onCreate.
526 mPreferences.setLocalId(mActivity, mCameraId);
527 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
528 try {
529 mCameraDevice = Util.openCamera(mActivity, mCameraId);
530 mParameters = mCameraDevice.getParameters();
531 } catch (CameraHardwareException e) {
532 Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
533 return;
534 } catch (CameraDisabledException e) {
535 Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
536 return;
537 }
538 initializeCapabilities();
539 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
540 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
541 mFocusManager.setMirror(mirror);
542 mFocusManager.setParameters(mInitialParams);
543 setupPreview();
544
545 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);
551 }
552 }
553
554 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() {
561 loadCameraPreferences();
562
563 mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
564 updateSceneMode();
565 showTapToFocusToastIfNeeded();
566
567
568 }
569
570 public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight) {
571 if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
572 }
573
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
607 mUI.initializeFirstTime();
608 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 }
614
615 mNamedImages = new NamedImages();
616
617 mFirstTimeInitialized = true;
618 addIdleHandler();
619
620 mActivity.updateStorageSpaceAndHint();
621 }
622
623 // 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
653 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
673 @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;
680 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
681 mUI.onStartFaceDetection(mDisplayOrientation,
682 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
683 mCameraDevice.setFaceDetectionListener(mUI);
684 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();
697 mUI.clearFaces();
698 }
699 }
700
Doris Liu7234f4d2013-05-14 14:37:46 -0700701 private final class ShutterCallback
702 implements android.hardware.Camera.ShutterCallback {
Doris Liu4da59672013-05-31 17:26:12 -0700703
704 private boolean mAnimateFlash;
705
706 public ShutterCallback(boolean animateFlash) {
707 mAnimateFlash = animateFlash;
708 }
709
Doris Liu7234f4d2013-05-14 14:37:46 -0700710 @Override
711 public void onShutter() {
712 mShutterCallbackTime = System.currentTimeMillis();
713 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
714 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Doris Liu4da59672013-05-31 17:26:12 -0700715 if (mAnimateFlash) {
716 mActivity.runOnUiThread(mFlashRunnable);
717 }
Doris Liu7234f4d2013-05-14 14:37:46 -0700718 }
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 }
755 if (mSceneMode == Util.SCENE_MODE_HDR) {
756 mUI.showSwitcher();
Doris Liuf7758b62013-06-06 16:54:18 -0700757 mUI.setSwipingEnabled(true);
Doris Liu7234f4d2013-05-14 14:37:46 -0700758 }
759
760 mJpegPictureCallbackTime = System.currentTimeMillis();
761 // If postview callback has arrived, the captured image is displayed
762 // in postview callback. If not, the captured image is displayed in
763 // raw picture callback.
764 if (mPostViewPictureCallbackTime != 0) {
765 mShutterToPictureDisplayedTime =
766 mPostViewPictureCallbackTime - mShutterCallbackTime;
767 mPictureDisplayedToJpegCallbackTime =
768 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
769 } else {
770 mShutterToPictureDisplayedTime =
771 mRawPictureCallbackTime - mShutterCallbackTime;
772 mPictureDisplayedToJpegCallbackTime =
773 mJpegPictureCallbackTime - mRawPictureCallbackTime;
774 }
775 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
776 + mPictureDisplayedToJpegCallbackTime + "ms");
777
778 /*TODO:
779 // Only animate when in full screen capture mode
780 // i.e. If monkey/a user swipes to the gallery during picture taking,
781 // don't show animation
782 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
783 && mActivity.mShowCameraAppView) {
784 // Finish capture animation
Doris Liu4da59672013-05-31 17:26:12 -0700785 mHandler.removeMessages(CAPTURE_ANIMATION_DONE);
Doris Liu7234f4d2013-05-14 14:37:46 -0700786 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSlide();
Doris Liu4da59672013-05-31 17:26:12 -0700787 mHandler.sendEmptyMessageDelayed(CAPTURE_ANIMATION_DONE,
788 CaptureAnimManager.getAnimationDuration());
Doris Liu7234f4d2013-05-14 14:37:46 -0700789 } */
790 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
791 if (!mIsImageCaptureIntent) {
792 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
793 setupPreview();
794 } else {
795 // Camera HAL of some devices have a bug. Starting preview
796 // immediately after taking a picture will fail. Wait some
797 // time before starting the preview.
798 mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
799 }
800 }
801
802 if (!mIsImageCaptureIntent) {
803 // Calculate the width and the height of the jpeg.
804 Size s = mParameters.getPictureSize();
805 ExifInterface exif = Exif.getExif(jpegData);
806 int orientation = Exif.getOrientation(exif);
807 int width, height;
808 if ((mJpegRotation + orientation) % 180 == 0) {
809 width = s.width;
810 height = s.height;
811 } else {
812 width = s.height;
813 height = s.width;
814 }
815 String title = mNamedImages.getTitle();
816 long date = mNamedImages.getDate();
817 if (title == null) {
818 Log.e(TAG, "Unbalanced name/data pair");
819 } else {
820 if (date == -1) date = mCaptureStartTime;
821 if (mHeading >= 0) {
822 // heading direction has been updated by the sensor.
823 ExifTag directionRefTag = exif.buildTag(
824 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
825 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
826 ExifTag directionTag = exif.buildTag(
827 ExifInterface.TAG_GPS_IMG_DIRECTION,
828 new Rational(mHeading, 1));
829 exif.setTag(directionRefTag);
830 exif.setTag(directionTag);
831 }
832 mActivity.getMediaSaveService().addImage(
833 jpegData, title, date, mLocation, width, height,
834 orientation, exif, mOnMediaSavedListener, mContentResolver);
835 }
836 } else {
837 mJpegImageData = jpegData;
838 if (!mQuickCapture) {
839 mUI.showPostCaptureAlert();
840 } else {
841 onCaptureDone();
842 }
843 }
844
845 // Check this in advance of each shot so we don't add to shutter
846 // latency. It's true that someone else could write to the SD card in
847 // the mean time and fill it, but that could have happened between the
848 // shutter press and saving the JPEG too.
849 mActivity.updateStorageSpaceAndHint();
850
851 long now = System.currentTimeMillis();
852 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
853 Log.v(TAG, "mJpegCallbackFinishTime = "
854 + mJpegCallbackFinishTime + "ms");
855 mJpegPictureCallbackTime = 0;
856 }
857 }
858
859 private final class AutoFocusCallback
860 implements android.hardware.Camera.AutoFocusCallback {
861 @Override
862 public void onAutoFocus(
863 boolean focused, android.hardware.Camera camera) {
864 if (mPaused) return;
865
866 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
867 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
868 setCameraState(IDLE);
869 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
870 }
871 }
872
873 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
874 private final class AutoFocusMoveCallback
875 implements android.hardware.Camera.AutoFocusMoveCallback {
876 @Override
877 public void onAutoFocusMoving(
878 boolean moving, android.hardware.Camera camera) {
879 mFocusManager.onAutoFocusMoving(moving);
880 }
881 }
882
883 private static class NamedImages {
884 private ArrayList<NamedEntity> mQueue;
885 private boolean mStop;
886 private NamedEntity mNamedEntity;
887
888 public NamedImages() {
889 mQueue = new ArrayList<NamedEntity>();
890 }
891
892 public void nameNewImage(ContentResolver resolver, long date) {
893 NamedEntity r = new NamedEntity();
894 r.title = Util.createJpegName(date);
895 r.date = date;
896 mQueue.add(r);
897 }
898
899 public String getTitle() {
900 if (mQueue.isEmpty()) {
901 mNamedEntity = null;
902 return null;
903 }
904 mNamedEntity = mQueue.get(0);
905 mQueue.remove(0);
906
907 return mNamedEntity.title;
908 }
909
910 // Must be called after getTitle().
911 public long getDate() {
912 if (mNamedEntity == null) return -1;
913 return mNamedEntity.date;
914 }
915
916 private static class NamedEntity {
917 String title;
918 long date;
919 }
920 }
921
922 private void setCameraState(int state) {
923 mCameraState = state;
924 switch (state) {
925 case PhotoController.PREVIEW_STOPPED:
926 case PhotoController.SNAPSHOT_IN_PROGRESS:
Doris Liu7234f4d2013-05-14 14:37:46 -0700927 case PhotoController.SWITCHING_CAMERA:
928 mUI.enableGestures(false);
929 break;
930 case PhotoController.IDLE:
931 mUI.enableGestures(true);
932 break;
933 }
934 }
935
936 private void animateFlash() {
937 /* //TODO:
938 // Only animate when in full screen capture mode
939 // i.e. If monkey/a user swipes to the gallery during picture taking,
940 // don't show animation
941 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
942 && mActivity.mShowCameraAppView) {
943 // Start capture animation.
944 ((CameraScreenNail) mActivity.mCameraScreenNail).animateFlash(mDisplayRotation);
Doris Liu4da59672013-05-31 17:26:12 -0700945 mUI.enablePreviewThumb(true);
946 mHandler.sendEmptyMessageDelayed(CAPTURE_ANIMATION_DONE,
947 CaptureAnimManager.getAnimationDuration());
Doris Liu7234f4d2013-05-14 14:37:46 -0700948 } */
949 }
950
951 @Override
952 public boolean capture() {
953 // If we are already in the middle of taking a snapshot or the image save request
954 // is full then ignore.
955 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
956 || mCameraState == SWITCHING_CAMERA
957 || mActivity.getMediaSaveService().isQueueFull()) {
958 return false;
959 }
960 mCaptureStartTime = System.currentTimeMillis();
961 mPostViewPictureCallbackTime = 0;
962 mJpegImageData = null;
963
964 final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR);
965
966 if (animateBefore) {
967 animateFlash();
968 }
969
970 // Set rotation and gps data.
Doris Liu4da59672013-05-31 17:26:12 -0700971 int orientation;
Doris Liu7234f4d2013-05-14 14:37:46 -0700972 // We need to be consistent with the framework orientation (i.e. the
973 // orientation of the UI.) when the auto-rotate screen setting is on.
974 if (mActivity.isAutoRotateScreen()) {
975 orientation = (360 - mDisplayRotation) % 360;
976 } else {
977 orientation = mOrientation;
978 }
979 mJpegRotation = Util.getJpegRotation(mCameraId, orientation);
980 mParameters.setRotation(mJpegRotation);
981 Location loc = mLocationManager.getCurrentLocation();
982 Util.setGpsParameters(mParameters, loc);
983 mCameraDevice.setParameters(mParameters);
984
Doris Liu4da59672013-05-31 17:26:12 -0700985 mCameraDevice.takePicture2(new ShutterCallback(!animateBefore),
986 mRawPictureCallback, mPostViewPictureCallback,
987 new JpegPictureCallback(loc), mCameraState,
988 mFocusManager.getFocusState());
Doris Liu7234f4d2013-05-14 14:37:46 -0700989
990 mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime);
991
992 mFaceDetectionStarted = false;
993 setCameraState(SNAPSHOT_IN_PROGRESS);
Doris Liu4da59672013-05-31 17:26:12 -0700994 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
995 UsageStatistics.ACTION_CAPTURE_DONE, "Photo");
Doris Liu7234f4d2013-05-14 14:37:46 -0700996 return true;
997 }
998
999 @Override
1000 public void setFocusParameters() {
1001 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1002 }
1003
1004 private int getPreferredCameraId(ComboPreferences preferences) {
1005 int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
1006 if (intentCameraId != -1) {
1007 // Testing purpose. Launch a specific camera through the intent
1008 // extras.
1009 return intentCameraId;
1010 } else {
1011 return CameraSettings.readPreferredCameraId(preferences);
1012 }
1013 }
1014
1015 private void updateSceneMode() {
1016 // If scene mode is set, we cannot set flash mode, white balance, and
1017 // focus mode, instead, we read it from driver
1018 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1019 overrideCameraSettings(mParameters.getFlashMode(),
1020 mParameters.getWhiteBalance(), mParameters.getFocusMode());
1021 } else {
1022 overrideCameraSettings(null, null, null);
1023 }
1024 }
1025
1026 private void overrideCameraSettings(final String flashMode,
1027 final String whiteBalance, final String focusMode) {
1028 mUI.overrideSettings(
1029 CameraSettings.KEY_FLASH_MODE, flashMode,
1030 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
1031 CameraSettings.KEY_FOCUS_MODE, focusMode);
1032 }
1033
1034 private void loadCameraPreferences() {
1035 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
1036 mCameraId, CameraHolder.instance().getCameraInfo());
1037 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
1038 }
1039
1040 @Override
1041 public void onOrientationChanged(int orientation) {
1042 // We keep the last known orientation. So if the user first orient
1043 // the camera then point the camera to floor or sky, we still have
1044 // the correct orientation.
1045 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
1046 mOrientation = Util.roundOrientation(orientation, mOrientation);
1047
1048 // Show the toast after getting the first orientation changed.
1049 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1050 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1051 showTapToFocusToast();
1052 }
1053 }
1054
1055 @Override
1056 public void onStop() {
1057 if (mMediaProviderClient != null) {
1058 mMediaProviderClient.release();
1059 mMediaProviderClient = null;
1060 }
1061 }
1062
1063 @Override
1064 public void onCaptureCancelled() {
1065 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1066 mActivity.finish();
1067 }
1068
1069 @Override
1070 public void onCaptureRetake() {
1071 if (mPaused)
1072 return;
1073 mUI.hidePostCaptureAlert();
1074 setupPreview();
1075 }
1076
1077 @Override
1078 public void onCaptureDone() {
1079 if (mPaused) {
1080 return;
1081 }
1082
1083 byte[] data = mJpegImageData;
1084
1085 if (mCropValue == null) {
1086 // First handle the no crop case -- just return the value. If the
1087 // caller specifies a "save uri" then write the data to its
1088 // stream. Otherwise, pass back a scaled down version of the bitmap
1089 // directly in the extras.
1090 if (mSaveUri != null) {
1091 OutputStream outputStream = null;
1092 try {
1093 outputStream = mContentResolver.openOutputStream(mSaveUri);
1094 outputStream.write(data);
1095 outputStream.close();
1096
1097 mActivity.setResultEx(Activity.RESULT_OK);
1098 mActivity.finish();
1099 } catch (IOException ex) {
1100 // ignore exception
1101 } finally {
1102 Util.closeSilently(outputStream);
1103 }
1104 } else {
1105 ExifInterface exif = Exif.getExif(data);
1106 int orientation = Exif.getOrientation(exif);
1107 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
1108 bitmap = Util.rotate(bitmap, orientation);
1109 mActivity.setResultEx(Activity.RESULT_OK,
1110 new Intent("inline-data").putExtra("data", bitmap));
1111 mActivity.finish();
1112 }
1113 } else {
1114 // Save the image to a temp file and invoke the cropper
1115 Uri tempUri = null;
1116 FileOutputStream tempStream = null;
1117 try {
1118 File path = mActivity.getFileStreamPath(sTempCropFilename);
1119 path.delete();
1120 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1121 tempStream.write(data);
1122 tempStream.close();
1123 tempUri = Uri.fromFile(path);
1124 } catch (FileNotFoundException ex) {
1125 mActivity.setResultEx(Activity.RESULT_CANCELED);
1126 mActivity.finish();
1127 return;
1128 } catch (IOException ex) {
1129 mActivity.setResultEx(Activity.RESULT_CANCELED);
1130 mActivity.finish();
1131 return;
1132 } finally {
1133 Util.closeSilently(tempStream);
1134 }
1135
1136 Bundle newExtras = new Bundle();
1137 if (mCropValue.equals("circle")) {
1138 newExtras.putString("circleCrop", "true");
1139 }
1140 if (mSaveUri != null) {
1141 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1142 } else {
1143 newExtras.putBoolean(CropExtras.KEY_RETURN_DATA, true);
1144 }
1145 if (mActivity.isSecureCamera()) {
1146 newExtras.putBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, true);
1147 }
1148
Ruben Brunk88741b42013-05-28 12:34:14 -07001149 Intent cropIntent = new Intent(CropActivity.CROP_ACTION);
Doris Liu7234f4d2013-05-14 14:37:46 -07001150
1151 cropIntent.setData(tempUri);
1152 cropIntent.putExtras(newExtras);
1153
1154 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1155 }
1156 }
1157
1158 @Override
1159 public void onShutterButtonFocus(boolean pressed) {
1160 if (mPaused || mUI.collapseCameraControls()
1161 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1162 || (mCameraState == PREVIEW_STOPPED)) return;
1163
1164 // Do not do focus if there is not enough storage.
1165 if (pressed && !canTakePicture()) return;
1166
1167 if (pressed) {
Doris Liu7234f4d2013-05-14 14:37:46 -07001168 mFocusManager.onShutterDown();
1169 } else {
1170 // for countdown mode, we need to postpone the shutter release
1171 // i.e. lock the focus during countdown.
1172 if (!mUI.isCountingDown()) {
1173 mFocusManager.onShutterUp();
1174 }
1175 }
1176 }
1177
1178 @Override
1179 public void onShutterButtonClick() {
1180 if (mPaused || mUI.collapseCameraControls()
1181 || (mCameraState == SWITCHING_CAMERA)
1182 || (mCameraState == PREVIEW_STOPPED)) return;
1183
1184 // Do not take the picture if there is not enough storage.
1185 if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
1186 Log.i(TAG, "Not enough space or storage not ready. remaining="
1187 + mActivity.getStorageSpace());
1188 return;
1189 }
1190 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1191
Doris Liu4da59672013-05-31 17:26:12 -07001192 if (mSceneMode == Util.SCENE_MODE_HDR) {
1193 mUI.hideSwitcher();
Doris Liuf7758b62013-06-06 16:54:18 -07001194 mUI.setSwipingEnabled(false);
Doris Liu4da59672013-05-31 17:26:12 -07001195 }
Doris Liu7234f4d2013-05-14 14:37:46 -07001196 // If the user wants to do a snapshot while the previous one is still
1197 // in progress, remember the fact and do it after we finish the previous
1198 // one and re-start the preview. Snapshot in progress also includes the
1199 // state that autofocus is focusing and a picture will be taken when
1200 // focus callback arrives.
1201 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1202 && !mIsImageCaptureIntent) {
1203 mSnapshotOnIdle = true;
1204 return;
1205 }
1206
1207 String timer = mPreferences.getString(
1208 CameraSettings.KEY_TIMER,
1209 mActivity.getString(R.string.pref_camera_timer_default));
1210 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1211 mActivity.getString(R.string.pref_camera_timer_sound_default))
1212 .equals(mActivity.getString(R.string.setting_on_value));
1213
1214 int seconds = Integer.parseInt(timer);
1215 // When shutter button is pressed, check whether the previous countdown is
1216 // finished. If not, cancel the previous countdown and start a new one.
1217 if (mUI.isCountingDown()) {
1218 mUI.cancelCountDown();
1219 }
1220 if (seconds > 0) {
1221 mUI.startCountDown(seconds, playSound);
1222 } else {
1223 mSnapshotOnIdle = false;
1224 mFocusManager.doSnap();
1225 }
1226 }
1227
1228 @Override
1229 public void installIntentFilter() {
1230 }
1231
1232 @Override
1233 public boolean updateStorageHintOnResume() {
1234 return mFirstTimeInitialized;
1235 }
1236
1237 @Override
1238 public void updateCameraAppView() {
1239 }
1240
1241 @Override
1242 public void onResumeBeforeSuper() {
1243 mPaused = false;
1244 }
1245
1246 @Override
1247 public void onResumeAfterSuper() {
1248 if (mOpenCameraFail || mCameraDisabled) return;
1249
1250 mJpegPictureCallbackTime = 0;
1251 mZoomValue = 0;
1252 // Start the preview if it is not started.
1253 if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
1254 resetExposureCompensation();
1255 mCameraStartUpThread = new CameraStartUpThread();
1256 mCameraStartUpThread.start();
1257 }
1258
1259 // If first time initialization is not finished, put it in the
1260 // message queue.
1261 if (!mFirstTimeInitialized) {
1262 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1263 } else {
1264 initializeSecondTime();
1265 }
1266 keepScreenOnAwhile();
1267
1268 // Dismiss open menu if exists.
1269 PopupManager.getInstance(mActivity).notifyShowPopup(null);
1270 UsageStatistics.onContentViewChanged(
1271 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
1272
1273 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1274 if (gsensor != null) {
1275 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1276 }
1277
1278 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1279 if (msensor != null) {
1280 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1281 }
1282 }
1283
1284 void waitCameraStartUpThread() {
1285 try {
1286 if (mCameraStartUpThread != null) {
1287 mCameraStartUpThread.cancel();
1288 mCameraStartUpThread.join();
1289 mCameraStartUpThread = null;
1290 setCameraState(IDLE);
1291 }
1292 } catch (InterruptedException e) {
1293 // ignore
1294 }
1295 }
1296
1297 @Override
1298 public void onPauseBeforeSuper() {
1299 mPaused = true;
1300 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1301 if (gsensor != null) {
1302 mSensorManager.unregisterListener(this, gsensor);
1303 }
1304
1305 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1306 if (msensor != null) {
1307 mSensorManager.unregisterListener(this, msensor);
1308 }
1309 }
1310
1311 @Override
1312 public void onPauseAfterSuper() {
1313 // Wait the camera start up thread to finish.
1314 waitCameraStartUpThread();
1315
1316 // When camera is started from secure lock screen for the first time
1317 // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1318 // To reduce the latency, keep the camera for a short time so it does
1319 // not need to be opened again.
1320 if (mCameraDevice != null && mActivity.isSecureCamera()
1321 && ActivityBase.isFirstStartAfterScreenOn()) {
1322 ActivityBase.resetFirstStartAfterScreenOn();
1323 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1324 }
1325 // Reset the focus first. Camera CTS does not guarantee that
1326 // cancelAutoFocus is allowed after preview stops.
1327 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1328 mCameraDevice.cancelAutoFocus();
1329 }
1330 stopPreview();
1331
1332 mNamedImages = null;
1333
1334 if (mLocationManager != null) mLocationManager.recordLocation(false);
1335
1336 // If we are in an image capture intent and has taken
1337 // a picture, we just clear it in onPause.
1338 mJpegImageData = null;
1339
1340 // Remove the messages in the event queue.
1341 mHandler.removeMessages(SETUP_PREVIEW);
1342 mHandler.removeMessages(FIRST_TIME_INIT);
1343 mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
1344 mHandler.removeMessages(SWITCH_CAMERA);
1345 mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
1346 mHandler.removeMessages(CAMERA_OPEN_DONE);
1347 mHandler.removeMessages(START_PREVIEW_DONE);
1348 mHandler.removeMessages(OPEN_CAMERA_FAIL);
1349 mHandler.removeMessages(CAMERA_DISABLED);
1350
1351 closeCamera();
1352
1353 resetScreenOn();
1354 mUI.onPause();
1355
1356 mPendingSwitchCameraId = -1;
1357 if (mFocusManager != null) mFocusManager.removeMessages();
1358 MediaSaveService s = mActivity.getMediaSaveService();
1359 if (s != null) {
1360 s.setListener(null);
1361 }
1362 }
1363
1364 /**
1365 * The focus manager is the first UI related element to get initialized,
1366 * and it requires the RenderOverlay, so initialize it here
1367 */
1368 private void initializeFocusManager() {
1369 // Create FocusManager object. startPreview needs it.
1370 // if mFocusManager not null, reuse it
1371 // otherwise create a new instance
1372 if (mFocusManager != null) {
1373 mFocusManager.removeMessages();
1374 } else {
1375 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1376 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
1377 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1378 R.array.pref_camera_focusmode_default_array);
1379 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
1380 mInitialParams, this, mirror,
1381 mActivity.getMainLooper(), mUI);
1382 }
1383 }
1384
1385 @Override
1386 public void onConfigurationChanged(Configuration newConfig) {
1387 Log.v(TAG, "onConfigurationChanged");
1388 setDisplayOrientation();
1389 }
1390
1391 @Override
1392 public void onActivityResult(
1393 int requestCode, int resultCode, Intent data) {
1394 switch (requestCode) {
1395 case REQUEST_CROP: {
1396 Intent intent = new Intent();
1397 if (data != null) {
1398 Bundle extras = data.getExtras();
1399 if (extras != null) {
1400 intent.putExtras(extras);
1401 }
1402 }
1403 mActivity.setResultEx(resultCode, intent);
1404 mActivity.finish();
1405
1406 File path = mActivity.getFileStreamPath(sTempCropFilename);
1407 path.delete();
1408
1409 break;
1410 }
1411 }
1412 }
1413
1414 private boolean canTakePicture() {
1415 return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
1416 }
1417
1418 @Override
1419 public void autoFocus() {
1420 mFocusStartTime = System.currentTimeMillis();
1421 mCameraDevice.autoFocus(mAutoFocusCallback);
1422 setCameraState(FOCUSING);
1423 }
1424
1425 @Override
1426 public void cancelAutoFocus() {
1427 mCameraDevice.cancelAutoFocus();
1428 setCameraState(IDLE);
1429 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1430 }
1431
1432 // Preview area is touched. Handle touch focus.
1433 @Override
1434 public void onSingleTapUp(View view, int x, int y) {
1435 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1436 || mCameraState == SNAPSHOT_IN_PROGRESS
1437 || mCameraState == SWITCHING_CAMERA
1438 || mCameraState == PREVIEW_STOPPED) {
1439 return;
1440 }
1441
1442 // Do not trigger touch focus if popup window is opened.
1443 if (mUI.removeTopLevelPopup()) return;
1444
1445 // Check if metering area or focus area is supported.
1446 if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1447 mFocusManager.onSingleTapUp(x, y);
1448 }
1449
1450 @Override
1451 public boolean onBackPressed() {
1452 return mUI.onBackPressed();
1453 }
1454
1455 @Override
1456 public boolean onKeyDown(int keyCode, KeyEvent event) {
1457 switch (keyCode) {
1458 case KeyEvent.KEYCODE_VOLUME_UP:
1459 case KeyEvent.KEYCODE_VOLUME_DOWN:
1460 case KeyEvent.KEYCODE_FOCUS:
1461 if (/*TODO: mActivity.isInCameraApp() &&*/ mFirstTimeInitialized) {
1462 if (event.getRepeatCount() == 0) {
1463 onShutterButtonFocus(true);
1464 }
1465 return true;
1466 }
1467 return false;
1468 case KeyEvent.KEYCODE_CAMERA:
1469 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1470 onShutterButtonClick();
1471 }
1472 return true;
1473 case KeyEvent.KEYCODE_DPAD_CENTER:
1474 // If we get a dpad center event without any focused view, move
1475 // the focus to the shutter button and press it.
1476 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1477 // Start auto-focus immediately to reduce shutter lag. After
1478 // the shutter button gets the focus, onShutterButtonFocus()
1479 // will be called again but it is fine.
1480 if (mUI.removeTopLevelPopup()) return true;
1481 onShutterButtonFocus(true);
1482 mUI.pressShutterButton();
1483 }
1484 return true;
1485 }
1486 return false;
1487 }
1488
1489 @Override
1490 public boolean onKeyUp(int keyCode, KeyEvent event) {
1491 switch (keyCode) {
1492 case KeyEvent.KEYCODE_VOLUME_UP:
1493 case KeyEvent.KEYCODE_VOLUME_DOWN:
1494 if (/*mActivity.isInCameraApp() && */ mFirstTimeInitialized) {
1495 onShutterButtonClick();
1496 return true;
1497 }
1498 return false;
1499 case KeyEvent.KEYCODE_FOCUS:
1500 if (mFirstTimeInitialized) {
1501 onShutterButtonFocus(false);
1502 }
1503 return true;
1504 }
1505 return false;
1506 }
1507
1508 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1509 private void closeCamera() {
1510 if (mCameraDevice != null) {
1511 mCameraDevice.setZoomChangeListener(null);
1512 if(ApiHelper.HAS_FACE_DETECTION) {
1513 mCameraDevice.setFaceDetectionListener(null);
1514 }
1515 mCameraDevice.setErrorCallback(null);
1516 CameraHolder.instance().release();
1517 mFaceDetectionStarted = false;
1518 mCameraDevice = null;
1519 setCameraState(PREVIEW_STOPPED);
1520 mFocusManager.onCameraReleased();
1521 }
1522 }
1523
1524 private void setDisplayOrientation() {
1525 mDisplayRotation = Util.getDisplayRotation(mActivity);
1526 mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
1527 mCameraDisplayOrientation = mDisplayOrientation;
1528 mUI.setDisplayOrientation(mDisplayOrientation);
1529 if (mFocusManager != null) {
1530 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1531 }
1532 // Change the camera display orientation
1533 if (mCameraDevice != null) {
1534 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1535 }
1536 }
1537
1538 // Only called by UI thread.
1539 private void setupPreview() {
1540 mFocusManager.resetTouchFocus();
1541 startPreview();
1542 setCameraState(IDLE);
1543 startFaceDetection();
1544 }
1545
1546 // This can be called by UI Thread or CameraStartUpThread. So this should
1547 // not modify the views.
1548 private void startPreview() {
1549 mCameraDevice.setErrorCallback(mErrorCallback);
1550
1551 // ICS camera frameworks has a bug. Face detection state is not cleared
1552 // after taking a picture. Stop the preview to work around it. The bug
1553 // was fixed in JB.
1554 if (mCameraState != PREVIEW_STOPPED) stopPreview();
1555
1556 setDisplayOrientation();
1557
1558 if (!mSnapshotOnIdle) {
1559 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1560 // resume it because it may have been paused by autoFocus call.
1561 if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
1562 mCameraDevice.cancelAutoFocus();
1563 }
1564 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1565 }
1566 setCameraParameters(UPDATE_PARAM_ALL);
1567 // Let UI set its expected aspect ratio
1568 mUI.setPreviewSize(mParameters.getPreviewSize());
1569 Object st = mUI.getSurfaceTexture();
1570 if (st != null) {
1571 mCameraDevice.setPreviewTextureAsync((SurfaceTexture) st);
1572 }
1573
1574 Log.v(TAG, "startPreview");
1575 mCameraDevice.startPreviewAsync();
1576 mFocusManager.onPreviewStarted();
1577
1578 if (mSnapshotOnIdle) {
1579 mHandler.post(mDoSnapRunnable);
1580 }
1581 }
1582
1583 @Override
1584 public void stopPreview() {
1585 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1586 Log.v(TAG, "stopPreview");
1587 mCameraDevice.stopPreview();
1588 mFaceDetectionStarted = false;
1589 }
1590 setCameraState(PREVIEW_STOPPED);
1591 if (mFocusManager != null) mFocusManager.onPreviewStopped();
1592 }
1593
1594 @SuppressWarnings("deprecation")
1595 private void updateCameraParametersInitialize() {
1596 // Reset preview frame rate to the maximum because it may be lowered by
1597 // video camera application.
Angus Kongd82eae22013-06-11 12:00:15 -07001598 int[] frameRates = Util.getMaxPreviewFpsRange(mParameters);
1599 if (fpsRange.length > 0) {
1600 mParameters.setPreviewFpsRange(
1601 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1602 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Doris Liu7234f4d2013-05-14 14:37:46 -07001603 }
1604
1605 mParameters.set(Util.RECORDING_HINT, Util.FALSE);
1606
1607 // Disable video stabilization. Convenience methods not available in API
1608 // level <= 14
1609 String vstabSupported = mParameters.get("video-stabilization-supported");
1610 if ("true".equals(vstabSupported)) {
1611 mParameters.set("video-stabilization", "false");
1612 }
1613 }
1614
1615 private void updateCameraParametersZoom() {
1616 // Set zoom.
1617 if (mParameters.isZoomSupported()) {
1618 mParameters.setZoom(mZoomValue);
1619 }
1620 }
1621
1622 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1623 private void setAutoExposureLockIfSupported() {
1624 if (mAeLockSupported) {
1625 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1626 }
1627 }
1628
1629 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1630 private void setAutoWhiteBalanceLockIfSupported() {
1631 if (mAwbLockSupported) {
1632 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1633 }
1634 }
1635
1636 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1637 private void setFocusAreasIfSupported() {
1638 if (mFocusAreaSupported) {
1639 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1640 }
1641 }
1642
1643 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1644 private void setMeteringAreasIfSupported() {
1645 if (mMeteringAreaSupported) {
1646 // Use the same area for focus and metering.
1647 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1648 }
1649 }
1650
1651 private void updateCameraParametersPreference() {
1652 setAutoExposureLockIfSupported();
1653 setAutoWhiteBalanceLockIfSupported();
1654 setFocusAreasIfSupported();
1655 setMeteringAreasIfSupported();
1656
1657 // Set picture size.
1658 String pictureSize = mPreferences.getString(
1659 CameraSettings.KEY_PICTURE_SIZE, null);
1660 if (pictureSize == null) {
1661 CameraSettings.initialCameraPictureSize(mActivity, mParameters);
1662 } else {
1663 List<Size> supported = mParameters.getSupportedPictureSizes();
1664 CameraSettings.setCameraPictureSize(
1665 pictureSize, supported, mParameters);
1666 }
1667 Size size = mParameters.getPictureSize();
1668
1669 // Set a preview size that is closest to the viewfinder height and has
1670 // the right aspect ratio.
1671 List<Size> sizes = mParameters.getSupportedPreviewSizes();
1672 Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes,
1673 (double) size.width / size.height);
1674 Size original = mParameters.getPreviewSize();
1675 if (!original.equals(optimalSize)) {
1676 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
1677
1678 // Zoom related settings will be changed for different preview
1679 // sizes, so set and read the parameters to get latest values
Doris Liu4da59672013-05-31 17:26:12 -07001680 if (mHandler.getLooper() == Looper.myLooper()) {
1681 // On UI thread only, not when camera starts up
1682 setupPreview();
1683 } else {
1684 mCameraDevice.setParameters(mParameters);
1685 }
Doris Liu7234f4d2013-05-14 14:37:46 -07001686 mParameters = mCameraDevice.getParameters();
1687 }
1688 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1689
1690 // Since changing scene mode may change supported values, set scene mode
1691 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1692 // separate preference.
1693 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
1694 mActivity.getString(R.string.pref_camera_hdr_default));
1695 if (mActivity.getString(R.string.setting_on_value).equals(hdr)) {
1696 mSceneMode = Util.SCENE_MODE_HDR;
1697 } else {
1698 mSceneMode = mPreferences.getString(
1699 CameraSettings.KEY_SCENE_MODE,
1700 mActivity.getString(R.string.pref_camera_scenemode_default));
1701 }
1702 if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
1703 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1704 mParameters.setSceneMode(mSceneMode);
1705
1706 // Setting scene mode will change the settings of flash mode,
1707 // white balance, and focus mode. Here we read back the
1708 // parameters, so we can know those settings.
1709 mCameraDevice.setParameters(mParameters);
1710 mParameters = mCameraDevice.getParameters();
1711 }
1712 } else {
1713 mSceneMode = mParameters.getSceneMode();
1714 if (mSceneMode == null) {
1715 mSceneMode = Parameters.SCENE_MODE_AUTO;
1716 }
1717 }
1718
1719 // Set JPEG quality.
1720 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1721 CameraProfile.QUALITY_HIGH);
1722 mParameters.setJpegQuality(jpegQuality);
1723
1724 // For the following settings, we need to check if the settings are
1725 // still supported by latest driver, if not, ignore the settings.
1726
1727 // Set exposure compensation
1728 int value = CameraSettings.readExposure(mPreferences);
1729 int max = mParameters.getMaxExposureCompensation();
1730 int min = mParameters.getMinExposureCompensation();
1731 if (value >= min && value <= max) {
1732 mParameters.setExposureCompensation(value);
1733 } else {
1734 Log.w(TAG, "invalid exposure range: " + value);
1735 }
1736
1737 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1738 // Set flash mode.
1739 String flashMode = mPreferences.getString(
1740 CameraSettings.KEY_FLASH_MODE,
1741 mActivity.getString(R.string.pref_camera_flashmode_default));
1742 List<String> supportedFlash = mParameters.getSupportedFlashModes();
1743 if (Util.isSupported(flashMode, supportedFlash)) {
1744 mParameters.setFlashMode(flashMode);
1745 } else {
1746 flashMode = mParameters.getFlashMode();
1747 if (flashMode == null) {
1748 flashMode = mActivity.getString(
1749 R.string.pref_camera_flashmode_no_flash);
1750 }
1751 }
1752
1753 // Set white balance parameter.
1754 String whiteBalance = mPreferences.getString(
1755 CameraSettings.KEY_WHITE_BALANCE,
1756 mActivity.getString(R.string.pref_camera_whitebalance_default));
1757 if (Util.isSupported(whiteBalance,
1758 mParameters.getSupportedWhiteBalance())) {
1759 mParameters.setWhiteBalance(whiteBalance);
1760 } else {
1761 whiteBalance = mParameters.getWhiteBalance();
1762 if (whiteBalance == null) {
1763 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1764 }
1765 }
1766
1767 // Set focus mode.
1768 mFocusManager.overrideFocusMode(null);
1769 mParameters.setFocusMode(mFocusManager.getFocusMode());
1770 } else {
1771 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1772 }
1773
1774 if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1775 updateAutoFocusMoveCallback();
1776 }
1777 }
1778
1779 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
1780 private void updateAutoFocusMoveCallback() {
1781 if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
1782 mCameraDevice.setAutoFocusMoveCallback(
1783 (AutoFocusMoveCallback) mAutoFocusMoveCallback);
1784 } else {
1785 mCameraDevice.setAutoFocusMoveCallback(null);
1786 }
1787 }
1788
1789 // We separate the parameters into several subsets, so we can update only
1790 // the subsets actually need updating. The PREFERENCE set needs extra
1791 // locking because the preference can be changed from GLThread as well.
1792 private void setCameraParameters(int updateSet) {
1793 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1794 updateCameraParametersInitialize();
1795 }
1796
1797 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1798 updateCameraParametersZoom();
1799 }
1800
1801 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
1802 updateCameraParametersPreference();
1803 }
1804
1805 mCameraDevice.setParameters(mParameters);
1806 }
1807
1808 // If the Camera is idle, update the parameters immediately, otherwise
1809 // accumulate them in mUpdateSet and update later.
1810 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1811 mUpdateSet |= additionalUpdateSet;
1812 if (mCameraDevice == null) {
1813 // We will update all the parameters when we open the device, so
1814 // we don't need to do anything now.
1815 mUpdateSet = 0;
1816 return;
1817 } else if (isCameraIdle()) {
1818 setCameraParameters(mUpdateSet);
1819 updateSceneMode();
1820 mUpdateSet = 0;
1821 } else {
1822 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1823 mHandler.sendEmptyMessageDelayed(
1824 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1825 }
1826 }
1827 }
1828
1829 public boolean isCameraIdle() {
1830 return (mCameraState == IDLE) ||
1831 (mCameraState == PREVIEW_STOPPED) ||
1832 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1833 && (mCameraState != SWITCHING_CAMERA));
1834 }
1835
1836 public boolean isImageCaptureIntent() {
1837 String action = mActivity.getIntent().getAction();
1838 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
1839 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
1840 }
1841
1842 private void setupCaptureParams() {
1843 Bundle myExtras = mActivity.getIntent().getExtras();
1844 if (myExtras != null) {
1845 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1846 mCropValue = myExtras.getString("crop");
1847 }
1848 }
1849
1850 @Override
1851 public void onSharedPreferenceChanged() {
1852 // ignore the events after "onPause()"
1853 if (mPaused) return;
1854
1855 boolean recordLocation = RecordLocationPreference.get(
1856 mPreferences, mContentResolver);
1857 mLocationManager.recordLocation(recordLocation);
1858
1859 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
1860 mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, mPreferences);
1861 }
1862
1863 @Override
1864 public void onCameraPickerClicked(int cameraId) {
1865 if (mPaused || mPendingSwitchCameraId != -1) return;
1866
1867 mPendingSwitchCameraId = cameraId;
1868
1869 Log.v(TAG, "Start to switch camera. cameraId=" + cameraId);
1870 // We need to keep a preview frame for the animation before
1871 // releasing the camera. This will trigger onPreviewTextureCopied.
1872 //TODO: Need to animate the camera switch
1873 switchCamera();
1874 }
1875
1876 // Preview texture has been copied. Now camera can be released and the
1877 // animation can be started.
1878 @Override
1879 public void onPreviewTextureCopied() {
1880 mHandler.sendEmptyMessage(SWITCH_CAMERA);
1881 }
1882
1883 @Override
1884 public void onCaptureTextureCopied() {
1885 }
1886
1887 @Override
1888 public void onUserInteraction() {
1889 if (!mActivity.isFinishing()) keepScreenOnAwhile();
1890 }
1891
1892 private void resetScreenOn() {
1893 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1894 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1895 }
1896
1897 private void keepScreenOnAwhile() {
1898 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1899 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1900 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1901 }
1902
1903 @Override
1904 public void onOverriddenPreferencesClicked() {
1905 if (mPaused) return;
1906 mUI.showPreferencesToast();
1907 }
1908
1909 private void showTapToFocusToast() {
1910 // TODO: Use a toast?
1911 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1912 // Clear the preference.
1913 Editor editor = mPreferences.edit();
1914 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
1915 editor.apply();
1916 }
1917
1918 private void initializeCapabilities() {
1919 mInitialParams = mCameraDevice.getParameters();
1920 mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
1921 mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
1922 mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
1923 mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams);
1924 mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
1925 Util.FOCUS_MODE_CONTINUOUS_PICTURE);
1926 }
1927
1928 @Override
1929 public void onCountDownFinished() {
1930 mSnapshotOnIdle = false;
1931 mFocusManager.doSnap();
1932 mFocusManager.onShutterUp();
1933 }
1934
1935 @Override
1936 public boolean needsSwitcher() {
1937 return !mIsImageCaptureIntent;
1938 }
1939
1940 @Override
1941 public boolean needsPieMenu() {
1942 return true;
1943 }
1944
1945 @Override
1946 public void onShowSwitcherPopup() {
1947 mUI.onShowSwitcherPopup();
1948 }
1949
1950 @Override
1951 public int onZoomChanged(int index) {
1952 // Not useful to change zoom value when the activity is paused.
1953 if (mPaused) return index;
1954 mZoomValue = index;
1955 if (mParameters == null || mCameraDevice == null) return index;
1956 // Set zoom parameters asynchronously
1957 mParameters.setZoom(mZoomValue);
1958 mCameraDevice.setParameters(mParameters);
1959 Parameters p = mCameraDevice.getParameters();
1960 if (p != null) return p.getZoom();
1961 return index;
1962 }
1963
1964 @Override
1965 public int getCameraState() {
1966 return mCameraState;
1967 }
1968
1969 @Override
1970 public void onQueueStatus(boolean full) {
1971 mUI.enableShutter(!full);
1972 }
1973
1974 @Override
1975 public void onMediaSaveServiceConnected(MediaSaveService s) {
1976 // We set the listener only when both service and shutterbutton
1977 // are initialized.
1978 if (mFirstTimeInitialized) {
1979 s.setListener(this);
1980 }
1981 }
1982
1983 @Override
1984 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1985 }
1986
1987 @Override
1988 public void onSensorChanged(SensorEvent event) {
1989 int type = event.sensor.getType();
1990 float[] data;
1991 if (type == Sensor.TYPE_ACCELEROMETER) {
1992 data = mGData;
1993 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
1994 data = mMData;
1995 } else {
1996 // we should not be here.
1997 return;
1998 }
1999 for (int i = 0; i < 3 ; i++) {
2000 data[i] = event.values[i];
2001 }
2002 float[] orientation = new float[3];
2003 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2004 SensorManager.getOrientation(mR, orientation);
2005 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2006 if (mHeading < 0) {
2007 mHeading += 360;
2008 }
2009 }
2010/* Below is no longer needed, except to get rid of compile error
2011 * TODO: Remove these
2012 */
2013
2014 // TODO: Delete this function after old camera code is removed
2015 @Override
2016 public void onRestorePreferencesClicked() {}
2017
2018 @Override
2019 public void onFullScreenChanged(boolean full) {
2020 /* //TODO:
2021 mUI.onFullScreenChanged(full);
2022 if (ApiHelper.HAS_SURFACE_TEXTURE) {
2023 if (mActivity.mCameraScreenNail != null) {
2024 ((CameraScreenNail) mActivity.mCameraScreenNail).setFullScreen(full);
2025 }
2026 return;
2027 } */
2028 }
2029
2030}