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.Sensor;
28 import android.hardware.SensorEvent;
29 import android.hardware.SensorEventListener;
30 import android.hardware.SensorManager;
31 import android.location.Location;
32 import android.media.CameraProfile;
33 import android.net.Uri;
34 import android.os.AsyncTask;
35 import android.os.Build;
36 import android.os.Bundle;
37 import android.os.Handler;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.os.MessageQueue;
41 import android.os.SystemClock;
42 import android.provider.MediaStore;
43 import android.view.KeyEvent;
44 import android.view.OrientationEventListener;
45 import android.view.View;
47 import com.android.camera.PhotoModule.NamedImages.NamedEntity;
48 import com.android.camera.app.AppController;
49 import com.android.camera.app.CameraAppUI;
50 import com.android.camera.app.CameraProvider;
51 import com.android.camera.app.MediaSaver;
52 import com.android.camera.app.MemoryManager;
53 import com.android.camera.app.MemoryManager.MemoryListener;
54 import com.android.camera.app.MotionManager;
55 import com.android.camera.debug.Log;
56 import com.android.camera.exif.ExifInterface;
57 import com.android.camera.exif.ExifTag;
58 import com.android.camera.exif.Rational;
59 import com.android.camera.hardware.HardwareSpec;
60 import com.android.camera.hardware.HardwareSpecImpl;
61 import com.android.camera.module.ModuleController;
62 import com.android.camera.remote.RemoteCameraModule;
63 import com.android.camera.settings.CameraPictureSizesCacher;
64 import com.android.camera.settings.Keys;
65 import com.android.camera.settings.ResolutionUtil;
66 import com.android.camera.settings.SettingsManager;
67 import com.android.camera.settings.SettingsUtil;
68 import com.android.camera.ui.CountDownView;
69 import com.android.camera.ui.TouchCoordinate;
70 import com.android.camera.util.ApiHelper;
71 import com.android.camera.util.CameraUtil;
72 import com.android.camera.util.GcamHelper;
73 import com.android.camera.util.GservicesHelper;
74 import com.android.camera.util.SessionStatsCollector;
75 import com.android.camera.util.UsageStatistics;
76 import com.android.camera.widget.AspectRatioSelector;
77 import com.android.camera2.R;
78 import com.android.ex.camera2.portability.CameraAgent.CameraAFCallback;
79 import com.android.ex.camera2.portability.CameraAgent.CameraAFMoveCallback;
80 import com.android.ex.camera2.portability.CameraAgent.CameraPictureCallback;
81 import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
82 import com.android.ex.camera2.portability.CameraAgent.CameraShutterCallback;
83 import com.android.ex.camera2.portability.CameraCapabilities;
84 import com.android.ex.camera2.portability.CameraDeviceInfo.Characteristics;
85 import com.android.ex.camera2.portability.CameraSettings;
86 import com.android.ex.camera2.portability.Size;
87 import com.google.common.logging.eventprotos;
89 import java.io.ByteArrayOutputStream;
91 import java.io.FileNotFoundException;
92 import java.io.FileOutputStream;
93 import java.io.IOException;
94 import java.io.OutputStream;
95 import java.lang.ref.WeakReference;
96 import java.util.ArrayList;
97 import java.util.List;
98 import java.util.Vector;
100 public class PhotoModule
102 implements PhotoController,
105 FocusOverlayManager.Listener,
107 SettingsManager.OnSettingChangedListener,
109 CountDownView.OnCountDownStatusListener {
111 public static final String PHOTO_MODULE_STRING_ID = "PhotoModule";
113 private static final Log.Tag TAG = new Log.Tag(PHOTO_MODULE_STRING_ID);
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 CameraActivity mActivity;
135 private CameraProxy mCameraDevice;
136 private int mCameraId;
137 private CameraCapabilities mCameraCapabilities;
138 private CameraSettings mCameraSettings;
139 private boolean mPaused;
143 // The activity is going to switch to the specified camera id. This is
144 // needed because texture copy is done in GL thread. -1 means camera is not
146 protected int mPendingSwitchCameraId = -1;
148 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
149 // needed to be updated in mUpdateSet.
150 private int mUpdateSet;
152 private float mZoomValue; // The current zoom ratio.
153 private int mTimerDuration;
154 /** Set when a volume button is clicked to take photo */
155 private boolean mVolumeButtonClickedFlag = false;
157 private boolean mFocusAreaSupported;
158 private boolean mMeteringAreaSupported;
159 private boolean mAeLockSupported;
160 private boolean mAwbLockSupported;
161 private boolean mContinuousFocusSupported;
163 // The degrees of the device rotated clockwise from its natural orientation.
164 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
166 private static final String sTempCropFilename = "crop-temp";
168 private boolean mFaceDetectionStarted = false;
170 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
171 private String mCropValue;
172 private Uri mSaveUri;
174 private Uri mDebugUri;
176 // We use a queue to generated names of the images to be used later
177 // when the image is ready to be saved.
178 private NamedImages mNamedImages;
180 private final Runnable mDoSnapRunnable = new Runnable() {
183 onShutterButtonClick();
188 * An unpublished intent flag requesting to return as soon as capturing is
189 * completed. TODO: consider publishing by moving into MediaStore.
191 private static final String EXTRA_QUICK_CAPTURE =
192 "android.intent.extra.quickCapture";
194 // The display rotation in degrees. This is only valid when mCameraState is
195 // not PREVIEW_STOPPED.
196 private int mDisplayRotation;
197 // The value for android.hardware.Camera.setDisplayOrientation.
198 private int mCameraDisplayOrientation;
199 // The value for UI components like indicators.
200 private int mDisplayOrientation;
201 // The value for cameradevice.CameraSettings.setPhotoRotationDegrees.
202 private int mJpegRotation;
203 // Indicates whether we are using front camera
204 private boolean mMirror;
205 private boolean mFirstTimeInitialized;
206 private boolean mIsImageCaptureIntent;
208 private int mCameraState = PREVIEW_STOPPED;
209 private boolean mSnapshotOnIdle = false;
211 private ContentResolver mContentResolver;
213 private AppController mAppController;
215 private final PostViewPictureCallback mPostViewPictureCallback =
216 new PostViewPictureCallback();
217 private final RawPictureCallback mRawPictureCallback =
218 new RawPictureCallback();
219 private final AutoFocusCallback mAutoFocusCallback =
220 new AutoFocusCallback();
221 private final Object mAutoFocusMoveCallback =
222 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
223 ? new AutoFocusMoveCallback()
226 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
228 private long mFocusStartTime;
229 private long mShutterCallbackTime;
230 private long mPostViewPictureCallbackTime;
231 private long mRawPictureCallbackTime;
232 private long mJpegPictureCallbackTime;
233 private long mOnResumeTime;
234 private byte[] mJpegImageData;
235 /** Touch coordinate for shutter button press. */
236 private TouchCoordinate mShutterTouchCoordinate;
239 // These latency time are for the CameraLatency test.
240 public long mAutoFocusTime;
241 public long mShutterLag;
242 public long mShutterToPictureDisplayedTime;
243 public long mPictureDisplayedToJpegCallbackTime;
244 public long mJpegCallbackFinishTime;
245 public long mCaptureStartTime;
247 // This handles everything about focus.
248 private FocusOverlayManager mFocusManager;
250 private final int mGcamModeIndex;
251 private SoundPlayer mCountdownSoundPlayer;
253 private CameraCapabilities.SceneMode mSceneMode;
255 private final Handler mHandler = new MainHandler(this);
257 private boolean mQuickCapture;
258 private SensorManager mSensorManager;
259 private final float[] mGData = new float[3];
260 private final float[] mMData = new float[3];
261 private final float[] mR = new float[16];
262 private int mHeading = -1;
264 /** True if all the parameters needed to start preview is ready. */
265 private boolean mCameraPreviewParamsReady = false;
267 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
268 new MediaSaver.OnMediaSavedListener() {
270 public void onMediaSaved(Uri uri) {
272 mActivity.notifyNewMedia(uri);
276 private boolean mShouldResizeTo16x9 = false;
278 private final Runnable mResumeTaskRunnable = new Runnable() {
286 * We keep the flash setting before entering scene modes (HDR)
287 * and restore it after HDR is off.
289 private String mFlashModeBeforeSceneMode;
292 * This callback gets called when user select whether or not to
293 * turn on geo-tagging.
295 public interface LocationDialogCallback {
297 * Gets called after user selected/unselected geo-tagging feature.
299 * @param selected whether or not geo-tagging feature is selected
301 public void onLocationTaggingSelected(boolean selected);
305 * This callback defines the text that is shown in the aspect ratio selection
306 * dialog, provides the current aspect ratio, and gets notified when user changes
307 * aspect ratio selection in the dialog.
309 public interface AspectRatioDialogCallback {
311 * Returns current aspect ratio that is being used to set as default.
313 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
316 * Gets notified when user has made the aspect ratio selection.
318 * @param newAspectRatio aspect ratio that user has selected
319 * @param dialogHandlingFinishedRunnable runnable to run when the operations
320 * needed to handle changes from dialog
323 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
324 Runnable dialogHandlingFinishedRunnable);
327 private void checkDisplayRotation() {
328 // Set the display orientation if display rotation has changed.
329 // Sometimes this happens when the device is held upside
330 // down and camera app is opened. Rotation animation will
331 // take some time and the rotation value we have got may be
332 // wrong. Framework does not have a callback for this now.
333 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
334 setDisplayOrientation();
336 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
337 mHandler.postDelayed(new Runnable() {
340 checkDisplayRotation();
347 * This Handler is used to post message back onto the main thread of the
350 private static class MainHandler extends Handler {
351 private final WeakReference<PhotoModule> mModule;
353 public MainHandler(PhotoModule module) {
354 super(Looper.getMainLooper());
355 mModule = new WeakReference<PhotoModule>(module);
359 public void handleMessage(Message msg) {
360 PhotoModule module = mModule.get();
361 if (module == null) {
365 case MSG_FIRST_TIME_INIT: {
366 module.initializeFirstTime();
370 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
371 module.setCameraParametersWhenIdle(0);
378 private void switchToGcamCapture() {
379 if (mActivity != null && mGcamModeIndex != 0) {
380 SettingsManager settingsManager = mActivity.getSettingsManager();
381 settingsManager.set(SettingsManager.SCOPE_GLOBAL,
382 Keys.KEY_CAMERA_HDR_PLUS, true);
384 // Disable the HDR+ button to prevent callbacks from being
385 // queued before the correct callback is attached to the button
386 // in the new module. The new module will set the enabled/disabled
387 // of this button when the module's preferred camera becomes available.
388 ButtonManager buttonManager = mActivity.getButtonManager();
390 buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
392 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
394 // Do not post this to avoid this module switch getting interleaved with
395 // other button callbacks.
396 mActivity.onModeSelected(mGcamModeIndex);
398 buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
403 * Constructs a new photo module.
405 public PhotoModule(AppController app) {
407 mGcamModeIndex = app.getAndroidContext().getResources()
408 .getInteger(R.integer.camera_mode_gcam);
412 public String getPeekAccessibilityString() {
413 return mAppController.getAndroidContext()
414 .getResources().getString(R.string.photo_accessibility_peek);
418 public String getModuleStringIdentifier() {
419 return PHOTO_MODULE_STRING_ID;
423 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
424 mActivity = activity;
425 // TODO: Need to look at the controller interface to see if we can get
426 // rid of passing in the activity directly.
427 mAppController = mActivity;
429 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
430 mActivity.setPreviewStatusListener(mUI);
432 SettingsManager settingsManager = mActivity.getSettingsManager();
433 mCameraId = settingsManager.getInteger(mAppController.getModuleScope(),
436 // TODO: Move this to SettingsManager as a part of upgrade procedure.
437 if (!settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
438 Keys.KEY_USER_SELECTED_ASPECT_RATIO)) {
439 // Switch to back camera to set aspect ratio.
440 mCameraId = settingsManager.getIntegerDefault(Keys.KEY_CAMERA_ID);
443 mContentResolver = mActivity.getContentResolver();
445 // Surface texture is from camera screen nail and startPreview needs it.
446 // This must be done before startPreview.
447 mIsImageCaptureIntent = isImageCaptureIntent();
449 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
450 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
451 mUI.setCountdownFinishedListener(this);
452 mCountdownSoundPlayer = new SoundPlayer(mAppController.getAndroidContext());
454 // TODO: Make this a part of app controller API.
455 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
456 cancelButton.setOnClickListener(new View.OnClickListener() {
458 public void onClick(View view) {
464 private void cancelCountDown() {
465 if (mUI.isCountingDown()) {
466 // Cancel on-going countdown.
467 mUI.cancelCountDown();
469 mAppController.getCameraAppUI().transitionToCapture();
470 mAppController.getCameraAppUI().showModeOptions();
474 public boolean isUsingBottomBar() {
478 private void initializeControlByIntent() {
479 if (mIsImageCaptureIntent) {
480 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
481 setupCaptureParams();
485 private void onPreviewStarted() {
486 mAppController.onPreviewStarted();
487 setCameraState(IDLE);
488 startFaceDetection();
493 * Prompt the user to pick to record location and choose aspect ratio for the
494 * very first run of camera only.
496 private void settingsFirstRun() {
497 final SettingsManager settingsManager = mActivity.getSettingsManager();
499 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
503 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SCOPE_GLOBAL,
504 Keys.KEY_RECORD_LOCATION);
505 boolean aspectRatioPrompt = !settingsManager.getBoolean(
506 SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO);
507 if (!locationPrompt && !aspectRatioPrompt) {
511 // Check if the back camera exists
512 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
513 if (backCameraId == -1) {
514 // If there is no back camera, do not show the prompt.
518 if (locationPrompt) {
519 // Show both location and aspect ratio selection dialog.
520 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
522 public void onLocationTaggingSelected(boolean selected) {
523 Keys.setLocation(mActivity.getSettingsManager(), selected,
524 mActivity.getLocationManager());
526 }, createAspectRatioDialogCallback());
528 // App upgrade. Only show aspect ratio selection.
529 boolean wasShown = mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
531 // If the dialog was not shown, set this flag to true so that we
532 // never have to check for it again. It means that we don't need
533 // to show the dialog on this device.
534 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
535 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
540 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
541 Size currentSize = mCameraSettings.getCurrentPhotoSize();
542 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
543 if (aspectRatio < 1f) {
544 aspectRatio = 1 / aspectRatio;
546 final AspectRatioSelector.AspectRatio currentAspectRatio;
547 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
548 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
549 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
550 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
552 // TODO: Log error and not show dialog.
556 List<Size> sizes = mCameraCapabilities.getSupportedPhotoSizes();
557 List<Size> pictureSizes = ResolutionUtil
558 .getDisplayableSizesFromSupported(sizes, true);
560 // This logic below finds the largest resolution for each aspect ratio.
561 // TODO: Move this somewhere that can be shared with SettingsActivity
562 int aspectRatio4x3Resolution = 0;
563 int aspectRatio16x9Resolution = 0;
564 Size largestSize4x3 = new Size(0, 0);
565 Size largestSize16x9 = new Size(0, 0);
566 for (Size size : pictureSizes) {
567 float pictureAspectRatio = (float) size.width() / (float) size.height();
568 pictureAspectRatio = pictureAspectRatio < 1 ?
569 1f / pictureAspectRatio : pictureAspectRatio;
570 int resolution = size.width() * size.height();
571 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
572 if (resolution > aspectRatio4x3Resolution) {
573 aspectRatio4x3Resolution = resolution;
574 largestSize4x3 = size;
576 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
577 if (resolution > aspectRatio16x9Resolution) {
578 aspectRatio16x9Resolution = resolution;
579 largestSize16x9 = size;
584 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
585 final Size size4x3ToSelect = largestSize4x3;
586 final Size size16x9ToSelect = largestSize16x9;
588 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
591 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
592 return currentAspectRatio;
596 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
597 Runnable dialogHandlingFinishedRunnable) {
598 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
599 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
600 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
601 Keys.KEY_PICTURE_SIZE_BACK,
603 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
604 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
605 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
606 Keys.KEY_PICTURE_SIZE_BACK,
607 largestSize16x9Text);
609 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
610 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
611 String aspectRatio = mActivity.getSettingsManager().getString(
612 SettingsManager.SCOPE_GLOBAL,
613 Keys.KEY_USER_SELECTED_ASPECT_RATIO);
614 Log.e(TAG, "aspect ratio after setting it to true=" + aspectRatio);
615 if (newAspectRatio != currentAspectRatio) {
618 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
620 mHandler.post(dialogHandlingFinishedRunnable);
628 public void onPreviewUIReady() {
633 public void onPreviewUIDestroyed() {
634 if (mCameraDevice == null) {
637 mCameraDevice.setPreviewTexture(null);
642 public void startPreCaptureAnimation() {
643 mAppController.startPreCaptureAnimation();
646 private void onCameraOpened() {
648 initializeControlByIntent();
651 private void switchCamera() {
657 mAppController.freezeScreenUntilPreviewReady();
658 SettingsManager settingsManager = mActivity.getSettingsManager();
660 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
662 mCameraId = mPendingSwitchCameraId;
664 settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId);
667 if (mFocusManager != null) {
668 mFocusManager.removeMessages();
671 mMirror = isCameraFrontFacing();
672 mFocusManager.setMirror(mMirror);
673 // Start switch camera animation. Post a message because
674 // onFrameAvailable from the old camera may already exist.
678 * Uses the {@link CameraProvider} to open the currently-selected camera
679 * device, using {@link GservicesHelper} to choose between API-1 and API-2.
681 private void requestCameraOpen() {
682 Log.v(TAG, "requestCameraOpen");
683 mActivity.getCameraProvider().requestCamera(mCameraId,
684 GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity));
687 private final ButtonManager.ButtonCallback mCameraCallback =
688 new ButtonManager.ButtonCallback() {
690 public void onStateChanged(int state) {
691 // At the time this callback is fired, the camera id
692 // has be set to the desired camera.
694 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
697 // If switching to back camera, and HDR+ is still on,
698 // switch back to gcam, otherwise handle callback normally.
699 SettingsManager settingsManager = mActivity.getSettingsManager();
700 if (Keys.isCameraBackFacing(settingsManager,
701 mAppController.getModuleScope())) {
702 if (Keys.requestsReturnToHdrPlus(settingsManager,
703 mAppController.getModuleScope())) {
704 switchToGcamCapture();
709 mPendingSwitchCameraId = state;
711 Log.d(TAG, "Start to switch camera. cameraId=" + state);
712 // We need to keep a preview frame for the animation before
713 // releasing the camera. This will trigger
714 // onPreviewTextureCopied.
715 // TODO: Need to animate the camera switch
720 private final ButtonManager.ButtonCallback mHdrPlusCallback =
721 new ButtonManager.ButtonCallback() {
723 public void onStateChanged(int state) {
724 SettingsManager settingsManager = mActivity.getSettingsManager();
725 if (GcamHelper.hasGcamAsSeparateModule()) {
726 // Set the camera setting to default backfacing.
727 settingsManager.setToDefault(mAppController.getModuleScope(),
729 switchToGcamCapture();
731 if (Keys.isHdrOn(settingsManager)) {
732 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
733 mCameraCapabilities.getStringifier().stringify(
734 CameraCapabilities.SceneMode.HDR));
736 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
737 mCameraCapabilities.getStringifier().stringify(
738 CameraCapabilities.SceneMode.AUTO));
740 updateParametersSceneMode();
741 mCameraDevice.applySettings(mCameraSettings);
747 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
749 public void onClick(View v) {
750 onCaptureCancelled();
754 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
756 public void onClick(View v) {
761 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
763 public void onClick(View v) {
764 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
770 public void hardResetSettings(SettingsManager settingsManager) {
771 // PhotoModule should hard reset HDR+ to off,
772 // and HDR to off if HDR+ is supported.
773 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
774 if (GcamHelper.hasGcamAsSeparateModule()) {
775 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false);
780 public HardwareSpec getHardwareSpec() {
781 return (mCameraSettings != null ?
782 new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities) : null);
786 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
787 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
789 bottomBarSpec.enableCamera = true;
790 bottomBarSpec.cameraCallback = mCameraCallback;
791 bottomBarSpec.enableFlash = !mAppController.getSettingsManager()
792 .getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR);
793 bottomBarSpec.enableHdr = true;
794 bottomBarSpec.hdrCallback = mHdrPlusCallback;
795 bottomBarSpec.enableGridLines = true;
796 if (mCameraCapabilities != null) {
797 bottomBarSpec.enableExposureCompensation = true;
798 bottomBarSpec.exposureCompensationSetCallback =
799 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
801 public void setExposure(int value) {
802 setExposureCompensation(value);
805 bottomBarSpec.minExposureCompensation =
806 mCameraCapabilities.getMinExposureCompensation();
807 bottomBarSpec.maxExposureCompensation =
808 mCameraCapabilities.getMaxExposureCompensation();
809 bottomBarSpec.exposureCompensationStep =
810 mCameraCapabilities.getExposureCompensationStep();
813 bottomBarSpec.enableSelfTimer = true;
814 bottomBarSpec.showSelfTimer = true;
816 if (isImageCaptureIntent()) {
817 bottomBarSpec.showCancel = true;
818 bottomBarSpec.cancelCallback = mCancelCallback;
819 bottomBarSpec.showDone = true;
820 bottomBarSpec.doneCallback = mDoneCallback;
821 bottomBarSpec.showRetake = true;
822 bottomBarSpec.retakeCallback = mRetakeCallback;
825 return bottomBarSpec;
828 // either open a new camera or switch cameras
829 private void openCameraCommon() {
830 mUI.onCameraOpened(mCameraCapabilities, mCameraSettings);
831 if (mIsImageCaptureIntent) {
832 // Set hdr plus to default: off.
833 SettingsManager settingsManager = mActivity.getSettingsManager();
834 settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL,
835 Keys.KEY_CAMERA_HDR_PLUS);
841 public void updatePreviewAspectRatio(float aspectRatio) {
842 mAppController.updatePreviewAspectRatio(aspectRatio);
845 private void resetExposureCompensation() {
846 SettingsManager settingsManager = mActivity.getSettingsManager();
847 if (settingsManager == null) {
848 Log.e(TAG, "Settings manager is null!");
851 settingsManager.setToDefault(mAppController.getCameraScope(),
855 // Snapshots can only be taken after this is called. It should be called
856 // once only. We could have done these things in onCreate() but we want to
857 // make preview screen appear as soon as possible.
858 private void initializeFirstTime() {
859 if (mFirstTimeInitialized || mPaused) {
863 mUI.initializeFirstTime();
865 // We set the listener only when both service and shutterbutton
867 getServices().getMemoryManager().addListener(this);
869 mNamedImages = new NamedImages();
871 mFirstTimeInitialized = true;
874 mActivity.updateStorageSpaceAndHint(null);
877 // If the activity is paused and resumed, this method will be called in
879 private void initializeSecondTime() {
880 getServices().getMemoryManager().addListener(this);
881 mNamedImages = new NamedImages();
882 mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings);
885 private void addIdleHandler() {
886 MessageQueue queue = Looper.myQueue();
887 queue.addIdleHandler(new MessageQueue.IdleHandler() {
889 public boolean queueIdle() {
890 Storage.ensureOSXCompatible();
897 public void startFaceDetection() {
898 if (mFaceDetectionStarted) {
901 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
902 mFaceDetectionStarted = true;
903 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
904 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
905 mCameraDevice.startFaceDetection();
906 SessionStatsCollector.instance().faceScanActive(true);
911 public void stopFaceDetection() {
912 if (!mFaceDetectionStarted) {
915 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
916 mFaceDetectionStarted = false;
917 mCameraDevice.setFaceDetectionCallback(null, null);
918 mCameraDevice.stopFaceDetection();
920 SessionStatsCollector.instance().faceScanActive(false);
924 private final class ShutterCallback
925 implements CameraShutterCallback {
927 private final boolean mNeedsAnimation;
929 public ShutterCallback(boolean needsAnimation) {
930 mNeedsAnimation = needsAnimation;
934 public void onShutter(CameraProxy camera) {
935 mShutterCallbackTime = System.currentTimeMillis();
936 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
937 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
938 if (mNeedsAnimation) {
939 mActivity.runOnUiThread(new Runnable() {
942 animateAfterShutter();
949 private final class PostViewPictureCallback
950 implements CameraPictureCallback {
952 public void onPictureTaken(byte[] data, CameraProxy camera) {
953 mPostViewPictureCallbackTime = System.currentTimeMillis();
954 Log.v(TAG, "mShutterToPostViewCallbackTime = "
955 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
960 private final class RawPictureCallback
961 implements CameraPictureCallback {
963 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
964 mRawPictureCallbackTime = System.currentTimeMillis();
965 Log.v(TAG, "mShutterToRawCallbackTime = "
966 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
970 private static class ResizeBundle {
972 float targetAspectRatio;
977 * @return Cropped image if the target aspect ratio is larger than the jpeg
978 * aspect ratio on the long axis. The original jpeg otherwise.
980 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
982 final byte[] jpegData = dataBundle.jpegData;
983 final ExifInterface exif = dataBundle.exif;
984 float targetAspectRatio = dataBundle.targetAspectRatio;
986 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
987 int originalWidth = original.getWidth();
988 int originalHeight = original.getHeight();
992 if (originalWidth > originalHeight) {
993 newHeight = (int) (originalWidth / targetAspectRatio);
994 newWidth = originalWidth;
996 newWidth = (int) (originalHeight / targetAspectRatio);
997 newHeight = originalHeight;
999 int xOffset = (originalWidth - newWidth)/2;
1000 int yOffset = (originalHeight - newHeight)/2;
1002 if (xOffset < 0 || yOffset < 0) {
1006 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
1007 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
1008 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
1010 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1012 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
1013 dataBundle.jpegData = stream.toByteArray();
1017 private final class JpegPictureCallback
1018 implements CameraPictureCallback {
1021 public JpegPictureCallback(Location loc) {
1026 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
1027 mAppController.setShutterEnabled(true);
1031 if (mIsImageCaptureIntent) {
1034 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
1035 mUI.setSwipingEnabled(true);
1038 mJpegPictureCallbackTime = System.currentTimeMillis();
1039 // If postview callback has arrived, the captured image is displayed
1040 // in postview callback. If not, the captured image is displayed in
1041 // raw picture callback.
1042 if (mPostViewPictureCallbackTime != 0) {
1043 mShutterToPictureDisplayedTime =
1044 mPostViewPictureCallbackTime - mShutterCallbackTime;
1045 mPictureDisplayedToJpegCallbackTime =
1046 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
1048 mShutterToPictureDisplayedTime =
1049 mRawPictureCallbackTime - mShutterCallbackTime;
1050 mPictureDisplayedToJpegCallbackTime =
1051 mJpegPictureCallbackTime - mRawPictureCallbackTime;
1053 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
1054 + mPictureDisplayedToJpegCallbackTime + "ms");
1056 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
1057 if (!mIsImageCaptureIntent) {
1061 long now = System.currentTimeMillis();
1062 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1063 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1064 mJpegPictureCallbackTime = 0;
1066 final ExifInterface exif = Exif.getExif(originalJpegData);
1068 if (mShouldResizeTo16x9) {
1069 final ResizeBundle dataBundle = new ResizeBundle();
1070 dataBundle.jpegData = originalJpegData;
1071 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1072 dataBundle.exif = exif;
1073 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1076 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1077 return cropJpegDataToAspectRatio(resizeBundles[0]);
1081 protected void onPostExecute(ResizeBundle result) {
1082 saveFinalPhoto(result.jpegData, result.exif, camera);
1084 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1087 saveFinalPhoto(originalJpegData, exif, camera);
1091 void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1093 int orientation = Exif.getOrientation(exif);
1095 float zoomValue = 1.0f;
1096 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
1097 zoomValue = mCameraSettings.getCurrentZoomRatio();
1099 boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
1100 String flashSetting =
1101 mActivity.getSettingsManager().getString(mAppController.getCameraScope(),
1102 Keys.KEY_FLASH_MODE);
1103 boolean gridLinesOn = Keys.areGridLinesOn(mActivity.getSettingsManager());
1104 UsageStatistics.instance().photoCaptureDoneEvent(
1105 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1106 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
1107 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
1108 (float) mTimerDuration, mShutterTouchCoordinate, mVolumeButtonClickedFlag);
1109 mShutterTouchCoordinate = null;
1110 mVolumeButtonClickedFlag = false;
1112 if (!mIsImageCaptureIntent) {
1113 // Calculate the width and the height of the jpeg.
1114 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1115 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
1117 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1119 height = exifHeight;
1122 s = mCameraSettings.getCurrentPhotoSize();
1123 if ((mJpegRotation + orientation) % 180 == 0) {
1125 height = s.height();
1131 NamedEntity name = mNamedImages.getNextNameEntity();
1132 String title = (name == null) ? null : name.title;
1133 long date = (name == null) ? -1 : name.date;
1135 // Handle debug mode outputs
1136 if (mDebugUri != null) {
1137 // If using a debug uri, save jpeg there.
1138 saveToDebugUri(jpegData);
1140 // Adjust the title of the debug image shown in mediastore.
1141 if (title != null) {
1142 title = DEBUG_IMAGE_PREFIX + title;
1146 if (title == null) {
1147 Log.e(TAG, "Unbalanced name/data pair");
1150 date = mCaptureStartTime;
1152 if (mHeading >= 0) {
1153 // heading direction has been updated by the sensor.
1154 ExifTag directionRefTag = exif.buildTag(
1155 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1156 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1157 ExifTag directionTag = exif.buildTag(
1158 ExifInterface.TAG_GPS_IMG_DIRECTION,
1159 new Rational(mHeading, 1));
1160 exif.setTag(directionRefTag);
1161 exif.setTag(directionTag);
1163 getServices().getMediaSaver().addImage(
1164 jpegData, title, date, mLocation, width, height,
1165 orientation, exif, mOnMediaSavedListener, mContentResolver);
1167 // Animate capture with real jpeg data instead of a preview
1169 mUI.animateCapture(jpegData, orientation, mMirror);
1171 mJpegImageData = jpegData;
1172 if (!mQuickCapture) {
1173 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
1179 // Send the taken photo to remote shutter listeners, if any are
1181 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
1183 // Check this in advance of each shot so we don't add to shutter
1184 // latency. It's true that someone else could write to the SD card
1185 // in the mean time and fill it, but that could have happened
1186 // between the shutter press and saving the JPEG too.
1187 mActivity.updateStorageSpaceAndHint(null);
1191 private final class AutoFocusCallback implements CameraAFCallback {
1193 public void onAutoFocus(boolean focused, CameraProxy camera) {
1194 SessionStatsCollector.instance().autofocusResult(focused);
1199 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
1200 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
1201 setCameraState(IDLE);
1202 mFocusManager.onAutoFocus(focused, false);
1206 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1207 private final class AutoFocusMoveCallback
1208 implements CameraAFMoveCallback {
1210 public void onAutoFocusMoving(
1211 boolean moving, CameraProxy camera) {
1212 mFocusManager.onAutoFocusMoving(moving);
1213 SessionStatsCollector.instance().autofocusMoving(moving);
1218 * This class is just a thread-safe queue for name,date holder objects.
1220 public static class NamedImages {
1221 private final Vector<NamedEntity> mQueue;
1223 public NamedImages() {
1224 mQueue = new Vector<NamedEntity>();
1227 public void nameNewImage(long date) {
1228 NamedEntity r = new NamedEntity();
1229 r.title = CameraUtil.createJpegName(date);
1234 public NamedEntity getNextNameEntity() {
1235 synchronized (mQueue) {
1236 if (!mQueue.isEmpty()) {
1237 return mQueue.remove(0);
1243 public static class NamedEntity {
1244 public String title;
1249 private void setCameraState(int state) {
1250 mCameraState = state;
1252 case PREVIEW_STOPPED:
1253 case SNAPSHOT_IN_PROGRESS:
1254 case SWITCHING_CAMERA:
1255 // TODO: Tell app UI to disable swipe
1257 case PhotoController.IDLE:
1258 // TODO: Tell app UI to enable swipe
1263 private void animateAfterShutter() {
1264 // Only animate when in full screen capture mode
1265 // i.e. If monkey/a user swipes to the gallery during picture taking,
1266 // don't show animation
1267 if (!mIsImageCaptureIntent) {
1273 public boolean capture() {
1274 // If we are already in the middle of taking a snapshot or the image
1275 // save request is full then ignore.
1276 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
1277 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
1280 mCaptureStartTime = System.currentTimeMillis();
1282 mPostViewPictureCallbackTime = 0;
1283 mJpegImageData = null;
1285 final boolean animateBefore = (mSceneMode == CameraCapabilities.SceneMode.HDR);
1287 if (animateBefore) {
1288 animateAfterShutter();
1291 // Set rotation and gps data.
1294 if (mActivity.isAutoRotateScreen()) {
1295 orientation = mDisplayRotation;
1297 orientation = mOrientation;
1299 Characteristics info =
1300 mActivity.getCameraProvider().getCharacteristics(mCameraId);
1301 mJpegRotation = info.getJpegOrientation(orientation);
1302 Location loc = mActivity.getLocationManager().getCurrentLocation();
1303 CameraUtil.setGpsParameters(mCameraSettings, loc);
1304 mCameraDevice.applySettings(mCameraSettings);
1306 // We don't want user to press the button again while taking a
1307 // multi-second HDR photo.
1308 mAppController.setShutterEnabled(false);
1309 mCameraDevice.takePicture(mHandler,
1310 new ShutterCallback(!animateBefore),
1311 mRawPictureCallback, mPostViewPictureCallback,
1312 new JpegPictureCallback(loc));
1314 mNamedImages.nameNewImage(mCaptureStartTime);
1316 mFaceDetectionStarted = false;
1317 setCameraState(SNAPSHOT_IN_PROGRESS);
1322 public void setFocusParameters() {
1323 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1326 private void updateSceneMode() {
1327 // If scene mode is set, we cannot set flash mode, white balance, and
1328 // focus mode, instead, we read it from driver
1329 if (CameraCapabilities.SceneMode.AUTO != mSceneMode) {
1330 overrideCameraSettings(mCameraSettings.getCurrentFlashMode(),
1331 mCameraSettings.getCurrentFocusMode());
1335 private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode,
1336 CameraCapabilities.FocusMode focusMode) {
1337 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
1338 SettingsManager settingsManager = mActivity.getSettingsManager();
1339 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE,
1340 stringifier.stringify(flashMode));
1341 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE,
1342 stringifier.stringify(focusMode));
1346 public void onOrientationChanged(int orientation) {
1347 // We keep the last known orientation. So if the user first orient
1348 // the camera then point the camera to floor or sky, we still have
1349 // the correct orientation.
1350 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1353 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
1357 public void onCameraAvailable(CameraProxy cameraProxy) {
1358 Log.v(TAG, "onCameraAvailable");
1362 mCameraDevice = cameraProxy;
1364 initializeCapabilities();
1366 // Reset zoom value index.
1368 if (mFocusManager == null) {
1369 initializeFocusManager();
1371 mFocusManager.updateCapabilities(mCameraCapabilities);
1373 // Do camera parameter dependent initialization.
1374 mCameraSettings = mCameraDevice.getSettings();
1375 setCameraParameters(UPDATE_PARAM_ALL);
1376 // Set a listener which updates camera parameters based
1377 // on changed settings.
1378 SettingsManager settingsManager = mActivity.getSettingsManager();
1379 settingsManager.addListener(this);
1380 mCameraPreviewParamsReady = true;
1388 public void onCaptureCancelled() {
1389 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1394 public void onCaptureRetake() {
1398 mUI.hidePostCaptureAlert();
1399 mUI.hideIntentReviewImageView();
1404 public void onCaptureDone() {
1409 byte[] data = mJpegImageData;
1411 if (mCropValue == null) {
1412 // First handle the no crop case -- just return the value. If the
1413 // caller specifies a "save uri" then write the data to its
1414 // stream. Otherwise, pass back a scaled down version of the bitmap
1415 // directly in the extras.
1416 if (mSaveUri != null) {
1417 OutputStream outputStream = null;
1419 outputStream = mContentResolver.openOutputStream(mSaveUri);
1420 outputStream.write(data);
1421 outputStream.close();
1423 mActivity.setResultEx(Activity.RESULT_OK);
1425 } catch (IOException ex) {
1428 CameraUtil.closeSilently(outputStream);
1431 ExifInterface exif = Exif.getExif(data);
1432 int orientation = Exif.getOrientation(exif);
1433 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1434 bitmap = CameraUtil.rotate(bitmap, orientation);
1435 mActivity.setResultEx(Activity.RESULT_OK,
1436 new Intent("inline-data").putExtra("data", bitmap));
1440 // Save the image to a temp file and invoke the cropper
1442 FileOutputStream tempStream = null;
1444 File path = mActivity.getFileStreamPath(sTempCropFilename);
1446 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1447 tempStream.write(data);
1449 tempUri = Uri.fromFile(path);
1450 } catch (FileNotFoundException ex) {
1451 mActivity.setResultEx(Activity.RESULT_CANCELED);
1454 } catch (IOException ex) {
1455 mActivity.setResultEx(Activity.RESULT_CANCELED);
1459 CameraUtil.closeSilently(tempStream);
1462 Bundle newExtras = new Bundle();
1463 if (mCropValue.equals("circle")) {
1464 newExtras.putString("circleCrop", "true");
1466 if (mSaveUri != null) {
1467 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1469 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
1471 if (mActivity.isSecureCamera()) {
1472 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
1475 // TODO: Share this constant.
1476 final String CROP_ACTION = "com.android.camera.action.CROP";
1477 Intent cropIntent = new Intent(CROP_ACTION);
1479 cropIntent.setData(tempUri);
1480 cropIntent.putExtras(newExtras);
1482 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1487 public void onShutterCoordinate(TouchCoordinate coord) {
1488 mShutterTouchCoordinate = coord;
1492 public void onShutterButtonFocus(boolean pressed) {
1493 // Do nothing. We don't support half-press to focus anymore.
1497 public void onShutterButtonClick() {
1498 if (mPaused || (mCameraState == SWITCHING_CAMERA)
1499 || (mCameraState == PREVIEW_STOPPED)) {
1500 mVolumeButtonClickedFlag = false;
1504 // Do not take the picture if there is not enough storage.
1505 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
1506 Log.i(TAG, "Not enough space or storage not ready. remaining="
1507 + mActivity.getStorageSpaceBytes());
1508 mVolumeButtonClickedFlag = false;
1511 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
1512 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
1514 int countDownDuration = mActivity.getSettingsManager()
1515 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
1516 mTimerDuration = countDownDuration;
1517 if (countDownDuration > 0) {
1518 // Start count down.
1519 mAppController.getCameraAppUI().transitionToCancel();
1520 mAppController.getCameraAppUI().hideModeOptions();
1521 mUI.startCountdown(countDownDuration);
1528 private void focusAndCapture() {
1529 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
1530 mUI.setSwipingEnabled(false);
1532 // If the user wants to do a snapshot while the previous one is still
1533 // in progress, remember the fact and do it after we finish the previous
1534 // one and re-start the preview. Snapshot in progress also includes the
1535 // state that autofocus is focusing and a picture will be taken when
1536 // focus callback arrives.
1537 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1538 if (!mIsImageCaptureIntent) {
1539 mSnapshotOnIdle = true;
1544 mSnapshotOnIdle = false;
1545 mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode());
1549 public void onRemainingSecondsChanged(int remainingSeconds) {
1550 if (remainingSeconds == 1) {
1551 mCountdownSoundPlayer.play(R.raw.beep_twice, 0.6f);
1552 } else if (remainingSeconds == 2 || remainingSeconds == 3) {
1553 mCountdownSoundPlayer.play(R.raw.beep_once, 0.6f);
1558 public void onCountDownFinished() {
1559 if (mIsImageCaptureIntent) {
1560 mAppController.getCameraAppUI().transitionToIntentReviewLayout();
1562 mAppController.getCameraAppUI().transitionToCapture();
1564 mAppController.getCameraAppUI().showModeOptions();
1571 private void onResumeTasks() {
1575 Log.v(TAG, "Executing onResumeTasks.");
1577 mCountdownSoundPlayer.loadSound(R.raw.beep_once);
1578 mCountdownSoundPlayer.loadSound(R.raw.beep_twice);
1579 if (mFocusManager != null) {
1580 // If camera is not open when resume is called, focus manager will
1581 // not be initialized yet, in which case it will start listening to
1582 // preview area size change later in the initialization.
1583 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1585 mAppController.addPreviewAreaSizeChangedListener(mUI);
1587 CameraProvider camProvider = mActivity.getCameraProvider();
1588 if (camProvider == null) {
1589 // No camera provider, the Activity is destroyed already.
1592 requestCameraOpen();
1594 mJpegPictureCallbackTime = 0;
1597 mOnResumeTime = SystemClock.uptimeMillis();
1598 checkDisplayRotation();
1600 // If first time initialization is not finished, put it in the
1602 if (!mFirstTimeInitialized) {
1603 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
1605 initializeSecondTime();
1608 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1609 if (gsensor != null) {
1610 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1613 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1614 if (msensor != null) {
1615 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1618 getServices().getRemoteShutterListener().onModuleReady(this);
1619 SessionStatsCollector.instance().sessionActive(true);
1623 * @return Whether the currently active camera is front-facing.
1625 private boolean isCameraFrontFacing() {
1626 return mAppController.getCameraProvider().getCharacteristics(mCameraId)
1631 * The focus manager is the first UI related element to get initialized, and
1632 * it requires the RenderOverlay, so initialize it here
1634 private void initializeFocusManager() {
1635 // Create FocusManager object. startPreview needs it.
1636 // if mFocusManager not null, reuse it
1637 // otherwise create a new instance
1638 if (mFocusManager != null) {
1639 mFocusManager.removeMessages();
1641 mMirror = isCameraFrontFacing();
1642 String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
1643 R.array.pref_camera_focusmode_default_array);
1644 ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
1645 new ArrayList<CameraCapabilities.FocusMode>();
1646 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
1647 for (String modeString : defaultFocusModesStrings) {
1648 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
1650 defaultFocusModes.add(mode);
1654 new FocusOverlayManager(mAppController, defaultFocusModes,
1655 mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
1657 MotionManager motionManager = getServices().getMotionManager();
1658 if (motionManager != null) {
1659 motionManager.addListener(mFocusManager);
1662 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1666 * @return Whether we are resuming from within the lockscreen.
1668 private boolean isResumeFromLockscreen() {
1669 String action = mActivity.getIntent().getAction();
1670 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1671 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
1675 public void resume() {
1678 // Add delay on resume from lock screen only, in order to to speed up
1679 // the onResume --> onPause --> onResume cycle from lock screen.
1680 // Don't do always because letting go of thread can cause delay.
1681 if (isResumeFromLockscreen()) {
1682 Log.v(TAG, "On resume, from lock screen.");
1683 // Note: onPauseAfterSuper() will delete this runnable, so we will
1684 // at most have 1 copy queued up.
1685 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
1687 Log.v(TAG, "On resume.");
1693 public void pause() {
1695 mHandler.removeCallbacks(mResumeTaskRunnable);
1696 getServices().getRemoteShutterListener().onModuleExit();
1697 SessionStatsCollector.instance().sessionActive(false);
1699 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1700 if (gsensor != null) {
1701 mSensorManager.unregisterListener(this, gsensor);
1704 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1705 if (msensor != null) {
1706 mSensorManager.unregisterListener(this, msensor);
1709 // Reset the focus first. Camera CTS does not guarantee that
1710 // cancelAutoFocus is allowed after preview stops.
1711 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1712 mCameraDevice.cancelAutoFocus();
1715 // If the camera has not been opened asynchronously yet,
1716 // and startPreview hasn't been called, then this is a no-op.
1717 // (e.g. onResume -> onPause -> onResume).
1720 mCountdownSoundPlayer.release();
1722 mNamedImages = null;
1723 // If we are in an image capture intent and has taken
1724 // a picture, we just clear it in onPause.
1725 mJpegImageData = null;
1727 // Remove the messages and runnables in the queue.
1728 mHandler.removeCallbacksAndMessages(null);
1731 mActivity.enableKeepScreenOn(false);
1734 mPendingSwitchCameraId = -1;
1735 if (mFocusManager != null) {
1736 mFocusManager.removeMessages();
1738 getServices().getMemoryManager().removeListener(this);
1739 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
1740 mAppController.removePreviewAreaSizeChangedListener(mUI);
1742 SettingsManager settingsManager = mActivity.getSettingsManager();
1743 settingsManager.removeListener(this);
1747 public void destroy() {
1748 // TODO: implement this.
1752 public void onLayoutOrientationChanged(boolean isLandscape) {
1753 setDisplayOrientation();
1757 public void updateCameraOrientation() {
1758 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
1759 setDisplayOrientation();
1763 private boolean canTakePicture() {
1764 return isCameraIdle()
1765 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
1769 public void autoFocus() {
1770 Log.v(TAG,"Starting auto focus");
1771 mFocusStartTime = System.currentTimeMillis();
1772 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
1773 SessionStatsCollector.instance().autofocusManualTrigger();
1774 setCameraState(FOCUSING);
1778 public void cancelAutoFocus() {
1779 mCameraDevice.cancelAutoFocus();
1780 setCameraState(IDLE);
1781 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1785 public void onSingleTapUp(View view, int x, int y) {
1786 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1787 || mCameraState == SNAPSHOT_IN_PROGRESS
1788 || mCameraState == SWITCHING_CAMERA
1789 || mCameraState == PREVIEW_STOPPED) {
1793 // Check if metering area or focus area is supported.
1794 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1797 mFocusManager.onSingleTapUp(x, y);
1801 public boolean onBackPressed() {
1802 return mUI.onBackPressed();
1806 public boolean onKeyDown(int keyCode, KeyEvent event) {
1808 case KeyEvent.KEYCODE_VOLUME_UP:
1809 case KeyEvent.KEYCODE_VOLUME_DOWN:
1810 case KeyEvent.KEYCODE_FOCUS:
1811 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1812 !mActivity.getCameraAppUI().isInIntentReview()) {
1813 if (event.getRepeatCount() == 0) {
1814 onShutterButtonFocus(true);
1819 case KeyEvent.KEYCODE_CAMERA:
1820 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1821 onShutterButtonClick();
1824 case KeyEvent.KEYCODE_DPAD_CENTER:
1825 // If we get a dpad center event without any focused view, move
1826 // the focus to the shutter button and press it.
1827 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1828 // Start auto-focus immediately to reduce shutter lag. After
1829 // the shutter button gets the focus, onShutterButtonFocus()
1830 // will be called again but it is fine.
1831 onShutterButtonFocus(true);
1839 public boolean onKeyUp(int keyCode, KeyEvent event) {
1841 case KeyEvent.KEYCODE_VOLUME_UP:
1842 case KeyEvent.KEYCODE_VOLUME_DOWN:
1843 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1844 !mActivity.getCameraAppUI().isInIntentReview()) {
1845 if (mUI.isCountingDown()) {
1848 mVolumeButtonClickedFlag = true;
1849 onShutterButtonClick();
1854 case KeyEvent.KEYCODE_FOCUS:
1855 if (mFirstTimeInitialized) {
1856 onShutterButtonFocus(false);
1863 private void closeCamera() {
1864 if (mCameraDevice != null) {
1865 stopFaceDetection();
1866 mCameraDevice.setZoomChangeListener(null);
1867 mCameraDevice.setFaceDetectionCallback(null, null);
1868 mCameraDevice.setErrorCallback(null, null);
1870 mFaceDetectionStarted = false;
1871 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
1872 mCameraDevice = null;
1873 setCameraState(PREVIEW_STOPPED);
1874 mFocusManager.onCameraReleased();
1878 private void setDisplayOrientation() {
1879 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1880 Characteristics info =
1881 mActivity.getCameraProvider().getCharacteristics(mCameraId);
1882 mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
1883 mCameraDisplayOrientation = mDisplayOrientation;
1884 mUI.setDisplayOrientation(mDisplayOrientation);
1885 if (mFocusManager != null) {
1886 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1888 // Change the camera display orientation
1889 if (mCameraDevice != null) {
1890 mCameraDevice.setDisplayOrientation(mDisplayRotation);
1894 /** Only called by UI thread. */
1895 private void setupPreview() {
1896 mFocusManager.resetTouchFocus();
1901 * Returns whether we can/should start the preview or not.
1903 private boolean checkPreviewPreconditions() {
1908 if (mCameraDevice == null) {
1909 Log.w(TAG, "startPreview: camera device not ready yet.");
1913 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
1915 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
1919 if (!mCameraPreviewParamsReady) {
1920 Log.w(TAG, "startPreview: parameters for preview is not ready.");
1927 * The start/stop preview should only run on the UI thread.
1929 private void startPreview() {
1930 if (!checkPreviewPreconditions()) {
1934 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
1935 setDisplayOrientation();
1937 if (!mSnapshotOnIdle) {
1938 // If the focus mode is continuous autofocus, call cancelAutoFocus
1939 // to resume it because it may have been paused by autoFocus call.
1940 if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
1941 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
1942 mCameraDevice.cancelAutoFocus();
1944 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1946 setCameraParameters(UPDATE_PARAM_ALL);
1947 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
1949 Log.i(TAG, "startPreview");
1950 mCameraDevice.startPreview();
1952 mFocusManager.onPreviewStarted();
1954 SessionStatsCollector.instance().previewActive(true);
1955 if (mSnapshotOnIdle) {
1956 mHandler.post(mDoSnapRunnable);
1961 public void stopPreview() {
1962 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1963 Log.i(TAG, "stopPreview");
1964 mCameraDevice.stopPreview();
1965 mFaceDetectionStarted = false;
1967 setCameraState(PREVIEW_STOPPED);
1968 if (mFocusManager != null) {
1969 mFocusManager.onPreviewStopped();
1971 SessionStatsCollector.instance().previewActive(false);
1975 public void onSettingChanged(SettingsManager settingsManager, String key) {
1976 if (key.equals(Keys.KEY_FLASH_MODE)) {
1977 updateParametersFlashMode();
1979 if (key.equals(Keys.KEY_CAMERA_HDR)) {
1980 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
1981 Keys.KEY_CAMERA_HDR)) {
1983 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
1984 mFlashModeBeforeSceneMode = settingsManager.getString(
1985 mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
1987 if (mFlashModeBeforeSceneMode != null) {
1988 settingsManager.set(mAppController.getCameraScope(),
1989 Keys.KEY_FLASH_MODE,
1990 mFlashModeBeforeSceneMode);
1991 updateParametersFlashMode();
1992 mFlashModeBeforeSceneMode = null;
1994 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
1998 if (mCameraDevice != null) {
1999 mCameraDevice.applySettings(mCameraSettings);
2003 private void updateCameraParametersInitialize() {
2004 // Reset preview frame rate to the maximum because it may be lowered by
2005 // video camera application.
2006 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
2007 if (fpsRange != null && fpsRange.length > 0) {
2008 mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
2011 mCameraSettings.setRecordingHintEnabled(false);
2013 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
2014 mCameraSettings.setVideoStabilization(false);
2018 private void updateCameraParametersZoom() {
2020 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
2021 mCameraSettings.setZoomRatio(mZoomValue);
2025 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
2026 private void setAutoExposureLockIfSupported() {
2027 if (mAeLockSupported) {
2028 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
2032 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
2033 private void setAutoWhiteBalanceLockIfSupported() {
2034 if (mAwbLockSupported) {
2035 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
2039 private void setFocusAreasIfSupported() {
2040 if (mFocusAreaSupported) {
2041 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
2045 private void setMeteringAreasIfSupported() {
2046 if (mMeteringAreaSupported) {
2047 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
2051 private void updateCameraParametersPreference() {
2052 setAutoExposureLockIfSupported();
2053 setAutoWhiteBalanceLockIfSupported();
2054 setFocusAreasIfSupported();
2055 setMeteringAreasIfSupported();
2057 // Initialize focus mode.
2058 mFocusManager.overrideFocusMode(null);
2060 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
2061 SessionStatsCollector.instance().autofocusActive(
2062 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
2063 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
2066 // Set picture size.
2067 updateParametersPictureSize();
2069 // Set JPEG quality.
2070 updateParametersPictureQuality();
2072 // For the following settings, we need to check if the settings are
2073 // still supported by latest driver, if not, ignore the settings.
2075 // Set exposure compensation
2076 updateParametersExposureCompensation();
2078 // Set the scene mode: also sets flash and white balance.
2079 updateParametersSceneMode();
2081 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2082 updateAutoFocusMoveCallback();
2086 private void updateParametersPictureSize() {
2087 SettingsManager settingsManager = mActivity.getSettingsManager();
2088 String pictureSizeKey = isCameraFrontFacing() ? Keys.KEY_PICTURE_SIZE_FRONT
2089 : Keys.KEY_PICTURE_SIZE_BACK;
2090 String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL,
2093 List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes();
2094 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2095 mCameraDevice.getCameraId(), supported);
2096 SettingsUtil.setCameraPictureSize(pictureSize, supported, mCameraSettings,
2097 mCameraDevice.getCameraId());
2099 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2100 mCameraDevice.getCameraId());
2101 if (ApiHelper.IS_NEXUS_5) {
2102 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2103 mShouldResizeTo16x9 = true;
2105 mShouldResizeTo16x9 = false;
2109 // Set a preview size that is closest to the viewfinder height and has
2110 // the right aspect ratio.
2111 List<Size> sizes = mCameraCapabilities.getSupportedPreviewSizes();
2112 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
2113 (double) size.width() / size.height());
2114 Size original = mCameraSettings.getCurrentPreviewSize();
2115 if (!optimalSize.equals(original)) {
2116 mCameraSettings.setPreviewSize(optimalSize);
2118 // Zoom related settings will be changed for different preview
2119 // sizes, so set and read the parameters to get latest values
2120 if (mHandler.getLooper() == Looper.myLooper()) {
2121 // On UI thread only, not when camera starts up
2124 mCameraDevice.applySettings(mCameraSettings);
2126 mCameraSettings = mCameraDevice.getSettings();
2129 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
2130 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2131 / (float) optimalSize.height());
2133 Log.i(TAG, "Preview size is " + optimalSize);
2136 private void updateParametersPictureQuality() {
2137 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2138 CameraProfile.QUALITY_HIGH);
2139 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
2142 private void updateParametersExposureCompensation() {
2143 SettingsManager settingsManager = mActivity.getSettingsManager();
2144 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2145 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2146 int value = settingsManager.getInteger(mAppController.getCameraScope(),
2148 int max = mCameraCapabilities.getMaxExposureCompensation();
2149 int min = mCameraCapabilities.getMinExposureCompensation();
2150 if (value >= min && value <= max) {
2151 mCameraSettings.setExposureCompensationIndex(value);
2153 Log.w(TAG, "invalid exposure range: " + value);
2156 // If exposure compensation is not enabled, reset the exposure compensation value.
2157 setExposureCompensation(0);
2162 private void updateParametersSceneMode() {
2163 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
2164 SettingsManager settingsManager = mActivity.getSettingsManager();
2166 mSceneMode = stringifier.
2167 sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2168 Keys.KEY_SCENE_MODE));
2169 if (mCameraCapabilities.supports(mSceneMode)) {
2170 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
2171 mCameraSettings.setSceneMode(mSceneMode);
2173 // Setting scene mode will change the settings of flash mode,
2174 // white balance, and focus mode. Here we read back the
2175 // parameters, so we can know those settings.
2176 mCameraDevice.applySettings(mCameraSettings);
2177 mCameraSettings = mCameraDevice.getSettings();
2180 mSceneMode = mCameraSettings.getCurrentSceneMode();
2181 if (mSceneMode == null) {
2182 mSceneMode = CameraCapabilities.SceneMode.AUTO;
2186 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
2188 updateParametersFlashMode();
2191 mFocusManager.overrideFocusMode(null);
2192 mCameraSettings.setFocusMode(
2193 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
2195 mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
2199 private void updateParametersFlashMode() {
2200 SettingsManager settingsManager = mActivity.getSettingsManager();
2202 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
2203 .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2204 Keys.KEY_FLASH_MODE));
2205 if (mCameraCapabilities.supports(flashMode)) {
2206 mCameraSettings.setFlashMode(flashMode);
2210 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
2211 private void updateAutoFocusMoveCallback() {
2212 if (mCameraSettings.getCurrentFocusMode() ==
2213 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
2214 mCameraDevice.setAutoFocusMoveCallback(mHandler,
2215 (CameraAFMoveCallback) mAutoFocusMoveCallback);
2217 mCameraDevice.setAutoFocusMoveCallback(null, null);
2222 * Sets the exposure compensation to the given value and also updates settings.
2224 * @param value exposure compensation value to be set
2226 public void setExposureCompensation(int value) {
2227 int max = mCameraCapabilities.getMaxExposureCompensation();
2228 int min = mCameraCapabilities.getMinExposureCompensation();
2229 if (value >= min && value <= max) {
2230 mCameraSettings.setExposureCompensationIndex(value);
2231 SettingsManager settingsManager = mActivity.getSettingsManager();
2232 settingsManager.set(mAppController.getCameraScope(),
2233 Keys.KEY_EXPOSURE, value);
2235 Log.w(TAG, "invalid exposure range: " + value);
2239 // We separate the parameters into several subsets, so we can update only
2240 // the subsets actually need updating. The PREFERENCE set needs extra
2241 // locking because the preference can be changed from GLThread as well.
2242 private void setCameraParameters(int updateSet) {
2243 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2244 updateCameraParametersInitialize();
2247 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2248 updateCameraParametersZoom();
2251 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
2252 updateCameraParametersPreference();
2255 mCameraDevice.applySettings(mCameraSettings);
2258 // If the Camera is idle, update the parameters immediately, otherwise
2259 // accumulate them in mUpdateSet and update later.
2260 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2261 mUpdateSet |= additionalUpdateSet;
2262 if (mCameraDevice == null) {
2263 // We will update all the parameters when we open the device, so
2264 // we don't need to do anything now.
2267 } else if (isCameraIdle()) {
2268 setCameraParameters(mUpdateSet);
2272 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2273 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
2279 public boolean isCameraIdle() {
2280 return (mCameraState == IDLE) ||
2281 (mCameraState == PREVIEW_STOPPED) ||
2282 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
2283 && (mCameraState != SWITCHING_CAMERA));
2287 public boolean isImageCaptureIntent() {
2288 String action = mActivity.getIntent().getAction();
2289 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
2290 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
2293 private void setupCaptureParams() {
2294 Bundle myExtras = mActivity.getIntent().getExtras();
2295 if (myExtras != null) {
2296 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2297 mCropValue = myExtras.getString("crop");
2301 private void initializeCapabilities() {
2302 mCameraCapabilities = mCameraDevice.getCapabilities();
2303 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2304 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2305 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2306 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
2307 mContinuousFocusSupported =
2308 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
2312 public void onZoomChanged(float ratio) {
2313 // Not useful to change zoom value when the activity is paused.
2318 if (mCameraSettings == null || mCameraDevice == null) {
2321 // Set zoom parameters asynchronously
2322 mCameraSettings.setZoomRatio(mZoomValue);
2323 mCameraDevice.applySettings(mCameraSettings);
2327 public int getCameraState() {
2328 return mCameraState;
2332 public void onMemoryStateChanged(int state) {
2333 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
2337 public void onLowMemory() {
2338 // Not much we can do in the photo module.
2342 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2346 public void onSensorChanged(SensorEvent event) {
2347 int type = event.sensor.getType();
2349 if (type == Sensor.TYPE_ACCELEROMETER) {
2351 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2354 // we should not be here.
2357 for (int i = 0; i < 3; i++) {
2358 data[i] = event.values[i];
2360 float[] orientation = new float[3];
2361 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2362 SensorManager.getOrientation(mR, orientation);
2363 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2369 // For debugging only.
2370 public void setDebugUri(Uri uri) {
2374 // For debugging only.
2375 private void saveToDebugUri(byte[] data) {
2376 if (mDebugUri != null) {
2377 OutputStream outputStream = null;
2379 outputStream = mContentResolver.openOutputStream(mDebugUri);
2380 outputStream.write(data);
2381 outputStream.close();
2382 } catch (IOException e) {
2383 Log.e(TAG, "Exception while writing debug jpeg file", e);
2385 CameraUtil.closeSilently(outputStream);
2391 public void onRemoteShutterPress() {
2392 mHandler.post(new Runnable() {