2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.camera;
19 import android.annotation.TargetApi;
20 import android.app.Activity;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.graphics.Bitmap;
25 import android.graphics.BitmapFactory;
26 import android.graphics.SurfaceTexture;
27 import android.hardware.Camera.CameraInfo;
28 import android.hardware.Camera.Parameters;
29 import android.hardware.Sensor;
30 import android.hardware.SensorEvent;
31 import android.hardware.SensorEventListener;
32 import android.hardware.SensorManager;
33 import android.location.Location;
34 import android.media.AudioManager;
35 import android.media.CameraProfile;
36 import android.media.SoundPool;
37 import android.net.Uri;
38 import android.os.AsyncTask;
39 import android.os.Build;
40 import android.os.Bundle;
41 import android.os.Handler;
42 import android.os.Looper;
43 import android.os.Message;
44 import android.os.MessageQueue;
45 import android.os.SystemClock;
46 import android.provider.MediaStore;
47 import android.view.KeyEvent;
48 import android.view.OrientationEventListener;
49 import android.view.View;
51 import com.android.camera.PhotoModule.NamedImages.NamedEntity;
52 import com.android.camera.app.AppController;
53 import com.android.camera.app.CameraAppUI;
54 import com.android.camera.app.CameraProvider;
55 import com.android.camera.app.LocationManager;
56 import com.android.camera.app.MediaSaver;
57 import com.android.camera.app.MemoryManager;
58 import com.android.camera.app.MemoryManager.MemoryListener;
59 import com.android.camera.app.MotionManager;
60 import com.android.camera.cameradevice.CameraCapabilities;
61 import com.android.camera.cameradevice.CameraManager;
62 import com.android.camera.cameradevice.CameraManager.CameraAFCallback;
63 import com.android.camera.cameradevice.CameraManager.CameraAFMoveCallback;
64 import com.android.camera.cameradevice.CameraManager.CameraPictureCallback;
65 import com.android.camera.cameradevice.CameraManager.CameraProxy;
66 import com.android.camera.cameradevice.CameraManager.CameraShutterCallback;
67 import com.android.camera.debug.Log;
68 import com.android.camera.exif.ExifInterface;
69 import com.android.camera.exif.ExifTag;
70 import com.android.camera.exif.Rational;
71 import com.android.camera.hardware.HardwareSpec;
72 import com.android.camera.hardware.HardwareSpecImpl;
73 import com.android.camera.module.ModuleController;
74 import com.android.camera.remote.RemoteCameraModule;
75 import com.android.camera.settings.CameraPictureSizesCacher;
76 import com.android.camera.settings.ResolutionUtil;
77 import com.android.camera.settings.SettingsManager;
78 import com.android.camera.settings.SettingsUtil;
79 import com.android.camera.ui.CountDownView;
80 import com.android.camera.ui.TouchCoordinate;
81 import com.android.camera.util.ApiHelper;
82 import com.android.camera.util.CameraUtil;
83 import com.android.camera.util.GcamHelper;
84 import com.android.camera.util.SessionStatsCollector;
85 import com.android.camera.util.Size;
86 import com.android.camera.util.UsageStatistics;
87 import com.android.camera.widget.AspectRatioSelector;
88 import com.android.camera2.R;
89 import com.google.common.logging.eventprotos;
91 import java.io.ByteArrayOutputStream;
93 import java.io.FileNotFoundException;
94 import java.io.FileOutputStream;
95 import java.io.IOException;
96 import java.io.OutputStream;
97 import java.lang.ref.WeakReference;
98 import java.text.DecimalFormat;
99 import java.util.List;
100 import java.util.Vector;
102 public class PhotoModule
104 implements PhotoController,
107 FocusOverlayManager.Listener,
109 SettingsManager.OnSettingChangedListener,
111 CountDownView.OnCountDownStatusListener {
113 private static final Log.Tag TAG = new Log.Tag("PhotoModule");
115 // We number the request code from 1000 to avoid collision with Gallery.
116 private static final int REQUEST_CROP = 1000;
118 // Messages defined for the UI thread handler.
119 private static final int MSG_FIRST_TIME_INIT = 1;
120 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
122 // The subset of parameters we need to update in setCameraParameters().
123 private static final int UPDATE_PARAM_INITIALIZE = 1;
124 private static final int UPDATE_PARAM_ZOOM = 2;
125 private static final int UPDATE_PARAM_PREFERENCE = 4;
126 private static final int UPDATE_PARAM_ALL = -1;
128 // This is the delay before we execute onResume tasks when coming
129 // from the lock screen, to allow time for onPause to execute.
130 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
132 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
134 private static DecimalFormat sMegaPixelFormat = new DecimalFormat("##0.0");
136 private CameraActivity mActivity;
137 private CameraProxy mCameraDevice;
138 private int mCameraId;
139 private CameraCapabilities mCameraCapabilities;
140 private Parameters mParameters;
141 private boolean mPaused;
142 private boolean mShouldSetPreviewCallbacks = ApiHelper.SHOULD_HARD_RESET_PREVIEW_CALLBACK;
146 // The activity is going to switch to the specified camera id. This is
147 // needed because texture copy is done in GL thread. -1 means camera is not
149 protected int mPendingSwitchCameraId = -1;
151 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
152 // needed to be updated in mUpdateSet.
153 private int mUpdateSet;
155 private static final int SCREEN_DELAY = 2 * 60 * 1000;
157 private int mZoomValue; // The current zoom value.
158 private int mTimerDuration;
159 /** Set when a volume button is clicked to take photo */
160 private boolean mVolumeButtonClickedFlag = false;
162 private Parameters mInitialParams;
163 private boolean mFocusAreaSupported;
164 private boolean mMeteringAreaSupported;
165 private boolean mAeLockSupported;
166 private boolean mAwbLockSupported;
167 private boolean mContinuousFocusSupported;
169 // The degrees of the device rotated clockwise from its natural orientation.
170 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
172 private static final String sTempCropFilename = "crop-temp";
174 private boolean mFaceDetectionStarted = false;
176 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
177 private String mCropValue;
178 private Uri mSaveUri;
180 private Uri mDebugUri;
182 // We use a queue to generated names of the images to be used later
183 // when the image is ready to be saved.
184 private NamedImages mNamedImages;
186 private final Runnable mDoSnapRunnable = new Runnable() {
189 onShutterButtonClick();
194 * An unpublished intent flag requesting to return as soon as capturing is
195 * completed. TODO: consider publishing by moving into MediaStore.
197 private static final String EXTRA_QUICK_CAPTURE =
198 "android.intent.extra.quickCapture";
200 // The display rotation in degrees. This is only valid when mCameraState is
201 // not PREVIEW_STOPPED.
202 private int mDisplayRotation;
203 // The value for android.hardware.Camera.setDisplayOrientation.
204 private int mCameraDisplayOrientation;
205 // The value for UI components like indicators.
206 private int mDisplayOrientation;
207 // The value for android.hardware.Camera.Parameters.setRotation.
208 private int mJpegRotation;
209 // Indicates whether we are using front camera
210 private boolean mMirror;
211 private boolean mFirstTimeInitialized;
212 private boolean mIsImageCaptureIntent;
214 private int mCameraState = PREVIEW_STOPPED;
215 private boolean mSnapshotOnIdle = false;
217 private ContentResolver mContentResolver;
219 private LocationManager mLocationManager;
220 private AppController mAppController;
222 private final PostViewPictureCallback mPostViewPictureCallback =
223 new PostViewPictureCallback();
224 private final RawPictureCallback mRawPictureCallback =
225 new RawPictureCallback();
226 private final AutoFocusCallback mAutoFocusCallback =
227 new AutoFocusCallback();
228 private final Object mAutoFocusMoveCallback =
229 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
230 ? new AutoFocusMoveCallback()
233 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
235 private long mFocusStartTime;
236 private long mShutterCallbackTime;
237 private long mPostViewPictureCallbackTime;
238 private long mRawPictureCallbackTime;
239 private long mJpegPictureCallbackTime;
240 private long mOnResumeTime;
241 private byte[] mJpegImageData;
242 /** Touch coordinate for shutter button press. */
243 private TouchCoordinate mShutterTouchCoordinate;
246 // These latency time are for the CameraLatency test.
247 public long mAutoFocusTime;
248 public long mShutterLag;
249 public long mShutterToPictureDisplayedTime;
250 public long mPictureDisplayedToJpegCallbackTime;
251 public long mJpegCallbackFinishTime;
252 public long mCaptureStartTime;
254 // This handles everything about focus.
255 private FocusOverlayManager mFocusManager;
257 private final int mGcamModeIndex;
258 private final CountdownSoundPlayer mCountdownSoundPlayer = new CountdownSoundPlayer();
260 private String mSceneMode;
262 private final Handler mHandler = new MainHandler(this);
264 private boolean mQuickCapture;
265 private SensorManager mSensorManager;
266 private final float[] mGData = new float[3];
267 private final float[] mMData = new float[3];
268 private final float[] mR = new float[16];
269 private int mHeading = -1;
271 /** True if all the parameters needed to start preview is ready. */
272 private boolean mCameraPreviewParamsReady = false;
274 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
275 new MediaSaver.OnMediaSavedListener() {
277 public void onMediaSaved(Uri uri) {
279 mActivity.notifyNewMedia(uri);
283 private boolean mShouldResizeTo16x9 = false;
285 private final Runnable mResumeTaskRunnable = new Runnable() {
293 * We keep the flash setting before entering scene modes (HDR)
294 * and restore it after HDR is off.
296 private String mFlashModeBeforeSceneMode;
299 * This callback gets called when user select whether or not to
300 * turn on geo-tagging.
302 public interface LocationDialogCallback {
304 * Gets called after user selected/unselected geo-tagging feature.
306 * @param selected whether or not geo-tagging feature is selected
308 public void onLocationTaggingSelected(boolean selected);
312 * This callback defines the text that is shown in the aspect ratio selection
313 * dialog, provides the current aspect ratio, and gets notified when user changes
314 * aspect ratio selection in the dialog.
316 public interface AspectRatioDialogCallback {
318 * Returns current aspect ratio that is being used to set as default.
320 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
323 * Gets notified when user has made the aspect ratio selection.
325 * @param newAspectRatio aspect ratio that user has selected
326 * @param dialogHandlingFinishedRunnable runnable to run when the operations
327 * needed to handle changes from dialog
330 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
331 Runnable dialogHandlingFinishedRunnable);
334 private void checkDisplayRotation() {
335 // Set the display orientation if display rotation has changed.
336 // Sometimes this happens when the device is held upside
337 // down and camera app is opened. Rotation animation will
338 // take some time and the rotation value we have got may be
339 // wrong. Framework does not have a callback for this now.
340 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
341 setDisplayOrientation();
343 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
344 mHandler.postDelayed(new Runnable() {
347 checkDisplayRotation();
354 * This Handler is used to post message back onto the main thread of the
357 private static class MainHandler extends Handler {
358 private final WeakReference<PhotoModule> mModule;
360 public MainHandler(PhotoModule module) {
361 super(Looper.getMainLooper());
362 mModule = new WeakReference<PhotoModule>(module);
366 public void handleMessage(Message msg) {
367 PhotoModule module = mModule.get();
368 if (module == null) {
372 case MSG_FIRST_TIME_INIT: {
373 module.initializeFirstTime();
377 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
378 module.setCameraParametersWhenIdle(0);
385 private void switchToGcamCapture() {
386 if (mActivity != null && mGcamModeIndex != 0) {
387 SettingsManager settingsManager = mActivity.getSettingsManager();
388 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS,
389 SettingsManager.VALUE_ON);
391 // Disable the HDR+ button to prevent callbacks from being
392 // queued before the correct callback is attached to the button
393 // in the new module. The new module will set the enabled/disabled
394 // of this button when the module's preferred camera becomes available.
395 ButtonManager buttonManager = mActivity.getButtonManager();
396 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
398 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
400 // Do not post this to avoid this module switch getting interleaved with
401 // other button callbacks.
402 mActivity.onModeSelected(mGcamModeIndex);
407 * Constructs a new photo module.
409 public PhotoModule(AppController app) {
411 mGcamModeIndex = app.getAndroidContext().getResources()
412 .getInteger(R.integer.camera_mode_gcam);
416 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
417 mActivity = activity;
418 // TODO: Need to look at the controller interface to see if we can get
419 // rid of passing in the activity directly.
420 mAppController = mActivity;
422 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
423 mActivity.setPreviewStatusListener(mUI);
425 SettingsManager settingsManager = mActivity.getSettingsManager();
426 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
428 // TODO: Move this to SettingsManager as a part of upgrade procedure.
429 if (!settingsManager.getBoolean(SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO)) {
430 // Switch to back camera to set aspect ratio.
431 mCameraId = Integer.parseInt(settingsManager
432 .getDefaultCameraIdSetting(activity).getDefault());
435 mContentResolver = mActivity.getContentResolver();
437 // Surface texture is from camera screen nail and startPreview needs it.
438 // This must be done before startPreview.
439 mIsImageCaptureIntent = isImageCaptureIntent();
441 mActivity.getCameraProvider().requestCamera(mCameraId);
443 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
444 mLocationManager = mActivity.getLocationManager();
445 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
446 mUI.setCountdownFinishedListener(this);
448 // TODO: Make this a part of app controller API.
449 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
450 cancelButton.setOnClickListener(new View.OnClickListener() {
452 public void onClick(View view) {
458 private void cancelCountDown() {
459 if (mUI.isCountingDown()) {
460 // Cancel on-going countdown.
461 mUI.cancelCountDown();
463 mAppController.getCameraAppUI().transitionToCapture();
464 mAppController.getCameraAppUI().showModeOptions();
468 public boolean isUsingBottomBar() {
472 private void initializeControlByIntent() {
473 if (mIsImageCaptureIntent) {
474 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
475 setupCaptureParams();
479 private void onPreviewStarted() {
480 mAppController.onPreviewStarted();
481 setCameraState(IDLE);
482 startFaceDetection();
487 * Prompt the user to pick to record location and choose aspect ratio for the
488 * very first run of camera only.
490 private void settingsFirstRun() {
491 final SettingsManager settingsManager = mActivity.getSettingsManager();
493 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
497 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION);
498 boolean aspectRatioPrompt = !settingsManager.getBoolean(
499 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO);
500 if (!locationPrompt && !aspectRatioPrompt) {
504 // Check if the back camera exists
505 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
506 if (backCameraId == -1) {
507 // If there is no back camera, do not show the prompt.
511 if (locationPrompt) {
512 // Show both location and aspect ratio selection dialog.
513 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
515 public void onLocationTaggingSelected(boolean selected) {
516 settingsManager.setLocation(selected, mActivity.getLocationManager());
518 }, createAspectRatioDialogCallback());
520 // App upgrade. Only show aspect ratio selection.
521 mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
525 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
526 Size currentSize = new Size(mParameters.getPictureSize());
527 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
528 if (aspectRatio < 1f) {
529 aspectRatio = 1 / aspectRatio;
531 final AspectRatioSelector.AspectRatio currentAspectRatio;
532 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
533 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
534 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
535 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
537 // TODO: Log error and not show dialog.
541 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
542 List<Size> pictureSizes = ResolutionUtil
543 .getDisplayableSizesFromSupported(sizes, true);
545 // This logic below finds the largest resolution for each aspect ratio.
546 // TODO: Move this somewhere that can be shared with SettingsActivity
547 int aspectRatio4x3Resolution = 0;
548 int aspectRatio16x9Resolution = 0;
549 Size largestSize4x3 = new Size(0, 0);
550 Size largestSize16x9 = new Size(0, 0);
551 for (Size size : pictureSizes) {
552 float pictureAspectRatio = (float) size.width() / (float) size.height();
553 pictureAspectRatio = pictureAspectRatio < 1 ?
554 1f / pictureAspectRatio : pictureAspectRatio;
555 int resolution = size.width() * size.height();
556 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
557 if (resolution > aspectRatio4x3Resolution) {
558 aspectRatio4x3Resolution = resolution;
559 largestSize4x3 = size;
561 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
562 if (resolution > aspectRatio16x9Resolution) {
563 aspectRatio16x9Resolution = resolution;
564 largestSize16x9 = size;
569 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
570 final Size size4x3ToSelect = largestSize4x3;
571 final Size size16x9ToSelect = largestSize16x9;
573 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
576 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
577 return currentAspectRatio;
581 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
582 Runnable dialogHandlingFinishedRunnable) {
583 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
584 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
585 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
587 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
588 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
589 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
590 largestSize16x9Text);
592 mActivity.getSettingsManager().setBoolean(
593 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO, true);
594 if (newAspectRatio != currentAspectRatio) {
597 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
599 mHandler.post(dialogHandlingFinishedRunnable);
607 public void onPreviewUIReady() {
612 public void onPreviewUIDestroyed() {
613 if (mCameraDevice == null) {
616 mCameraDevice.setPreviewTexture(null);
621 public void startPreCaptureAnimation() {
622 mAppController.startPreCaptureAnimation();
625 private void onCameraOpened() {
627 initializeControlByIntent();
630 private void switchCamera() {
636 mAppController.freezeScreenUntilPreviewReady();
637 SettingsManager settingsManager = mActivity.getSettingsManager();
639 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
641 mCameraId = mPendingSwitchCameraId;
642 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
643 mActivity.getCameraProvider().requestCamera(mCameraId);
645 if (mFocusManager != null) {
646 mFocusManager.removeMessages();
649 mMirror = isCameraFrontFacing();
650 mFocusManager.setMirror(mMirror);
651 // Start switch camera animation. Post a message because
652 // onFrameAvailable from the old camera may already exist.
655 private final ButtonManager.ButtonCallback mCameraCallback =
656 new ButtonManager.ButtonCallback() {
658 public void onStateChanged(int state) {
659 // At the time this callback is fired, the camera id
660 // has be set to the desired camera.
662 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
665 // If switching to back camera, and HDR+ is still on,
666 // switch back to gcam, otherwise handle callback normally.
667 SettingsManager settingsManager = mActivity.getSettingsManager();
668 if (settingsManager.isCameraBackFacing()) {
669 if (settingsManager.requestsReturnToHdrPlus()) {
670 switchToGcamCapture();
675 mPendingSwitchCameraId = state;
677 Log.d(TAG, "Start to switch camera. cameraId=" + state);
678 // We need to keep a preview frame for the animation before
679 // releasing the camera. This will trigger
680 // onPreviewTextureCopied.
681 // TODO: Need to animate the camera switch
686 private final ButtonManager.ButtonCallback mHdrPlusCallback =
687 new ButtonManager.ButtonCallback() {
689 public void onStateChanged(int state) {
690 SettingsManager settingsManager = mActivity.getSettingsManager();
691 if (GcamHelper.hasGcamCapture()) {
692 // Set the camera setting to default backfacing.
693 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
694 switchToGcamCapture();
696 if (settingsManager.isHdrOn()) {
697 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
698 CameraUtil.SCENE_MODE_HDR);
700 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
701 Parameters.SCENE_MODE_AUTO);
703 updateParametersSceneMode();
704 mCameraDevice.setParameters(mParameters);
710 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
712 public void onClick(View v) {
713 onCaptureCancelled();
717 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
719 public void onClick(View v) {
724 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
726 public void onClick(View v) {
727 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
733 public void hardResetSettings(SettingsManager settingsManager) {
734 // PhotoModule should hard reset HDR+ to off,
735 // and HDR to off if HDR+ is supported.
736 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, SettingsManager.VALUE_OFF);
737 if (GcamHelper.hasGcamCapture()) {
738 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR, SettingsManager.VALUE_OFF);
743 public HardwareSpec getHardwareSpec() {
744 return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
748 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
749 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
751 bottomBarSpec.enableCamera = true;
752 bottomBarSpec.cameraCallback = mCameraCallback;
753 bottomBarSpec.enableFlash = !SettingsManager.VALUE_ON
754 .equals(mAppController.getSettingsManager()
755 .get(SettingsManager.SETTING_CAMERA_HDR));
756 bottomBarSpec.enableHdr = true;
757 bottomBarSpec.hdrCallback = mHdrPlusCallback;
758 bottomBarSpec.enableGridLines = true;
759 if (mCameraCapabilities != null) {
760 bottomBarSpec.enableExposureCompensation = true;
761 bottomBarSpec.exposureCompensationSetCallback =
762 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
764 public void setExposure(int value) {
765 setExposureCompensation(value);
768 bottomBarSpec.minExposureCompensation =
769 mCameraCapabilities.getMinExposureCompensation();
770 bottomBarSpec.maxExposureCompensation =
771 mCameraCapabilities.getMaxExposureCompensation();
772 bottomBarSpec.exposureCompensationStep =
773 mCameraCapabilities.getExposureCompensationStep();
776 bottomBarSpec.enableSelfTimer = true;
777 bottomBarSpec.showSelfTimer = true;
779 if (isImageCaptureIntent()) {
780 bottomBarSpec.showCancel = true;
781 bottomBarSpec.cancelCallback = mCancelCallback;
782 bottomBarSpec.showDone = true;
783 bottomBarSpec.doneCallback = mDoneCallback;
784 bottomBarSpec.showRetake = true;
785 bottomBarSpec.retakeCallback = mRetakeCallback;
788 return bottomBarSpec;
791 // either open a new camera or switch cameras
792 private void openCameraCommon() {
793 mUI.onCameraOpened(mParameters);
794 if (mIsImageCaptureIntent) {
795 // Set hdr plus to default: off.
796 SettingsManager settingsManager = mActivity.getSettingsManager();
797 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
803 public void updatePreviewAspectRatio(float aspectRatio) {
804 mAppController.updatePreviewAspectRatio(aspectRatio);
807 private void resetExposureCompensation() {
808 SettingsManager settingsManager = mActivity.getSettingsManager();
809 if (settingsManager == null) {
810 Log.e(TAG, "Settings manager is null!");
813 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE);
816 // Snapshots can only be taken after this is called. It should be called
817 // once only. We could have done these things in onCreate() but we want to
818 // make preview screen appear as soon as possible.
819 private void initializeFirstTime() {
820 if (mFirstTimeInitialized || mPaused) {
824 mUI.initializeFirstTime();
826 // We set the listener only when both service and shutterbutton
828 getServices().getMemoryManager().addListener(this);
830 mNamedImages = new NamedImages();
832 mFirstTimeInitialized = true;
835 mActivity.updateStorageSpaceAndHint(null);
838 // If the activity is paused and resumed, this method will be called in
840 private void initializeSecondTime() {
841 getServices().getMemoryManager().addListener(this);
842 mNamedImages = new NamedImages();
843 mUI.initializeSecondTime(mParameters);
846 private void addIdleHandler() {
847 MessageQueue queue = Looper.myQueue();
848 queue.addIdleHandler(new MessageQueue.IdleHandler() {
850 public boolean queueIdle() {
851 Storage.ensureOSXCompatible();
858 public void startFaceDetection() {
859 if (mFaceDetectionStarted) {
862 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
863 mFaceDetectionStarted = true;
864 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
865 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
866 mCameraDevice.startFaceDetection();
867 SessionStatsCollector.instance().faceScanActive(true);
872 public void stopFaceDetection() {
873 if (!mFaceDetectionStarted) {
876 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
877 mFaceDetectionStarted = false;
878 mCameraDevice.setFaceDetectionCallback(null, null);
879 mCameraDevice.stopFaceDetection();
881 SessionStatsCollector.instance().faceScanActive(false);
885 private final class ShutterCallback
886 implements CameraShutterCallback {
888 private final boolean mNeedsAnimation;
890 public ShutterCallback(boolean needsAnimation) {
891 mNeedsAnimation = needsAnimation;
895 public void onShutter(CameraProxy camera) {
896 mShutterCallbackTime = System.currentTimeMillis();
897 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
898 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
899 if (mNeedsAnimation) {
900 mActivity.runOnUiThread(new Runnable() {
903 animateAfterShutter();
910 private final class PostViewPictureCallback
911 implements CameraPictureCallback {
913 public void onPictureTaken(byte[] data, CameraProxy camera) {
914 mPostViewPictureCallbackTime = System.currentTimeMillis();
915 Log.v(TAG, "mShutterToPostViewCallbackTime = "
916 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
921 private final class RawPictureCallback
922 implements CameraPictureCallback {
924 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
925 mRawPictureCallbackTime = System.currentTimeMillis();
926 Log.v(TAG, "mShutterToRawCallbackTime = "
927 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
931 private static class ResizeBundle {
933 float targetAspectRatio;
938 * @return Cropped image if the target aspect ratio is larger than the jpeg
939 * aspect ratio on the long axis. The original jpeg otherwise.
941 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
943 final byte[] jpegData = dataBundle.jpegData;
944 final ExifInterface exif = dataBundle.exif;
945 float targetAspectRatio = dataBundle.targetAspectRatio;
947 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
948 int originalWidth = original.getWidth();
949 int originalHeight = original.getHeight();
953 if (originalWidth > originalHeight) {
954 newHeight = (int) (originalWidth / targetAspectRatio);
955 newWidth = originalWidth;
957 newWidth = (int) (originalHeight / targetAspectRatio);
958 newHeight = originalHeight;
960 int xOffset = (originalWidth - newWidth)/2;
961 int yOffset = (originalHeight - newHeight)/2;
963 if (xOffset < 0 || yOffset < 0) {
967 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
968 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
969 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
971 ByteArrayOutputStream stream = new ByteArrayOutputStream();
973 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
974 dataBundle.jpegData = stream.toByteArray();
978 private final class JpegPictureCallback
979 implements CameraPictureCallback {
982 public JpegPictureCallback(Location loc) {
987 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
988 mAppController.setShutterEnabled(true);
992 if (mIsImageCaptureIntent) {
995 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
996 mUI.setSwipingEnabled(true);
999 mJpegPictureCallbackTime = System.currentTimeMillis();
1000 // If postview callback has arrived, the captured image is displayed
1001 // in postview callback. If not, the captured image is displayed in
1002 // raw picture callback.
1003 if (mPostViewPictureCallbackTime != 0) {
1004 mShutterToPictureDisplayedTime =
1005 mPostViewPictureCallbackTime - mShutterCallbackTime;
1006 mPictureDisplayedToJpegCallbackTime =
1007 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
1009 mShutterToPictureDisplayedTime =
1010 mRawPictureCallbackTime - mShutterCallbackTime;
1011 mPictureDisplayedToJpegCallbackTime =
1012 mJpegPictureCallbackTime - mRawPictureCallbackTime;
1014 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
1015 + mPictureDisplayedToJpegCallbackTime + "ms");
1017 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
1018 if (!mIsImageCaptureIntent) {
1022 long now = System.currentTimeMillis();
1023 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1024 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1025 mJpegPictureCallbackTime = 0;
1027 final ExifInterface exif = Exif.getExif(originalJpegData);
1029 if (mShouldResizeTo16x9) {
1030 final ResizeBundle dataBundle = new ResizeBundle();
1031 dataBundle.jpegData = originalJpegData;
1032 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1033 dataBundle.exif = exif;
1034 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1037 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1038 return cropJpegDataToAspectRatio(resizeBundles[0]);
1042 protected void onPostExecute(ResizeBundle result) {
1043 saveFinalPhoto(result.jpegData, result.exif, camera);
1045 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1048 saveFinalPhoto(originalJpegData, exif, camera);
1052 void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1054 int orientation = Exif.getOrientation(exif);
1056 float zoomValue = 0f;
1057 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
1058 int zoomIndex = mParameters.getZoom();
1059 List<Integer> zoomRatios = mParameters.getZoomRatios();
1060 if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
1061 zoomValue = 0.01f * zoomRatios.get(zoomIndex);
1065 boolean hdrOn = CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
1066 String flashSetting =
1067 mActivity.getSettingsManager().get(SettingsManager.SETTING_FLASH_MODE);
1068 boolean gridLinesOn = mActivity.getSettingsManager().areGridLinesOn();
1069 UsageStatistics.instance().photoCaptureDoneEvent(
1070 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1071 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
1072 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
1073 (float) mTimerDuration, mShutterTouchCoordinate, mVolumeButtonClickedFlag);
1074 mShutterTouchCoordinate = null;
1075 mVolumeButtonClickedFlag = false;
1077 if (!mIsImageCaptureIntent) {
1078 // Calculate the width and the height of the jpeg.
1079 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1080 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
1082 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1084 height = exifHeight;
1087 s = new Size(mParameters.getPictureSize());
1088 if ((mJpegRotation + orientation) % 180 == 0) {
1090 height = s.height();
1096 NamedEntity name = mNamedImages.getNextNameEntity();
1097 String title = (name == null) ? null : name.title;
1098 long date = (name == null) ? -1 : name.date;
1100 // Handle debug mode outputs
1101 if (mDebugUri != null) {
1102 // If using a debug uri, save jpeg there.
1103 saveToDebugUri(jpegData);
1105 // Adjust the title of the debug image shown in mediastore.
1106 if (title != null) {
1107 title = DEBUG_IMAGE_PREFIX + title;
1111 if (title == null) {
1112 Log.e(TAG, "Unbalanced name/data pair");
1115 date = mCaptureStartTime;
1117 if (mHeading >= 0) {
1118 // heading direction has been updated by the sensor.
1119 ExifTag directionRefTag = exif.buildTag(
1120 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1121 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1122 ExifTag directionTag = exif.buildTag(
1123 ExifInterface.TAG_GPS_IMG_DIRECTION,
1124 new Rational(mHeading, 1));
1125 exif.setTag(directionRefTag);
1126 exif.setTag(directionTag);
1128 getServices().getMediaSaver().addImage(
1129 jpegData, title, date, mLocation, width, height,
1130 orientation, exif, mOnMediaSavedListener, mContentResolver);
1132 // Animate capture with real jpeg data instead of a preview
1134 mUI.animateCapture(jpegData, orientation, mMirror);
1136 mJpegImageData = jpegData;
1137 if (!mQuickCapture) {
1138 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
1144 // Send the taken photo to remote shutter listeners, if any are
1146 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
1149 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
1153 // Check this in advance of each shot so we don't add to shutter
1154 // latency. It's true that someone else could write to the SD card
1155 // in the mean time and fill it, but that could have happened
1156 // between the shutter press and saving the JPEG too.
1157 mActivity.updateStorageSpaceAndHint(null);
1161 private final class AutoFocusCallback implements CameraAFCallback {
1163 public void onAutoFocus(boolean focused, CameraProxy camera) {
1164 SessionStatsCollector.instance().autofocusResult(focused);
1169 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
1170 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
1171 setCameraState(IDLE);
1172 mFocusManager.onAutoFocus(focused, false);
1176 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1177 private final class AutoFocusMoveCallback
1178 implements CameraAFMoveCallback {
1180 public void onAutoFocusMoving(
1181 boolean moving, CameraProxy camera) {
1182 mFocusManager.onAutoFocusMoving(moving);
1183 SessionStatsCollector.instance().autofocusMoving(moving);
1188 * This class is just a thread-safe queue for name,date holder objects.
1190 public static class NamedImages {
1191 private final Vector<NamedEntity> mQueue;
1193 public NamedImages() {
1194 mQueue = new Vector<NamedEntity>();
1197 public void nameNewImage(long date) {
1198 NamedEntity r = new NamedEntity();
1199 r.title = CameraUtil.createJpegName(date);
1204 public NamedEntity getNextNameEntity() {
1205 synchronized (mQueue) {
1206 if (!mQueue.isEmpty()) {
1207 return mQueue.remove(0);
1213 public static class NamedEntity {
1214 public String title;
1219 private void setCameraState(int state) {
1220 mCameraState = state;
1222 case PREVIEW_STOPPED:
1223 case SNAPSHOT_IN_PROGRESS:
1224 case SWITCHING_CAMERA:
1225 // TODO: Tell app UI to disable swipe
1227 case PhotoController.IDLE:
1228 // TODO: Tell app UI to enable swipe
1233 private void animateAfterShutter() {
1234 // Only animate when in full screen capture mode
1235 // i.e. If monkey/a user swipes to the gallery during picture taking,
1236 // don't show animation
1237 if (!mIsImageCaptureIntent) {
1243 public boolean capture() {
1244 // If we are already in the middle of taking a snapshot or the image
1245 // save request is full then ignore.
1246 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
1247 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
1250 mCaptureStartTime = System.currentTimeMillis();
1252 mPostViewPictureCallbackTime = 0;
1253 mJpegImageData = null;
1255 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
1257 if (animateBefore) {
1258 animateAfterShutter();
1261 // Set rotation and gps data.
1264 // We need to be consistent with the framework orientation (i.e. the
1265 // orientation of the UI.) when the auto-rotate screen setting is on.
1266 if (mActivity.isAutoRotateScreen()) {
1267 orientation = (360 - mDisplayRotation) % 360;
1269 orientation = mOrientation;
1271 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
1272 mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
1273 mParameters.setRotation(mJpegRotation);
1274 Location loc = mActivity.getLocationManager().getCurrentLocation();
1275 CameraUtil.setGpsParameters(mParameters, loc);
1276 mCameraDevice.setParameters(mParameters);
1278 // We don't want user to press the button again while taking a
1279 // multi-second HDR photo.
1280 mAppController.setShutterEnabled(false);
1281 mCameraDevice.takePicture(mHandler,
1282 new ShutterCallback(!animateBefore),
1283 mRawPictureCallback, mPostViewPictureCallback,
1284 new JpegPictureCallback(loc));
1286 mNamedImages.nameNewImage(mCaptureStartTime);
1288 mFaceDetectionStarted = false;
1289 setCameraState(SNAPSHOT_IN_PROGRESS);
1294 public void setFocusParameters() {
1295 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1298 private void updateSceneMode() {
1299 // If scene mode is set, we cannot set flash mode, white balance, and
1300 // focus mode, instead, we read it from driver
1301 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1302 overrideCameraSettings(mParameters.getFlashMode(), mParameters.getFocusMode());
1306 private void overrideCameraSettings(final String flashMode, final String focusMode) {
1307 SettingsManager settingsManager = mActivity.getSettingsManager();
1308 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode);
1309 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode);
1313 public void onOrientationChanged(int orientation) {
1314 // We keep the last known orientation. So if the user first orient
1315 // the camera then point the camera to floor or sky, we still have
1316 // the correct orientation.
1317 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1320 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
1324 public void onCameraAvailable(CameraProxy cameraProxy) {
1328 mCameraDevice = cameraProxy;
1330 initializeCapabilities();
1332 // Reset zoom value index.
1334 if (mFocusManager == null) {
1335 initializeFocusManager();
1337 mFocusManager.setParameters(mInitialParams, mCameraCapabilities);
1339 // Do camera parameter dependent initialization.
1340 mParameters = mCameraDevice.getParameters();
1341 setCameraParameters(UPDATE_PARAM_ALL);
1342 // Set a listener which updates camera parameters based
1343 // on changed settings.
1344 SettingsManager settingsManager = mActivity.getSettingsManager();
1345 settingsManager.addListener(this);
1346 mCameraPreviewParamsReady = true;
1354 public void onCaptureCancelled() {
1355 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1360 public void onCaptureRetake() {
1364 mUI.hidePostCaptureAlert();
1365 mUI.hideIntentReviewImageView();
1370 public void onCaptureDone() {
1375 byte[] data = mJpegImageData;
1377 if (mCropValue == null) {
1378 // First handle the no crop case -- just return the value. If the
1379 // caller specifies a "save uri" then write the data to its
1380 // stream. Otherwise, pass back a scaled down version of the bitmap
1381 // directly in the extras.
1382 if (mSaveUri != null) {
1383 OutputStream outputStream = null;
1385 outputStream = mContentResolver.openOutputStream(mSaveUri);
1386 outputStream.write(data);
1387 outputStream.close();
1389 mActivity.setResultEx(Activity.RESULT_OK);
1391 } catch (IOException ex) {
1394 CameraUtil.closeSilently(outputStream);
1397 ExifInterface exif = Exif.getExif(data);
1398 int orientation = Exif.getOrientation(exif);
1399 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1400 bitmap = CameraUtil.rotate(bitmap, orientation);
1401 mActivity.setResultEx(Activity.RESULT_OK,
1402 new Intent("inline-data").putExtra("data", bitmap));
1406 // Save the image to a temp file and invoke the cropper
1408 FileOutputStream tempStream = null;
1410 File path = mActivity.getFileStreamPath(sTempCropFilename);
1412 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1413 tempStream.write(data);
1415 tempUri = Uri.fromFile(path);
1416 } catch (FileNotFoundException ex) {
1417 mActivity.setResultEx(Activity.RESULT_CANCELED);
1420 } catch (IOException ex) {
1421 mActivity.setResultEx(Activity.RESULT_CANCELED);
1425 CameraUtil.closeSilently(tempStream);
1428 Bundle newExtras = new Bundle();
1429 if (mCropValue.equals("circle")) {
1430 newExtras.putString("circleCrop", "true");
1432 if (mSaveUri != null) {
1433 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1435 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
1437 if (mActivity.isSecureCamera()) {
1438 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
1441 // TODO: Share this constant.
1442 final String CROP_ACTION = "com.android.camera.action.CROP";
1443 Intent cropIntent = new Intent(CROP_ACTION);
1445 cropIntent.setData(tempUri);
1446 cropIntent.putExtras(newExtras);
1448 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1453 public void onShutterCoordinate(TouchCoordinate coord) {
1454 mShutterTouchCoordinate = coord;
1458 public void onShutterButtonFocus(boolean pressed) {
1459 // Do nothing. We don't support half-press to focus anymore.
1463 public void onShutterButtonClick() {
1464 if (mPaused || (mCameraState == SWITCHING_CAMERA)
1465 || (mCameraState == PREVIEW_STOPPED)) {
1466 mVolumeButtonClickedFlag = false;
1470 // Do not take the picture if there is not enough storage.
1471 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
1472 Log.i(TAG, "Not enough space or storage not ready. remaining="
1473 + mActivity.getStorageSpaceBytes());
1474 mVolumeButtonClickedFlag = false;
1477 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
1478 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
1480 int countDownDuration = Integer.parseInt(mActivity.getSettingsManager()
1481 .get(SettingsManager.SETTING_COUNTDOWN_DURATION));
1482 mTimerDuration = countDownDuration;
1483 if (countDownDuration > 0) {
1484 // Start count down.
1485 mAppController.getCameraAppUI().transitionToCancel();
1486 mAppController.getCameraAppUI().hideModeOptions();
1487 mUI.startCountdown(countDownDuration);
1494 private void focusAndCapture() {
1495 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
1496 mUI.setSwipingEnabled(false);
1498 // If the user wants to do a snapshot while the previous one is still
1499 // in progress, remember the fact and do it after we finish the previous
1500 // one and re-start the preview. Snapshot in progress also includes the
1501 // state that autofocus is focusing and a picture will be taken when
1502 // focus callback arrives.
1503 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1504 if (!mIsImageCaptureIntent) {
1505 mSnapshotOnIdle = true;
1510 mSnapshotOnIdle = false;
1511 mFocusManager.focusAndCapture();
1515 public void onRemainingSecondsChanged(int remainingSeconds) {
1516 mCountdownSoundPlayer.onRemainingSecondsChanged(remainingSeconds);
1520 public void onCountDownFinished() {
1521 if (mIsImageCaptureIntent) {
1522 mAppController.getCameraAppUI().transitionToIntentReviewLayout();
1524 mAppController.getCameraAppUI().transitionToCapture();
1526 mAppController.getCameraAppUI().showModeOptions();
1533 private void onResumeTasks() {
1537 Log.v(TAG, "Executing onResumeTasks.");
1538 CameraProvider camProvider = mActivity.getCameraProvider();
1539 if (camProvider == null) {
1540 // No camera provider, the Activity is destroyed already.
1543 camProvider.requestCamera(mCameraId);
1545 mJpegPictureCallbackTime = 0;
1548 mOnResumeTime = SystemClock.uptimeMillis();
1549 checkDisplayRotation();
1551 // If first time initialization is not finished, put it in the
1553 if (!mFirstTimeInitialized) {
1554 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
1556 initializeSecondTime();
1559 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1560 if (gsensor != null) {
1561 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1564 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1565 if (msensor != null) {
1566 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1571 * @return Whether the currently active camera is front-facing.
1573 private boolean isCameraFrontFacing() {
1574 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1575 return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1579 * The focus manager is the first UI related element to get initialized, and
1580 * it requires the RenderOverlay, so initialize it here
1582 private void initializeFocusManager() {
1583 // Create FocusManager object. startPreview needs it.
1584 // if mFocusManager not null, reuse it
1585 // otherwise create a new instance
1586 if (mFocusManager != null) {
1587 mFocusManager.removeMessages();
1589 mMirror = isCameraFrontFacing();
1590 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1591 R.array.pref_camera_focusmode_default_array);
1592 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1594 mInitialParams, this, mMirror,
1595 mActivity.getMainLooper(), mUI.getFocusUI());
1596 MotionManager motionManager = getServices().getMotionManager();
1597 if (motionManager != null) {
1598 motionManager.addListener(mFocusManager);
1601 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1605 public void resume() {
1607 mCountdownSoundPlayer.loadSounds();
1608 if (mFocusManager != null) {
1609 // If camera is not open when resume is called, focus manager will
1611 // be initialized yet, in which case it will start listening to
1612 // preview area size change later in the initialization.
1613 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1615 mAppController.addPreviewAreaSizeChangedListener(mUI);
1617 // Add delay on resume from lock screen only, in order to to speed up
1618 // the onResume --> onPause --> onResume cycle from lock screen.
1619 // Don't do always because letting go of thread can cause delay.
1620 String action = mActivity.getIntent().getAction();
1621 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1622 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1623 Log.v(TAG, "On resume, from lock screen.");
1624 // Note: onPauseAfterSuper() will delete this runnable, so we will
1625 // at most have 1 copy queued up.
1626 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
1628 Log.v(TAG, "On resume.");
1631 getServices().getRemoteShutterListener().onModuleReady(this);
1632 SessionStatsCollector.instance().sessionActive(true);
1636 public void pause() {
1638 mHandler.removeCallbacks(mResumeTaskRunnable);
1639 getServices().getRemoteShutterListener().onModuleExit();
1640 SessionStatsCollector.instance().sessionActive(false);
1642 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1643 if (gsensor != null) {
1644 mSensorManager.unregisterListener(this, gsensor);
1647 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1648 if (msensor != null) {
1649 mSensorManager.unregisterListener(this, msensor);
1652 // Reset the focus first. Camera CTS does not guarantee that
1653 // cancelAutoFocus is allowed after preview stops.
1654 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1655 mCameraDevice.cancelAutoFocus();
1658 // If the camera has not been opened asynchronously yet,
1659 // and startPreview hasn't been called, then this is a no-op.
1660 // (e.g. onResume -> onPause -> onResume).
1663 mCountdownSoundPlayer.release();
1665 mNamedImages = null;
1666 // If we are in an image capture intent and has taken
1667 // a picture, we just clear it in onPause.
1668 mJpegImageData = null;
1670 // Remove the messages and runnables in the queue.
1671 mHandler.removeCallbacksAndMessages(null);
1674 mActivity.enableKeepScreenOn(false);
1677 mPendingSwitchCameraId = -1;
1678 if (mFocusManager != null) {
1679 mFocusManager.removeMessages();
1681 getServices().getMemoryManager().removeListener(this);
1682 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
1683 mAppController.removePreviewAreaSizeChangedListener(mUI);
1685 SettingsManager settingsManager = mActivity.getSettingsManager();
1686 settingsManager.removeListener(this);
1690 public void destroy() {
1691 // TODO: implement this.
1695 public void onLayoutOrientationChanged(boolean isLandscape) {
1696 setDisplayOrientation();
1700 public void updateCameraOrientation() {
1701 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
1702 setDisplayOrientation();
1706 private boolean canTakePicture() {
1707 return isCameraIdle()
1708 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
1712 public void autoFocus() {
1713 Log.v(TAG,"Starting auto focus");
1714 mFocusStartTime = System.currentTimeMillis();
1715 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
1716 SessionStatsCollector.instance().autofocusManualTrigger();
1717 setCameraState(FOCUSING);
1721 public void cancelAutoFocus() {
1722 mCameraDevice.cancelAutoFocus();
1723 setCameraState(IDLE);
1724 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1728 public void onSingleTapUp(View view, int x, int y) {
1729 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1730 || mCameraState == SNAPSHOT_IN_PROGRESS
1731 || mCameraState == SWITCHING_CAMERA
1732 || mCameraState == PREVIEW_STOPPED) {
1736 // Check if metering area or focus area is supported.
1737 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1740 mFocusManager.onSingleTapUp(x, y);
1744 public boolean onBackPressed() {
1745 return mUI.onBackPressed();
1749 public boolean onKeyDown(int keyCode, KeyEvent event) {
1751 case KeyEvent.KEYCODE_VOLUME_UP:
1752 case KeyEvent.KEYCODE_VOLUME_DOWN:
1753 case KeyEvent.KEYCODE_FOCUS:
1754 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1755 !mActivity.getCameraAppUI().isInIntentReview()) {
1756 if (event.getRepeatCount() == 0) {
1757 onShutterButtonFocus(true);
1762 case KeyEvent.KEYCODE_CAMERA:
1763 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1764 onShutterButtonClick();
1767 case KeyEvent.KEYCODE_DPAD_CENTER:
1768 // If we get a dpad center event without any focused view, move
1769 // the focus to the shutter button and press it.
1770 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1771 // Start auto-focus immediately to reduce shutter lag. After
1772 // the shutter button gets the focus, onShutterButtonFocus()
1773 // will be called again but it is fine.
1774 onShutterButtonFocus(true);
1782 public boolean onKeyUp(int keyCode, KeyEvent event) {
1784 case KeyEvent.KEYCODE_VOLUME_UP:
1785 case KeyEvent.KEYCODE_VOLUME_DOWN:
1786 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1787 !mActivity.getCameraAppUI().isInIntentReview()) {
1788 if (mUI.isCountingDown()) {
1791 mVolumeButtonClickedFlag = true;
1792 onShutterButtonClick();
1797 case KeyEvent.KEYCODE_FOCUS:
1798 if (mFirstTimeInitialized) {
1799 onShutterButtonFocus(false);
1806 private void closeCamera() {
1807 if (mCameraDevice != null) {
1808 stopFaceDetection();
1809 mCameraDevice.setZoomChangeListener(null);
1810 mCameraDevice.setFaceDetectionCallback(null, null);
1811 mCameraDevice.setErrorCallback(null, null);
1813 mFaceDetectionStarted = false;
1814 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
1815 mCameraDevice = null;
1816 setCameraState(PREVIEW_STOPPED);
1817 mFocusManager.onCameraReleased();
1821 private void setDisplayOrientation() {
1822 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1823 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
1824 mCameraDisplayOrientation = mDisplayOrientation;
1825 mUI.setDisplayOrientation(mDisplayOrientation);
1826 if (mFocusManager != null) {
1827 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1829 // Change the camera display orientation
1830 if (mCameraDevice != null) {
1831 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1835 /** Only called by UI thread. */
1836 private void setupPreview() {
1837 mFocusManager.resetTouchFocus();
1842 * Returns whether we can/should start the preview or not.
1844 private boolean checkPreviewPreconditions() {
1849 if (mCameraDevice == null) {
1850 Log.w(TAG, "startPreview: camera device not ready yet.");
1854 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
1856 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
1860 if (!mCameraPreviewParamsReady) {
1861 Log.w(TAG, "startPreview: parameters for preview is not ready.");
1868 * The start/stop preview should only run on the UI thread.
1870 private void startPreview() {
1871 if (!checkPreviewPreconditions()) {
1875 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
1876 setDisplayOrientation();
1878 if (!mSnapshotOnIdle) {
1879 // If the focus mode is continuous autofocus, call cancelAutoFocus
1880 // to resume it because it may have been paused by autoFocus call.
1881 String focusMode = mFocusManager.getFocusMode();
1882 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
1883 mCameraDevice.cancelAutoFocus();
1885 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1887 setCameraParameters(UPDATE_PARAM_ALL);
1889 // Workaround for KitKat and KitKat MR1 which leave configured preview
1890 // callback streams lingering around when they should have been removed.
1891 // These preview callback streams are the cause for distorted preview.
1892 // For more details, see b/12210027
1893 if (mShouldSetPreviewCallbacks) {
1894 mShouldSetPreviewCallbacks = false;
1895 Size previewSize = new Size(mCameraDevice.getParameters().getPreviewSize());
1896 mCameraDevice.setPreviewDataCallbackWithBuffer(mHandler,
1897 new CameraManager.CameraPreviewDataCallback() {
1899 public void onPreviewFrame(byte[] data, CameraProxy camera) {
1900 // Remove callback after the first frame comes in.
1901 mCameraDevice.setPreviewDataCallbackWithBuffer(null, null);
1904 mCameraDevice.addCallbackBuffer(new byte[previewSize.width() * previewSize.height()]);
1906 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
1908 Log.i(TAG, "startPreview");
1909 mCameraDevice.startPreview();
1911 mFocusManager.onPreviewStarted();
1913 SessionStatsCollector.instance().previewActive(true);
1914 if (mSnapshotOnIdle) {
1915 mHandler.post(mDoSnapRunnable);
1920 public void stopPreview() {
1921 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1922 Log.i(TAG, "stopPreview");
1923 mCameraDevice.stopPreview();
1924 mFaceDetectionStarted = false;
1926 setCameraState(PREVIEW_STOPPED);
1927 if (mFocusManager != null) {
1928 mFocusManager.onPreviewStopped();
1930 SessionStatsCollector.instance().previewActive(false);
1934 public void onSettingChanged(SettingsManager settingsManager, int id) {
1936 case SettingsManager.SETTING_FLASH_MODE: {
1937 updateParametersFlashMode();
1940 case SettingsManager.SETTING_CAMERA_HDR: {
1941 String val = settingsManager.get(SettingsManager.SETTING_CAMERA_HDR);
1942 if (SettingsManager.VALUE_ON.equals(val)) {
1944 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
1945 mFlashModeBeforeSceneMode = settingsManager.get(SettingsManager
1946 .SETTING_FLASH_MODE);
1948 if (mFlashModeBeforeSceneMode != null) {
1949 settingsManager.set(SettingsManager.SETTING_FLASH_MODE,
1950 mFlashModeBeforeSceneMode);
1951 updateParametersFlashMode();
1952 mFlashModeBeforeSceneMode = null;
1954 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
1963 if (mCameraDevice != null) {
1964 mCameraDevice.setParameters(mParameters);
1968 private void updateCameraParametersInitialize() {
1969 // Reset preview frame rate to the maximum because it may be lowered by
1970 // video camera application.
1971 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
1972 if (fpsRange != null && fpsRange.length > 0) {
1973 mParameters.setPreviewFpsRange(
1974 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1975 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
1978 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
1980 // Disable video stabilization. Convenience methods not available in API
1982 String vstabSupported = mParameters.get("video-stabilization-supported");
1983 if ("true".equals(vstabSupported)) {
1984 mParameters.set("video-stabilization", "false");
1988 private void updateCameraParametersZoom() {
1990 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
1991 mParameters.setZoom(mZoomValue);
1995 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1996 private void setAutoExposureLockIfSupported() {
1997 if (mAeLockSupported) {
1998 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
2002 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
2003 private void setAutoWhiteBalanceLockIfSupported() {
2004 if (mAwbLockSupported) {
2005 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
2009 private void setFocusAreasIfSupported() {
2010 if (mFocusAreaSupported) {
2011 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
2015 private void setMeteringAreasIfSupported() {
2016 if (mMeteringAreaSupported) {
2017 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
2021 private void updateCameraParametersPreference() {
2022 setAutoExposureLockIfSupported();
2023 setAutoWhiteBalanceLockIfSupported();
2024 setFocusAreasIfSupported();
2025 setMeteringAreasIfSupported();
2027 // Initialize focus mode.
2028 mFocusManager.overrideFocusMode(null);
2029 mParameters.setFocusMode(mFocusManager.getFocusMode());
2030 SessionStatsCollector.instance().autofocusActive(
2031 mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
2033 // Set picture size.
2034 updateParametersPictureSize();
2036 // Set JPEG quality.
2037 updateParametersPictureQuality();
2039 // For the following settings, we need to check if the settings are
2040 // still supported by latest driver, if not, ignore the settings.
2042 // Set exposure compensation
2043 updateParametersExposureCompensation();
2045 // Set the scene mode: also sets flash and white balance.
2046 updateParametersSceneMode();
2048 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2049 updateAutoFocusMoveCallback();
2053 private void updateParametersPictureSize() {
2054 SettingsManager settingsManager = mActivity.getSettingsManager();
2055 String pictureSize = settingsManager
2056 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
2057 : SettingsManager.SETTING_PICTURE_SIZE_BACK);
2059 List<Size> supported = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
2060 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2061 mCameraDevice.getCameraId(), supported);
2062 SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
2063 mCameraDevice.getCameraId());
2065 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2066 mCameraDevice.getCameraId());
2067 if (ApiHelper.IS_NEXUS_5) {
2068 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2069 mShouldResizeTo16x9 = true;
2071 mShouldResizeTo16x9 = false;
2075 // Set a preview size that is closest to the viewfinder height and has
2076 // the right aspect ratio.
2077 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPreviewSizes());
2078 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
2079 (double) size.width() / size.height());
2080 Size original = new Size(mParameters.getPreviewSize());
2081 if (!original.equals(optimalSize)) {
2082 if (ApiHelper.SHOULD_HARD_RESET_PREVIEW_CALLBACK) {
2083 // Compare the aspect ratio.
2084 if ((original.width() * optimalSize.height())
2085 != (original.height() * optimalSize.width())) {
2086 // If aspect ratio has changed, set preview callback again, so
2087 // that the old preview callback stream will be forced to update.
2088 // This is a workaround for b/12210027, which was fixed in Kitkat MR2.
2089 mShouldSetPreviewCallbacks = true;
2092 mParameters.setPreviewSize(optimalSize.width(), optimalSize.height());
2094 // Zoom related settings will be changed for different preview
2095 // sizes, so set and read the parameters to get latest values
2096 if (mHandler.getLooper() == Looper.myLooper()) {
2097 // On UI thread only, not when camera starts up
2100 mCameraDevice.setParameters(mParameters);
2102 mParameters = mCameraDevice.getParameters();
2105 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
2106 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2107 / (float) optimalSize.height());
2109 Log.i(TAG, "Preview size is " + optimalSize);
2112 private void updateParametersPictureQuality() {
2113 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2114 CameraProfile.QUALITY_HIGH);
2115 mParameters.setJpegQuality(jpegQuality);
2118 private void updateParametersExposureCompensation() {
2119 SettingsManager settingsManager = mActivity.getSettingsManager();
2120 if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) {
2121 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE));
2122 int max = mCameraCapabilities.getMaxExposureCompensation();
2123 int min = mCameraCapabilities.getMinExposureCompensation();
2124 if (value >= min && value <= max) {
2125 mParameters.setExposureCompensation(value);
2127 Log.w(TAG, "invalid exposure range: " + value);
2130 // If exposure compensation is not enabled, reset the exposure compensation value.
2131 setExposureCompensation(0);
2136 private void updateParametersSceneMode() {
2137 SettingsManager settingsManager = mActivity.getSettingsManager();
2139 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
2140 if (mCameraCapabilities
2141 .supports(mCameraCapabilities.getStringifier().sceneModeFromString(mSceneMode))) {
2142 if (!mParameters.getSceneMode().equals(mSceneMode)) {
2143 mParameters.setSceneMode(mSceneMode);
2145 // Setting scene mode will change the settings of flash mode,
2146 // white balance, and focus mode. Here we read back the
2147 // parameters, so we can know those settings.
2148 mCameraDevice.setParameters(mParameters);
2149 mParameters = mCameraDevice.getParameters();
2152 mSceneMode = mParameters.getSceneMode();
2153 if (mSceneMode == null) {
2154 mSceneMode = Parameters.SCENE_MODE_AUTO;
2158 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
2160 updateParametersFlashMode();
2163 mFocusManager.overrideFocusMode(null);
2164 mParameters.setFocusMode(mFocusManager.getFocusMode());
2166 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
2170 private void updateParametersFlashMode() {
2171 SettingsManager settingsManager = mActivity.getSettingsManager();
2173 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
2174 if (mCameraCapabilities
2175 .supports(mCameraCapabilities.getStringifier().flashModeFromString(flashMode))) {
2176 mParameters.setFlashMode(flashMode);
2180 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
2181 private void updateAutoFocusMoveCallback() {
2182 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
2183 mCameraDevice.setAutoFocusMoveCallback(mHandler,
2184 (CameraAFMoveCallback) mAutoFocusMoveCallback);
2186 mCameraDevice.setAutoFocusMoveCallback(null, null);
2191 * Sets the exposure compensation to the given value and also updates settings.
2193 * @param value exposure compensation value to be set
2195 public void setExposureCompensation(int value) {
2196 int max = mCameraCapabilities.getMaxExposureCompensation();
2197 int min = mCameraCapabilities.getMinExposureCompensation();
2198 if (value >= min && value <= max) {
2199 mParameters.setExposureCompensation(value);
2200 SettingsManager settingsManager = mActivity.getSettingsManager();
2201 settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value));
2203 Log.w(TAG, "invalid exposure range: " + value);
2207 // We separate the parameters into several subsets, so we can update only
2208 // the subsets actually need updating. The PREFERENCE set needs extra
2209 // locking because the preference can be changed from GLThread as well.
2210 private void setCameraParameters(int updateSet) {
2211 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2212 updateCameraParametersInitialize();
2215 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2216 updateCameraParametersZoom();
2219 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
2220 updateCameraParametersPreference();
2223 mCameraDevice.setParameters(mParameters);
2226 // If the Camera is idle, update the parameters immediately, otherwise
2227 // accumulate them in mUpdateSet and update later.
2228 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2229 mUpdateSet |= additionalUpdateSet;
2230 if (mCameraDevice == null) {
2231 // We will update all the parameters when we open the device, so
2232 // we don't need to do anything now.
2235 } else if (isCameraIdle()) {
2236 setCameraParameters(mUpdateSet);
2240 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2241 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
2247 public boolean isCameraIdle() {
2248 return (mCameraState == IDLE) ||
2249 (mCameraState == PREVIEW_STOPPED) ||
2250 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
2251 && (mCameraState != SWITCHING_CAMERA));
2255 public boolean isImageCaptureIntent() {
2256 String action = mActivity.getIntent().getAction();
2257 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
2258 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
2261 private void setupCaptureParams() {
2262 Bundle myExtras = mActivity.getIntent().getExtras();
2263 if (myExtras != null) {
2264 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2265 mCropValue = myExtras.getString("crop");
2269 private void initializeCapabilities() {
2270 mInitialParams = mCameraDevice.getParameters();
2271 mCameraCapabilities = mCameraDevice.getCapabilities();
2272 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2273 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2274 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2275 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
2276 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
2277 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
2280 // TODO: Remove this
2282 public int onZoomChanged(int index) {
2283 // Not useful to change zoom value when the activity is paused.
2288 if (mParameters == null || mCameraDevice == null) {
2291 // Set zoom parameters asynchronously
2292 mParameters.setZoom(mZoomValue);
2293 mCameraDevice.setParameters(mParameters);
2294 Parameters p = mCameraDevice.getParameters();
2302 public int getCameraState() {
2303 return mCameraState;
2307 public void onMemoryStateChanged(int state) {
2308 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
2312 public void onLowMemory() {
2313 // Not much we can do in the photo module.
2317 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2321 public void onSensorChanged(SensorEvent event) {
2322 int type = event.sensor.getType();
2324 if (type == Sensor.TYPE_ACCELEROMETER) {
2326 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2329 // we should not be here.
2332 for (int i = 0; i < 3; i++) {
2333 data[i] = event.values[i];
2335 float[] orientation = new float[3];
2336 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2337 SensorManager.getOrientation(mR, orientation);
2338 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2344 // For debugging only.
2345 public void setDebugUri(Uri uri) {
2349 // For debugging only.
2350 private void saveToDebugUri(byte[] data) {
2351 if (mDebugUri != null) {
2352 OutputStream outputStream = null;
2354 outputStream = mContentResolver.openOutputStream(mDebugUri);
2355 outputStream.write(data);
2356 outputStream.close();
2357 } catch (IOException e) {
2358 Log.e(TAG, "Exception while writing debug jpeg file", e);
2360 CameraUtil.closeSilently(outputStream);
2366 public void onRemoteShutterPress() {
2367 mHandler.post(new Runnable() {
2376 * This class manages the loading/releasing/playing of the sounds needed for
2379 private class CountdownSoundPlayer {
2380 private SoundPool mSoundPool;
2381 private int mBeepOnce;
2382 private int mBeepTwice;
2386 if (mSoundPool == null) {
2387 mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
2388 mBeepOnce = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_once, 1);
2389 mBeepTwice = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_twice, 1);
2393 void onRemainingSecondsChanged(int newVal) {
2394 if (mSoundPool == null) {
2395 Log.e(TAG, "Cannot play sound - they have not been loaded.");
2399 mSoundPool.play(mBeepTwice, 1.0f, 1.0f, 0, 0, 1.0f);
2400 } else if (newVal == 2 || newVal == 3) {
2401 mSoundPool.play(mBeepOnce, 1.0f, 1.0f, 0, 0, 1.0f);
2406 if (mSoundPool != null) {
2407 mSoundPool.release();