blob: 789337e4becd4fa206c19e8c35bb69cf4230f817 [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.annotation.TargetApi;
20import android.app.Activity;
Michael Kolb8872c232013-01-29 10:33:22 -080021import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070022import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080023import android.content.Intent;
Michael Kolb8872c232013-01-29 10:33:22 -080024import android.graphics.Bitmap;
25import android.graphics.SurfaceTexture;
26import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080027import android.hardware.Camera.Parameters;
Michael Kolb8872c232013-01-29 10:33:22 -080028import android.hardware.Camera.Size;
Angus Kong0d00a892013-03-26 11:40:40 -070029import android.hardware.Sensor;
30import android.hardware.SensorEvent;
31import android.hardware.SensorEventListener;
32import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080033import android.location.Location;
34import android.media.CameraProfile;
35import android.net.Uri;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070036import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080037import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080038import android.os.Handler;
39import android.os.Looper;
40import android.os.Message;
41import android.os.MessageQueue;
42import android.os.SystemClock;
43import android.provider.MediaStore;
Michael Kolb8872c232013-01-29 10:33:22 -080044import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080045import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080046import android.view.View;
Sascha Haeberlingbd016452014-03-11 10:19:22 -070047import android.view.ViewGroup;
Michael Kolb8872c232013-01-29 10:33:22 -080048
Sameer Padala2c8cc452013-11-05 18:49:12 -080049import com.android.camera.PhotoModule.NamedImages.NamedEntity;
50import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080051import com.android.camera.app.CameraAppUI;
Angus Kong20fad242013-11-11 18:23:46 -080052import com.android.camera.app.CameraManager.CameraAFCallback;
53import com.android.camera.app.CameraManager.CameraAFMoveCallback;
54import com.android.camera.app.CameraManager.CameraPictureCallback;
55import com.android.camera.app.CameraManager.CameraProxy;
56import com.android.camera.app.CameraManager.CameraShutterCallback;
Kevin Gabayanffbc43c2013-12-09 11:41:50 -080057import com.android.camera.app.LocationManager;
Angus Kongfd4fc0e2013-11-07 15:38:09 -080058import com.android.camera.app.MediaSaver;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -080059import com.android.camera.app.MemoryManager;
60import com.android.camera.app.MemoryManager.MemoryListener;
Angus Kong2bca2102014-03-11 16:27:30 -070061import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070062import com.android.camera.exif.ExifInterface;
63import com.android.camera.exif.ExifTag;
64import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080065import com.android.camera.hardware.HardwareSpec;
66import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080067import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080068import com.android.camera.remote.RemoteCameraModule;
Erin Dahlgren357b7672013-11-20 17:38:14 -080069import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010070import com.android.camera.settings.SettingsUtil;
Angus Kongdcccc512013-08-08 17:06:03 -070071import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070072import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070073import com.android.camera.util.GcamHelper;
Sameer Padala2c8cc452013-11-05 18:49:12 -080074import com.android.camera.util.SmartCameraHelper;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070075import com.android.camera.util.UsageStatistics;
76import com.android.camera2.R;
Seth Raphael5e09d012013-12-18 13:45:03 -080077import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080078
Angus Kongdcccc512013-08-08 17:06:03 -070079import java.io.File;
80import java.io.FileNotFoundException;
81import java.io.FileOutputStream;
82import java.io.IOException;
83import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010084import java.lang.ref.WeakReference;
Angus Kongdcccc512013-08-08 17:06:03 -070085import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070086import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070087
Michael Kolb8872c232013-01-29 10:33:22 -080088public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080089 extends CameraModule
90 implements PhotoController,
91 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -080092 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080093 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -080094 SensorEventListener,
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080095 SettingsManager.OnSettingChangedListener,
96 RemoteCameraModule {
Michael Kolb8872c232013-01-29 10:33:22 -080097
Angus Kong2bca2102014-03-11 16:27:30 -070098 private static final Log.Tag TAG = new Log.Tag("PhotoModule");
Michael Kolb8872c232013-01-29 10:33:22 -080099
100 // We number the request code from 1000 to avoid collision with Gallery.
101 private static final int REQUEST_CROP = 1000;
102
Angus Kong13e87c42013-11-25 10:02:47 -0800103 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800104 private static final int MSG_FIRST_TIME_INIT = 1;
105 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800106
107 // The subset of parameters we need to update in setCameraParameters().
108 private static final int UPDATE_PARAM_INITIALIZE = 1;
109 private static final int UPDATE_PARAM_ZOOM = 2;
110 private static final int UPDATE_PARAM_PREFERENCE = 4;
111 private static final int UPDATE_PARAM_ALL = -1;
112
Andy Huibersdef975d2013-11-22 09:13:39 -0800113 // This is the delay before we execute onResume tasks when coming
114 // from the lock screen, to allow time for onPause to execute.
115 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800116
Ruben Brunkd7488272013-10-10 18:45:53 -0700117 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
118
Michael Kolb8872c232013-01-29 10:33:22 -0800119 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800120 private CameraProxy mCameraDevice;
121 private int mCameraId;
122 private Parameters mParameters;
123 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800124
125 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800126
Michael Kolb8872c232013-01-29 10:33:22 -0800127 // The activity is going to switch to the specified camera id. This is
128 // needed because texture copy is done in GL thread. -1 means camera is not
129 // switching.
130 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800131
132 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
133 // needed to be updated in mUpdateSet.
134 private int mUpdateSet;
135
136 private static final int SCREEN_DELAY = 2 * 60 * 1000;
137
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800138 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800139
140 private Parameters mInitialParams;
141 private boolean mFocusAreaSupported;
142 private boolean mMeteringAreaSupported;
143 private boolean mAeLockSupported;
144 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700145 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800146
147 // The degrees of the device rotated clockwise from its natural orientation.
148 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800149
150 private static final String sTempCropFilename = "crop-temp";
151
Michael Kolb8872c232013-01-29 10:33:22 -0800152 private boolean mFaceDetectionStarted = false;
153
Michael Kolb8872c232013-01-29 10:33:22 -0800154 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
155 private String mCropValue;
156 private Uri mSaveUri;
157
Ruben Brunkd217ed02013-10-08 23:31:13 -0700158 private Uri mDebugUri;
159
Angus Kongce5480e2013-01-29 17:43:48 -0800160 // We use a queue to generated names of the images to be used later
161 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800162 private NamedImages mNamedImages;
163
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800164 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800165 @Override
166 public void run() {
167 onShutterButtonClick();
168 }
169 };
170
Michael Kolb8872c232013-01-29 10:33:22 -0800171 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800172 * An unpublished intent flag requesting to return as soon as capturing is
173 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800174 */
175 private static final String EXTRA_QUICK_CAPTURE =
176 "android.intent.extra.quickCapture";
177
178 // The display rotation in degrees. This is only valid when mCameraState is
179 // not PREVIEW_STOPPED.
180 private int mDisplayRotation;
181 // The value for android.hardware.Camera.setDisplayOrientation.
182 private int mCameraDisplayOrientation;
183 // The value for UI components like indicators.
184 private int mDisplayOrientation;
185 // The value for android.hardware.Camera.Parameters.setRotation.
186 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700187 // Indicates whether we are using front camera
188 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800189 private boolean mFirstTimeInitialized;
190 private boolean mIsImageCaptureIntent;
191
Michael Kolb8872c232013-01-29 10:33:22 -0800192 private int mCameraState = PREVIEW_STOPPED;
193 private boolean mSnapshotOnIdle = false;
194
195 private ContentResolver mContentResolver;
196
197 private LocationManager mLocationManager;
Doris Liu2b906b82013-12-10 16:34:08 -0800198 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800199
Michael Kolb8872c232013-01-29 10:33:22 -0800200 private final PostViewPictureCallback mPostViewPictureCallback =
201 new PostViewPictureCallback();
202 private final RawPictureCallback mRawPictureCallback =
203 new RawPictureCallback();
204 private final AutoFocusCallback mAutoFocusCallback =
205 new AutoFocusCallback();
206 private final Object mAutoFocusMoveCallback =
207 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700208 ? new AutoFocusMoveCallback()
209 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800210
211 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
212
213 private long mFocusStartTime;
214 private long mShutterCallbackTime;
215 private long mPostViewPictureCallbackTime;
216 private long mRawPictureCallbackTime;
217 private long mJpegPictureCallbackTime;
218 private long mOnResumeTime;
219 private byte[] mJpegImageData;
220
221 // These latency time are for the CameraLatency test.
222 public long mAutoFocusTime;
223 public long mShutterLag;
224 public long mShutterToPictureDisplayedTime;
225 public long mPictureDisplayedToJpegCallbackTime;
226 public long mJpegCallbackFinishTime;
227 public long mCaptureStartTime;
228
229 // This handles everything about focus.
230 private FocusOverlayManager mFocusManager;
231
Doris Liubd1b8f92014-01-03 17:59:51 -0800232 private final int mGcamModeIndex;
Doris Liubd1b8f92014-01-03 17:59:51 -0800233
Michael Kolb8872c232013-01-29 10:33:22 -0800234 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800235
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100236 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800237
Michael Kolb8872c232013-01-29 10:33:22 -0800238 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700239 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800240 private final float[] mGData = new float[3];
241 private final float[] mMData = new float[3];
242 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700243 private int mHeading = -1;
244
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800245 /** Whether shutter is enabled. */
Spike Sprague215f6b02013-12-12 11:53:49 -0800246 private boolean mShutterEnabled = true;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800247
248 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700249 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700250
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800251 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800252 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800253 @Override
254 public void onMediaSaved(Uri uri) {
255 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700256 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800257 }
258 }
259 };
Michael Kolb8872c232013-01-29 10:33:22 -0800260
Angus Kongdcccc512013-08-08 17:06:03 -0700261 private void checkDisplayRotation() {
262 // Set the display orientation if display rotation has changed.
263 // Sometimes this happens when the device is held upside
264 // down and camera app is opened. Rotation animation will
265 // take some time and the rotation value we have got may be
266 // wrong. Framework does not have a callback for this now.
267 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
268 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800269 }
Angus Kongdcccc512013-08-08 17:06:03 -0700270 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
271 mHandler.postDelayed(new Runnable() {
272 @Override
273 public void run() {
274 checkDisplayRotation();
275 }
276 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800277 }
278 }
279
280 /**
281 * This Handler is used to post message back onto the main thread of the
282 * application
283 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100284 private static class MainHandler extends Handler {
285 private final WeakReference<PhotoModule> mModule;
286
287 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800288 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100289 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800290 }
291
Michael Kolb8872c232013-01-29 10:33:22 -0800292 @Override
293 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100294 PhotoModule module = mModule.get();
295 if (module == null) {
296 return;
297 }
Michael Kolb8872c232013-01-29 10:33:22 -0800298 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800299 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100300 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800301 break;
302 }
303
Angus Kong13e87c42013-11-25 10:02:47 -0800304 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100305 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800306 break;
307 }
Michael Kolb8872c232013-01-29 10:33:22 -0800308 }
309 }
310 }
311
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700312 private void switchToGcamCapture() {
313 if (mActivity != null && mGcamModeIndex != 0) {
314 SettingsManager settingsManager = mActivity.getSettingsManager();
315 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR,
316 SettingsManager.VALUE_ON);
317
318 // Disable the HDR+ button to prevent callbacks from being
319 // queued before the correct callback is attached to the button
320 // in the new module. The new module will set the enabled/disabled
321 // of this button when the module's preferred camera becomes available.
322 ButtonManager buttonManager = mActivity.getButtonManager();
323 buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS);
324
325 // Do not post this to avoid this module switch getting interleaved with
326 // other button callbacks.
327 mActivity.onModeSelected(mGcamModeIndex);
328 }
329 }
330
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800331 /**
332 * Constructs a new photo module.
333 */
Angus Kongc4e66562013-11-22 23:03:21 -0800334 public PhotoModule(AppController app) {
335 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800336 mGcamModeIndex = app.getAndroidContext().getResources()
337 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800338 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700339
Michael Kolb8872c232013-01-29 10:33:22 -0800340 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100341 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
342 mActivity = activity;
343 // TODO: Need to look at the controller interface to see if we can get
344 // rid of passing in the activity directly.
345 mAppController = mActivity;
346
347 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
348 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800349
350 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800351 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
Michael Kolb8872c232013-01-29 10:33:22 -0800352
353 mContentResolver = mActivity.getContentResolver();
354
Michael Kolb8872c232013-01-29 10:33:22 -0800355 // Surface texture is from camera screen nail and startPreview needs it.
356 // This must be done before startPreview.
357 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800358
Angus Kong20fad242013-11-11 18:23:46 -0800359 mActivity.getCameraProvider().requestCamera(mCameraId);
360
Michael Kolb8872c232013-01-29 10:33:22 -0800361 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Erin Dahlgren21c21a62013-11-19 16:37:38 -0800362 mLocationManager = mActivity.getLocationManager();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800363 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Michael Kolbd6954f32013-03-08 20:43:01 -0800364 }
365
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800366 @Override
367 public boolean isUsingBottomBar() {
368 return true;
369 }
370
Michael Kolbd6954f32013-03-08 20:43:01 -0800371 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800372 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800373 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800374 setupCaptureParams();
375 }
376 }
377
378 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800379 mAppController.onPreviewStarted();
Michael Kolbd6954f32013-03-08 20:43:01 -0800380 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800381 startFaceDetection();
382 locationFirstRun();
Teresa Ko59953a22014-03-07 15:43:28 -0800383 // TODO(teresako): Check with Camera team re: starting the Smart Camera here rather than
384 // in the onPreviewInitialDataReceived() function which is no longer being called in
385 // Denali. The original issue of the blue overlay not going away no longer seems to be
386 // an issue. Related CL: https://googleplex-android-review.git.corp.google.com/#/c/428719/.
Seth Raphael30836422014-02-06 14:49:47 -0800387 startSmartCamera();
388 }
389
Michael Kolb8872c232013-01-29 10:33:22 -0800390 // Prompt the user to pick to record location for the very first run of
391 // camera only
392 private void locationFirstRun() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800393 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100394
Erin Dahlgren4569b702014-02-24 14:21:11 -0800395 if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION)) {
396 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800397 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800398 if (mActivity.isSecureCamera()) {
399 return;
400 }
Michael Kolb8872c232013-01-29 10:33:22 -0800401 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800402 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800403 if (backCameraId == -1) {
404 // If there is no back camera, do not show the prompt.
405 return;
406 }
Doris Liu6a83d522013-07-02 12:03:32 -0700407 mUI.showLocationDialog();
408 }
Michael Kolb8872c232013-01-29 10:33:22 -0800409
ztenghui7b265a62013-09-09 14:58:44 -0700410 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700411 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800412 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700413 }
414
415 @Override
416 public void onPreviewUIDestroyed() {
417 if (mCameraDevice == null) {
418 return;
419 }
420 mCameraDevice.setPreviewTexture(null);
421 stopPreview();
422 }
423
Doris Liu1dfe7822013-12-12 00:02:08 -0800424 @Override
425 public void startPreCaptureAnimation() {
426 mAppController.startPreCaptureAnimation();
427 }
428
Michael Kolbd6954f32013-03-08 20:43:01 -0800429 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800430 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800431 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800432 }
433
Michael Kolbd6954f32013-03-08 20:43:01 -0800434 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800435 if (mPaused) {
436 return;
437 }
Erin Dahlgren357b7672013-11-20 17:38:14 -0800438 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800439
440 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800441 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800442 mCameraId = mPendingSwitchCameraId;
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800443 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800444 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800445 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800446 if (mFocusManager != null) {
447 mFocusManager.removeMessages();
448 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800449
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700450 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700451 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700452 // Start switch camera animation. Post a message because
453 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800454 }
455
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800456 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100457 new ButtonManager.ButtonCallback() {
458 @Override
459 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800460 // At the time this callback is fired, the camera id
461 // has be set to the desired camera.
462
Angus Kong97e282a2014-03-04 18:44:49 -0800463 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100464 return;
465 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800466 // If switching to back camera, and HDR+ is still on,
467 // switch back to gcam, otherwise handle callback normally.
468 SettingsManager settingsManager = mActivity.getSettingsManager();
469 if (settingsManager.isCameraBackFacing()) {
470 if (settingsManager.requestsReturnToHdrPlus()) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700471 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800472 return;
473 }
474 }
475
Sascha Haeberlingde303232014-02-07 02:30:53 +0100476 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800477
Sascha Haeberlingde303232014-02-07 02:30:53 +0100478 Log.v(TAG, "Start to switch camera. cameraId=" + state);
479 // We need to keep a preview frame for the animation before
480 // releasing the camera. This will trigger
481 // onPreviewTextureCopied.
482 // TODO: Need to animate the camera switch
483 switchCamera();
484 }
485 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800486
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800487 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100488 new ButtonManager.ButtonCallback() {
489 @Override
490 public void onStateChanged(int state) {
491 if (GcamHelper.hasGcamCapture()) {
492 // Set the camera setting to default backfacing.
493 SettingsManager settingsManager = mActivity.getSettingsManager();
494 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700495 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100496 } else {
497 mSceneMode = CameraUtil.SCENE_MODE_HDR;
498 updateParametersSceneMode();
499 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800500 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100501 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800502
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800503 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
504 @Override
505 public void onClick(View v) {
506 onCaptureCancelled();
507 }
508 };
509
510 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
511 @Override
512 public void onClick(View v) {
513 onCaptureDone();
514 }
515 };
516
517 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
518 @Override
519 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800520 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800521 onCaptureRetake();
522 }
523 };
524
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800525 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800526 public HardwareSpec getHardwareSpec() {
Erin Dahlgren49ab9222014-01-28 17:40:28 -0800527 return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800528 }
529
530 @Override
531 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
532 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
533
534 bottomBarSpec.enableCamera = true;
535 bottomBarSpec.cameraCallback = mCameraCallback;
536 bottomBarSpec.enableFlash = true;
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800537 bottomBarSpec.enableHdr = true;
538 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800539 bottomBarSpec.enableGridLines = true;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800540
541 if (isImageCaptureIntent()) {
542 bottomBarSpec.showCancel = true;
543 bottomBarSpec.cancelCallback = mCancelCallback;
544 bottomBarSpec.showDone = true;
545 bottomBarSpec.doneCallback = mDoneCallback;
546 bottomBarSpec.showRetake = true;
547 bottomBarSpec.retakeCallback = mRetakeCallback;
548 }
549
550 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800551 }
552
Michael Kolbd6954f32013-03-08 20:43:01 -0800553 // either open a new camera or switch cameras
554 private void openCameraCommon() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800555 mUI.onCameraOpened(mParameters);
Angus Kong0fb819b2013-10-08 13:44:19 -0700556 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800557 // Set hdr plus to default: off.
558 SettingsManager settingsManager = mActivity.getSettingsManager();
559 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700560 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800561 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800562 }
563
ztenghui7b265a62013-09-09 14:58:44 -0700564 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800565 public void updatePreviewAspectRatio(float aspectRatio) {
566 mAppController.updatePreviewAspectRatio(aspectRatio);
567 }
568
Michael Kolb8872c232013-01-29 10:33:22 -0800569 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800570 SettingsManager settingsManager = mActivity.getSettingsManager();
571 if (settingsManager == null) {
572 Log.e(TAG, "Settings manager is null!");
573 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800574 }
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800575 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE);
Michael Kolb8872c232013-01-29 10:33:22 -0800576 }
577
Michael Kolb8872c232013-01-29 10:33:22 -0800578 // Snapshots can only be taken after this is called. It should be called
579 // once only. We could have done these things in onCreate() but we want to
580 // make preview screen appear as soon as possible.
581 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700582 if (mFirstTimeInitialized || mPaused) {
583 return;
584 }
Michael Kolb8872c232013-01-29 10:33:22 -0800585
Michael Kolbd6954f32013-03-08 20:43:01 -0800586 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800587
Angus Kong86d36312013-01-31 18:22:44 -0800588 // We set the listener only when both service and shutterbutton
589 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800590 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800591
Michael Kolb8872c232013-01-29 10:33:22 -0800592 mNamedImages = new NamedImages();
593
594 mFirstTimeInitialized = true;
595 addIdleHandler();
596
597 mActivity.updateStorageSpaceAndHint();
598 }
599
Michael Kolbd6954f32013-03-08 20:43:01 -0800600 // If the activity is paused and resumed, this method will be called in
601 // onResume.
602 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800603 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800604 mNamedImages = new NamedImages();
605 mUI.initializeSecondTime(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -0800606 }
607
Michael Kolb8872c232013-01-29 10:33:22 -0800608 private void addIdleHandler() {
609 MessageQueue queue = Looper.myQueue();
610 queue.addIdleHandler(new MessageQueue.IdleHandler() {
611 @Override
612 public boolean queueIdle() {
613 Storage.ensureOSXCompatible();
614 return false;
615 }
616 });
617 }
618
Sameer Padala2c8cc452013-11-05 18:49:12 -0800619 private void startSmartCamera() {
620 SmartCameraHelper.register(mCameraDevice, mParameters.getPreviewSize(), mActivity,
Doris Liu773e1c92013-12-02 17:35:03 -0800621 (ViewGroup) mActivity.findViewById(R.id.camera_app_root));
Sameer Padala2c8cc452013-11-05 18:49:12 -0800622 }
623
624 private void stopSmartCamera() {
625 SmartCameraHelper.tearDown();
626 }
627
Michael Kolb8872c232013-01-29 10:33:22 -0800628 @Override
629 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800630 if (mFaceDetectionStarted) {
631 return;
632 }
Michael Kolb8872c232013-01-29 10:33:22 -0800633 if (mParameters.getMaxNumDetectedFaces() > 0) {
634 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700635 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700636 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800637 mCameraDevice.startFaceDetection();
638 }
639 }
640
Michael Kolb8872c232013-01-29 10:33:22 -0800641 @Override
642 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800643 if (!mFaceDetectionStarted) {
644 return;
645 }
Michael Kolb8872c232013-01-29 10:33:22 -0800646 if (mParameters.getMaxNumDetectedFaces() > 0) {
647 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700648 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800649 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800650 mUI.clearFaces();
Michael Kolb8872c232013-01-29 10:33:22 -0800651 }
652 }
653
Michael Kolb8872c232013-01-29 10:33:22 -0800654 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700655 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700656
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800657 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700658
Sascha Haeberling37f36112013-08-06 14:31:52 -0700659 public ShutterCallback(boolean needsAnimation) {
660 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700661 }
662
Michael Kolb8872c232013-01-29 10:33:22 -0800663 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700664 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800665 mShutterCallbackTime = System.currentTimeMillis();
666 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
667 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700668 if (mNeedsAnimation) {
669 mActivity.runOnUiThread(new Runnable() {
670 @Override
671 public void run() {
672 animateAfterShutter();
673 }
674 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700675 }
Michael Kolb8872c232013-01-29 10:33:22 -0800676 }
677 }
678
Angus Kong9ef99252013-07-18 18:04:19 -0700679 private final class PostViewPictureCallback
680 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800681 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800682 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800683 mPostViewPictureCallbackTime = System.currentTimeMillis();
684 Log.v(TAG, "mShutterToPostViewCallbackTime = "
685 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
686 + "ms");
687 }
688 }
689
Angus Kong9ef99252013-07-18 18:04:19 -0700690 private final class RawPictureCallback
691 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800692 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800693 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800694 mRawPictureCallbackTime = System.currentTimeMillis();
695 Log.v(TAG, "mShutterToRawCallbackTime = "
696 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
697 }
698 }
699
Angus Kong9ef99252013-07-18 18:04:19 -0700700 private final class JpegPictureCallback
701 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800702 Location mLocation;
703
704 public JpegPictureCallback(Location loc) {
705 mLocation = loc;
706 }
707
708 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800709 public void onPictureTaken(final byte[] jpegData, CameraProxy camera) {
710 setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800711 if (mPaused) {
712 return;
713 }
Doris Liu6432cd62013-06-13 17:20:31 -0700714 if (mIsImageCaptureIntent) {
715 stopPreview();
716 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700717 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700718 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800719 }
720
721 mJpegPictureCallbackTime = System.currentTimeMillis();
722 // If postview callback has arrived, the captured image is displayed
723 // in postview callback. If not, the captured image is displayed in
724 // raw picture callback.
725 if (mPostViewPictureCallbackTime != 0) {
726 mShutterToPictureDisplayedTime =
727 mPostViewPictureCallbackTime - mShutterCallbackTime;
728 mPictureDisplayedToJpegCallbackTime =
729 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
730 } else {
731 mShutterToPictureDisplayedTime =
732 mRawPictureCallbackTime - mShutterCallbackTime;
733 mPictureDisplayedToJpegCallbackTime =
734 mJpegPictureCallbackTime - mRawPictureCallbackTime;
735 }
736 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
737 + mPictureDisplayedToJpegCallbackTime + "ms");
738
Michael Kolb8872c232013-01-29 10:33:22 -0800739 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
740 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700741 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800742 }
743
Doris Liu36e56fb2013-09-11 17:38:08 -0700744 ExifInterface exif = Exif.getExif(jpegData);
745 int orientation = Exif.getOrientation(exif);
Ruben Brunkd217ed02013-10-08 23:31:13 -0700746
Ruben Brunkd7488272013-10-10 18:45:53 -0700747 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800748 // Calculate the width and the height of the jpeg.
749 Size s = mParameters.getPictureSize();
Michael Kolb8872c232013-01-29 10:33:22 -0800750 int width, height;
751 if ((mJpegRotation + orientation) % 180 == 0) {
752 width = s.width;
753 height = s.height;
754 } else {
755 width = s.height;
756 height = s.width;
757 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700758 NamedEntity name = mNamedImages.getNextNameEntity();
759 String title = (name == null) ? null : name.title;
760 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -0700761
762 // Handle debug mode outputs
763 if (mDebugUri != null) {
764 // If using a debug uri, save jpeg there.
765 saveToDebugUri(jpegData);
766
767 // Adjust the title of the debug image shown in mediastore.
768 if (title != null) {
769 title = DEBUG_IMAGE_PREFIX + title;
770 }
771 }
772
Michael Kolb8872c232013-01-29 10:33:22 -0800773 if (title == null) {
774 Log.e(TAG, "Unbalanced name/data pair");
775 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800776 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800777 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800778 }
Angus Kong0d00a892013-03-26 11:40:40 -0700779 if (mHeading >= 0) {
780 // heading direction has been updated by the sensor.
781 ExifTag directionRefTag = exif.buildTag(
782 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
783 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
784 ExifTag directionTag = exif.buildTag(
785 ExifInterface.TAG_GPS_IMG_DIRECTION,
786 new Rational(mHeading, 1));
787 exif.setTag(directionRefTag);
788 exif.setTag(directionTag);
789 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800790 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -0800791 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700792 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800793 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800794 // Animate capture with real jpeg data instead of a preview
795 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -0700796 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800797 } else {
798 mJpegImageData = jpegData;
799 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -0700800 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800801 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800802 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800803 }
804 }
805
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800806 // Send the taken photo to remote shutter listeners, if any are registered.
807 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
808
Michael Kolb8872c232013-01-29 10:33:22 -0800809 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800810 // latency. It's true that someone else could write to the SD card
811 // in the mean time and fill it, but that could have happened
812 // between the shutter press and saving the JPEG too.
Michael Kolb8872c232013-01-29 10:33:22 -0800813 mActivity.updateStorageSpaceAndHint();
814
815 long now = System.currentTimeMillis();
816 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
817 Log.v(TAG, "mJpegCallbackFinishTime = "
818 + mJpegCallbackFinishTime + "ms");
819 mJpegPictureCallbackTime = 0;
820 }
821 }
822
Angus Kong9ef99252013-07-18 18:04:19 -0700823 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800824 @Override
825 public void onAutoFocus(
Angus Kong9ef99252013-07-18 18:04:19 -0700826 boolean focused, CameraProxy camera) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800827 if (mPaused) {
828 return;
829 }
Michael Kolb8872c232013-01-29 10:33:22 -0800830
831 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
832 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
833 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800834 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb8872c232013-01-29 10:33:22 -0800835 }
836 }
837
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700838 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -0800839 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700840 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800841 @Override
842 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700843 boolean moving, CameraProxy camera) {
844 mFocusManager.onAutoFocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -0800845 }
846 }
847
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700848 /**
849 * This class is just a thread-safe queue for name,date holder objects.
850 */
851 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800852 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -0800853
854 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700855 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -0800856 }
857
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700858 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -0800859 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700860 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -0800861 r.date = date;
862 mQueue.add(r);
863 }
864
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700865 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800866 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700867 if (!mQueue.isEmpty()) {
868 return mQueue.remove(0);
869 }
Michael Kolb8872c232013-01-29 10:33:22 -0800870 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700871 return null;
Michael Kolb8872c232013-01-29 10:33:22 -0800872 }
873
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700874 public static class NamedEntity {
875 public String title;
876 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -0800877 }
878 }
879
880 private void setCameraState(int state) {
881 mCameraState = state;
882 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -0800883 case PREVIEW_STOPPED:
884 case SNAPSHOT_IN_PROGRESS:
885 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -0800886 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700887 break;
888 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -0800889 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700890 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800891 }
892 }
893
Sascha Haeberling37f36112013-08-06 14:31:52 -0700894 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -0800895 // Only animate when in full screen capture mode
896 // i.e. If monkey/a user swipes to the gallery during picture taking,
897 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -0700898 if (!mIsImageCaptureIntent) {
899 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -0700900 }
Michael Kolb8872c232013-01-29 10:33:22 -0800901 }
902
903 @Override
904 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800905 // If we are already in the middle of taking a snapshot or the image
906 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -0800907 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800908 || mCameraState == SWITCHING_CAMERA || !mShutterEnabled) {
Michael Kolb8872c232013-01-29 10:33:22 -0800909 return false;
910 }
911 mCaptureStartTime = System.currentTimeMillis();
912 mPostViewPictureCallbackTime = 0;
913 mJpegImageData = null;
914
Angus Kongb50b5cb2013-08-09 14:55:20 -0700915 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -0800916
917 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700918 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -0800919 }
920
921 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800922 int orientation;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800923
Doris Liu3cf565c2013-02-15 10:55:37 -0800924 // We need to be consistent with the framework orientation (i.e. the
925 // orientation of the UI.) when the auto-rotate screen setting is on.
926 if (mActivity.isAutoRotateScreen()) {
927 orientation = (360 - mDisplayRotation) % 360;
928 } else {
929 orientation = mOrientation;
930 }
Sascha Haeberlinga7cbfc02014-02-14 11:06:03 +0100931 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
932 mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800933 mParameters.setRotation(mJpegRotation);
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -0800934 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700935 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -0800936 mCameraDevice.setParameters(mParameters);
937
Sascha Haeberling88901942013-08-28 17:49:00 -0700938 // We don't want user to press the button again while taking a
939 // multi-second HDR photo.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800940 setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -0700941 mCameraDevice.takePicture(mHandler,
942 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -0700943 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -0700944 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -0800945
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700946 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -0800947
948 mFaceDetectionStarted = false;
949 setCameraState(SNAPSHOT_IN_PROGRESS);
Seth Raphael5e09d012013-12-18 13:45:03 -0800950 UsageStatistics.captureEvent(eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
951 UsageStatistics.hashFileName(mNamedImages.mQueue.lastElement().title + ".jpg"),
Andy Huibersf7d4eba2014-03-25 19:06:52 -0700952 mParameters, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800953 return true;
954 }
955
956 @Override
957 public void setFocusParameters() {
958 setCameraParameters(UPDATE_PARAM_PREFERENCE);
959 }
960
Michael Kolbd6954f32013-03-08 20:43:01 -0800961 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -0800962 // If scene mode is set, we cannot set flash mode, white balance, and
963 // focus mode, instead, we read it from driver
964 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
965 overrideCameraSettings(mParameters.getFlashMode(),
966 mParameters.getWhiteBalance(), mParameters.getFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -0800967 }
968 }
969
970 private void overrideCameraSettings(final String flashMode,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800971 final String whiteBalance, final String focusMode) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800972 SettingsManager settingsManager = mActivity.getSettingsManager();
973 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode);
974 settingsManager.set(SettingsManager.SETTING_WHITE_BALANCE, whiteBalance);
975 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -0800976 }
977
978 @Override
Michael Kolb8872c232013-01-29 10:33:22 -0800979 public void onOrientationChanged(int orientation) {
980 // We keep the last known orientation. So if the user first orient
981 // the camera then point the camera to floor or sky, we still have
982 // the correct orientation.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800983 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
984 return;
985 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700986 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800987 }
988
989 @Override
Angus Kong20fad242013-11-11 18:23:46 -0800990 public void onCameraAvailable(CameraProxy cameraProxy) {
991 if (mPaused) {
992 return;
993 }
994 mCameraDevice = cameraProxy;
995
Erin Dahlgren357b7672013-11-20 17:38:14 -0800996 resetExposureCompensation();
Angus Kong20fad242013-11-11 18:23:46 -0800997 initializeCapabilities();
998
999 // Reset zoom value index.
1000 mZoomValue = 0;
1001 if (mFocusManager == null) {
1002 initializeFocusManager();
1003 }
1004 mFocusManager.setParameters(mInitialParams);
1005
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001006 // Do camera parameter dependent initialization.
Angus Kong20fad242013-11-11 18:23:46 -08001007 mParameters = mCameraDevice.getParameters();
1008 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001009 // Set a listener which updates camera parameters based
1010 // on changed settings.
1011 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren1648c362014-01-06 15:06:04 -08001012 settingsManager.addListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001013 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001014
Angus Kong20fad242013-11-11 18:23:46 -08001015 startPreview();
1016
1017 onCameraOpened();
1018 }
1019
1020 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001021 public void onCaptureCancelled() {
1022 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1023 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001024 }
1025
Michael Kolbd6954f32013-03-08 20:43:01 -08001026 @Override
1027 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001028 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001029 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001030 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001031 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001032 setupPreview();
1033 }
1034
Michael Kolbd6954f32013-03-08 20:43:01 -08001035 @Override
1036 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001037 if (mPaused) {
1038 return;
1039 }
1040
1041 byte[] data = mJpegImageData;
1042
1043 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001044 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001045 // caller specifies a "save uri" then write the data to its
1046 // stream. Otherwise, pass back a scaled down version of the bitmap
1047 // directly in the extras.
1048 if (mSaveUri != null) {
1049 OutputStream outputStream = null;
1050 try {
1051 outputStream = mContentResolver.openOutputStream(mSaveUri);
1052 outputStream.write(data);
1053 outputStream.close();
1054
1055 mActivity.setResultEx(Activity.RESULT_OK);
1056 mActivity.finish();
1057 } catch (IOException ex) {
1058 // ignore exception
1059 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001060 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001061 }
1062 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001063 ExifInterface exif = Exif.getExif(data);
1064 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001065 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1066 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001067 mActivity.setResultEx(Activity.RESULT_OK,
1068 new Intent("inline-data").putExtra("data", bitmap));
1069 mActivity.finish();
1070 }
1071 } else {
1072 // Save the image to a temp file and invoke the cropper
1073 Uri tempUri = null;
1074 FileOutputStream tempStream = null;
1075 try {
1076 File path = mActivity.getFileStreamPath(sTempCropFilename);
1077 path.delete();
1078 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1079 tempStream.write(data);
1080 tempStream.close();
1081 tempUri = Uri.fromFile(path);
1082 } catch (FileNotFoundException ex) {
1083 mActivity.setResultEx(Activity.RESULT_CANCELED);
1084 mActivity.finish();
1085 return;
1086 } catch (IOException ex) {
1087 mActivity.setResultEx(Activity.RESULT_CANCELED);
1088 mActivity.finish();
1089 return;
1090 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001091 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001092 }
1093
1094 Bundle newExtras = new Bundle();
1095 if (mCropValue.equals("circle")) {
1096 newExtras.putString("circleCrop", "true");
1097 }
1098 if (mSaveUri != null) {
1099 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1100 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001101 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001102 }
1103 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001104 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001105 }
1106
Sascha Haeberling37f36112013-08-06 14:31:52 -07001107 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001108 final String CROP_ACTION = "com.android.camera.action.CROP";
1109 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001110
1111 cropIntent.setData(tempUri);
1112 cropIntent.putExtras(newExtras);
1113
1114 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1115 }
1116 }
1117
Michael Kolb8872c232013-01-29 10:33:22 -08001118 @Override
1119 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001120 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001121 }
1122
1123 @Override
1124 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001125 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001126 || (mCameraState == PREVIEW_STOPPED)) {
1127 return;
1128 }
Michael Kolb8872c232013-01-29 10:33:22 -08001129
1130 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001131 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001132 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001133 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001134 return;
1135 }
1136 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1137
Angus Kongb50b5cb2013-08-09 14:55:20 -07001138 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001139 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001140 }
Michael Kolb8872c232013-01-29 10:33:22 -08001141 // If the user wants to do a snapshot while the previous one is still
1142 // in progress, remember the fact and do it after we finish the previous
1143 // one and re-start the preview. Snapshot in progress also includes the
1144 // state that autofocus is focusing and a picture will be taken when
1145 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001146 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1147 if (!mIsImageCaptureIntent) {
1148 mSnapshotOnIdle = true;
1149 }
Michael Kolb8872c232013-01-29 10:33:22 -08001150 return;
1151 }
1152
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001153 mSnapshotOnIdle = false;
Angus Kongeaaf56d2014-02-20 11:59:10 -08001154 mFocusManager.focusAndCapture();
Michael Kolb8872c232013-01-29 10:33:22 -08001155 }
1156
Andy Huibersdef975d2013-11-22 09:13:39 -08001157 private void onResumeTasks() {
1158 Log.v(TAG, "Executing onResumeTasks.");
Angus Kong20fad242013-11-11 18:23:46 -08001159 mActivity.getCameraProvider().requestCamera(mCameraId);
1160
Michael Kolb8872c232013-01-29 10:33:22 -08001161 mJpegPictureCallbackTime = 0;
1162 mZoomValue = 0;
Angus Kong20fad242013-11-11 18:23:46 -08001163
1164 mOnResumeTime = SystemClock.uptimeMillis();
1165 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001166
1167 // If first time initialization is not finished, put it in the
1168 // message queue.
1169 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001170 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001171 } else {
1172 initializeSecondTime();
1173 }
Michael Kolb8872c232013-01-29 10:33:22 -08001174
Seth Raphael5e09d012013-12-18 13:45:03 -08001175 UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1176 eventprotos.CameraEvent.InteractionCause.BUTTON);
Angus Kong0d00a892013-03-26 11:40:40 -07001177
1178 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1179 if (gsensor != null) {
1180 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1181 }
1182
1183 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1184 if (msensor != null) {
1185 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1186 }
Michael Kolb8872c232013-01-29 10:33:22 -08001187 }
1188
Angus Kongc4e66562013-11-22 23:03:21 -08001189 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001190 * @return Whether the currently active camera is front-facing.
1191 */
1192 private boolean isCameraFrontFacing() {
1193 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1194 return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1195 }
1196
1197 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001198 * The focus manager is the first UI related element to get initialized, and
1199 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001200 */
1201 private void initializeFocusManager() {
1202 // Create FocusManager object. startPreview needs it.
1203 // if mFocusManager not null, reuse it
1204 // otherwise create a new instance
1205 if (mFocusManager != null) {
1206 mFocusManager.removeMessages();
1207 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001208 mMirror = isCameraFrontFacing();
Angus Kongc4e66562013-11-22 23:03:21 -08001209 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1210 R.array.pref_camera_focusmode_default_array);
Erin Dahlgren357b7672013-11-20 17:38:14 -08001211 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1212 defaultFocusModes,
Angus Kongc4e66562013-11-22 23:03:21 -08001213 mInitialParams, this, mMirror,
Doris Liu482de022013-12-18 19:18:16 -08001214 mActivity.getMainLooper(), mUI.getFocusUI());
Angus Kongc4e66562013-11-22 23:03:21 -08001215 }
Doris Liu482de022013-12-18 19:18:16 -08001216 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001217 }
1218
1219 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001220 public void resume() {
1221 mPaused = false;
Doris Liu482de022013-12-18 19:18:16 -08001222 if (mFocusManager != null) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001223 // If camera is not open when resume is called, focus manager will
1224 // not
Doris Liu15b99612013-12-21 11:32:28 -08001225 // be initialized yet, in which case it will start listening to
1226 // preview area size change later in the initialization.
Doris Liu482de022013-12-18 19:18:16 -08001227 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1228 }
Doris Liu59401042014-01-14 17:51:32 -08001229
1230 if (mUI.getPreviewAreaSizeChangedListener() != null) {
1231 mAppController.addPreviewAreaSizeChangedListener(
1232 mUI.getPreviewAreaSizeChangedListener());
1233 }
1234
Angus Kongc4e66562013-11-22 23:03:21 -08001235 // Add delay on resume from lock screen only, in order to to speed up
1236 // the onResume --> onPause --> onResume cycle from lock screen.
1237 // Don't do always because letting go of thread can cause delay.
1238 String action = mActivity.getIntent().getAction();
1239 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1240 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1241 Log.v(TAG, "On resume, from lock screen.");
1242 // Note: onPauseAfterSuper() will delete this runnable, so we will
1243 // at most have 1 copy queued up.
1244 mHandler.postDelayed(new Runnable() {
Sameer Padala2c8cc452013-11-05 18:49:12 -08001245 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001246 public void run() {
1247 onResumeTasks();
1248 }
1249 }, ON_RESUME_TASKS_DELAY_MSEC);
1250 } else {
1251 Log.v(TAG, "On resume.");
1252 onResumeTasks();
1253 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001254 getServices().getRemoteShutterListener().onModuleReady(this);
Angus Kongc4e66562013-11-22 23:03:21 -08001255 }
1256
1257 @Override
1258 public void pause() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001259 getServices().getRemoteShutterListener().onModuleExit();
Michael Kolb8872c232013-01-29 10:33:22 -08001260 mPaused = true;
Angus Kong0d00a892013-03-26 11:40:40 -07001261 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1262 if (gsensor != null) {
1263 mSensorManager.unregisterListener(this, gsensor);
1264 }
1265
1266 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1267 if (msensor != null) {
1268 mSensorManager.unregisterListener(this, msensor);
1269 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001270
Michael Kolb8872c232013-01-29 10:33:22 -08001271 // Reset the focus first. Camera CTS does not guarantee that
1272 // cancelAutoFocus is allowed after preview stops.
1273 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1274 mCameraDevice.cancelAutoFocus();
1275 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001276
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001277 // If the camera has not been opened asynchronously yet,
1278 // and startPreview hasn't been called, then this is a no-op.
1279 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001280 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001281
Angus Kongce5480e2013-01-29 17:43:48 -08001282 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001283
Michael Kolb8872c232013-01-29 10:33:22 -08001284 // If we are in an image capture intent and has taken
1285 // a picture, we just clear it in onPause.
1286 mJpegImageData = null;
1287
Angus Kongdcccc512013-08-08 17:06:03 -07001288 // Remove the messages and runnables in the queue.
1289 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001290
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001291 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001292 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001293 mUI.onPause();
1294
Michael Kolb8872c232013-01-29 10:33:22 -08001295 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001296 if (mFocusManager != null) {
1297 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001298 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001299 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001300 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu59401042014-01-14 17:51:32 -08001301 if (mUI.getPreviewAreaSizeChangedListener() != null) {
1302 mAppController.removePreviewAreaSizeChangedListener(
1303 mUI.getPreviewAreaSizeChangedListener());
1304 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001305
1306 SettingsManager settingsManager = mActivity.getSettingsManager();
1307 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001308 }
1309
Angus Kong20fad242013-11-11 18:23:46 -08001310 @Override
1311 public void destroy() {
1312 // TODO: implement this.
1313 }
1314
1315 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001316 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001317 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001318 }
1319
1320 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001321 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001322 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001323 setDisplayOrientation();
1324 }
1325 }
1326
Michael Kolb8872c232013-01-29 10:33:22 -08001327 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001328 return isCameraIdle()
1329 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001330 }
1331
1332 @Override
1333 public void autoFocus() {
1334 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001335 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001336 setCameraState(FOCUSING);
1337 }
1338
1339 @Override
1340 public void cancelAutoFocus() {
1341 mCameraDevice.cancelAutoFocus();
1342 setCameraState(IDLE);
1343 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1344 }
1345
Michael Kolb8872c232013-01-29 10:33:22 -08001346 @Override
1347 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001348 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1349 || mCameraState == SNAPSHOT_IN_PROGRESS
1350 || mCameraState == SWITCHING_CAMERA
1351 || mCameraState == PREVIEW_STOPPED) {
1352 return;
1353 }
1354
1355 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001356 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1357 return;
1358 }
Doris Liu482de022013-12-18 19:18:16 -08001359 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001360 }
1361
1362 @Override
1363 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001364 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001365 }
1366
1367 @Override
1368 public boolean onKeyDown(int keyCode, KeyEvent event) {
1369 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001370 case KeyEvent.KEYCODE_VOLUME_UP:
1371 case KeyEvent.KEYCODE_VOLUME_DOWN:
1372 case KeyEvent.KEYCODE_FOCUS:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001373 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001374 if (event.getRepeatCount() == 0) {
1375 onShutterButtonFocus(true);
1376 }
1377 return true;
1378 }
1379 return false;
1380 case KeyEvent.KEYCODE_CAMERA:
1381 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1382 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001383 }
1384 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001385 case KeyEvent.KEYCODE_DPAD_CENTER:
1386 // If we get a dpad center event without any focused view, move
1387 // the focus to the shutter button and press it.
1388 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1389 // Start auto-focus immediately to reduce shutter lag. After
1390 // the shutter button gets the focus, onShutterButtonFocus()
1391 // will be called again but it is fine.
1392 onShutterButtonFocus(true);
1393 mUI.pressShutterButton();
1394 }
1395 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001396 }
1397 return false;
1398 }
1399
1400 @Override
1401 public boolean onKeyUp(int keyCode, KeyEvent event) {
1402 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001403 case KeyEvent.KEYCODE_VOLUME_UP:
1404 case KeyEvent.KEYCODE_VOLUME_DOWN:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001405 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001406 onShutterButtonClick();
1407 return true;
1408 }
1409 return false;
1410 case KeyEvent.KEYCODE_FOCUS:
1411 if (mFirstTimeInitialized) {
1412 onShutterButtonFocus(false);
1413 }
Michael Kolb8872c232013-01-29 10:33:22 -08001414 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001415 }
1416 return false;
1417 }
1418
Michael Kolb8872c232013-01-29 10:33:22 -08001419 private void closeCamera() {
1420 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001421 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001422 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001423 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001424 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001425
Michael Kolb8872c232013-01-29 10:33:22 -08001426 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001427 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001428 mCameraDevice = null;
1429 setCameraState(PREVIEW_STOPPED);
1430 mFocusManager.onCameraReleased();
1431 }
1432 }
1433
1434 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001435 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1436 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001437 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001438 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001439 if (mFocusManager != null) {
1440 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1441 }
Doris Liu6432cd62013-06-13 17:20:31 -07001442 // Change the camera display orientation
1443 if (mCameraDevice != null) {
1444 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1445 }
Michael Kolb8872c232013-01-29 10:33:22 -08001446 }
1447
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001448 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001449 private void setupPreview() {
1450 mFocusManager.resetTouchFocus();
1451 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001452 }
1453
Angus Kong20fad242013-11-11 18:23:46 -08001454 /**
1455 * Returns whether we can/should start the preview or not.
1456 */
1457 private boolean checkPreviewPreconditions() {
1458 if (mPaused) {
1459 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001460 }
Michael Kolb8872c232013-01-29 10:33:22 -08001461
Angus Kong20fad242013-11-11 18:23:46 -08001462 if (mCameraDevice == null) {
1463 Log.w(TAG, "startPreview: camera device not ready yet.");
1464 return false;
1465 }
1466
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001467 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001468 if (st == null) {
1469 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001470 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001471 }
1472
1473 if (!mCameraPreviewParamsReady) {
1474 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001475 return false;
1476 }
1477 return true;
1478 }
1479
1480 /**
1481 * The start/stop preview should only run on the UI thread.
1482 */
1483 private void startPreview() {
1484 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001485 return;
1486 }
Angus Kong20fad242013-11-11 18:23:46 -08001487
Angus Kong2bca2102014-03-11 16:27:30 -07001488 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001489 // ICS camera frameworks has a bug. Face detection state is not cleared
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001490 // after taking a picture. Stop the preview to work around it. The bug
1491 // was fixed in JB.
1492 if (mCameraState != PREVIEW_STOPPED) {
1493 stopPreview();
1494 }
1495
1496 setDisplayOrientation();
1497
1498 if (!mSnapshotOnIdle) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001499 // If the focus mode is continuous autofocus, call cancelAutoFocus
1500 // to resume it because it may have been paused by autoFocus call.
Erin Dahlgren357b7672013-11-20 17:38:14 -08001501 String focusMode = mFocusManager.getFocusMode();
1502 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001503 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001504 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001505 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1506 }
1507 setCameraParameters(UPDATE_PARAM_ALL);
1508 // Let UI set its expected aspect ratio
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001509 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001510
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001511 Log.v(TAG, "startPreview");
1512 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001513
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001514 mFocusManager.onPreviewStarted();
1515 onPreviewStarted();
Michael Kolb8872c232013-01-29 10:33:22 -08001516
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001517 if (mSnapshotOnIdle) {
1518 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001519 }
1520 }
1521
Michael Kolbd6954f32013-03-08 20:43:01 -08001522 @Override
1523 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001524 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1525 Log.v(TAG, "stopPreview");
1526 mCameraDevice.stopPreview();
1527 mFaceDetectionStarted = false;
1528 }
1529 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001530 if (mFocusManager != null) {
1531 mFocusManager.onPreviewStopped();
1532 }
Sameer Padala2c8cc452013-11-05 18:49:12 -08001533 stopSmartCamera();
Michael Kolb8872c232013-01-29 10:33:22 -08001534 }
1535
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001536 @Override
Erin Dahlgren1648c362014-01-06 15:06:04 -08001537 public void onSettingChanged(SettingsManager settingsManager, int id) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001538 switch (id) {
1539 case SettingsManager.SETTING_FLASH_MODE: {
1540 updateParametersFlashMode();
1541 break;
1542 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001543 default: {
1544 // Do nothing.
1545 }
1546 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001547
1548 if (mCameraDevice != null) {
1549 mCameraDevice.setParameters(mParameters);
1550 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001551 }
1552
Michael Kolb8872c232013-01-29 10:33:22 -08001553 private void updateCameraParametersInitialize() {
1554 // Reset preview frame rate to the maximum because it may be lowered by
1555 // video camera application.
ztenghui16a35202013-09-23 11:35:36 -07001556 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mParameters);
1557 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001558 mParameters.setPreviewFpsRange(
1559 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1560 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001561 }
1562
Angus Kongb50b5cb2013-08-09 14:55:20 -07001563 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001564
1565 // Disable video stabilization. Convenience methods not available in API
1566 // level <= 14
1567 String vstabSupported = mParameters.get("video-stabilization-supported");
1568 if ("true".equals(vstabSupported)) {
1569 mParameters.set("video-stabilization", "false");
1570 }
1571 }
1572
1573 private void updateCameraParametersZoom() {
1574 // Set zoom.
1575 if (mParameters.isZoomSupported()) {
1576 mParameters.setZoom(mZoomValue);
1577 }
1578 }
1579
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001580 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001581 private void setAutoExposureLockIfSupported() {
1582 if (mAeLockSupported) {
1583 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1584 }
1585 }
1586
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001587 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001588 private void setAutoWhiteBalanceLockIfSupported() {
1589 if (mAwbLockSupported) {
1590 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1591 }
1592 }
1593
Michael Kolb8872c232013-01-29 10:33:22 -08001594 private void setFocusAreasIfSupported() {
1595 if (mFocusAreaSupported) {
1596 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1597 }
1598 }
1599
Michael Kolb8872c232013-01-29 10:33:22 -08001600 private void setMeteringAreasIfSupported() {
1601 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001602 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1603 }
1604 }
1605
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001606 private void updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08001607 setAutoExposureLockIfSupported();
1608 setAutoWhiteBalanceLockIfSupported();
1609 setFocusAreasIfSupported();
1610 setMeteringAreasIfSupported();
1611
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001612 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07001613 mFocusManager.overrideFocusMode(null);
1614 mParameters.setFocusMode(mFocusManager.getFocusMode());
1615
Michael Kolb8872c232013-01-29 10:33:22 -08001616 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001617 updateParametersPictureSize();
1618
1619 // Set JPEG quality.
1620 updateParametersPictureQuality();
1621
1622 // For the following settings, we need to check if the settings are
1623 // still supported by latest driver, if not, ignore the settings.
1624
1625 // Set exposure compensation
1626 updateParametersExposureCompensation();
1627
1628 // Set the scene mode: also sets flash and white balance.
1629 updateParametersSceneMode();
1630
1631 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1632 updateAutoFocusMoveCallback();
1633 }
1634 }
1635
1636 private void updateParametersPictureSize() {
1637 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001638 String pictureSize = settingsManager
1639 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
1640 : SettingsManager.SETTING_PICTURE_SIZE_BACK);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001641
1642 List<Size> supported = mParameters.getSupportedPictureSizes();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001643 SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
1644 mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001645 Size size = mParameters.getPictureSize();
1646
1647 // Set a preview size that is closest to the viewfinder height and has
1648 // the right aspect ratio.
1649 List<Size> sizes = mParameters.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001650 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Michael Kolb8872c232013-01-29 10:33:22 -08001651 (double) size.width / size.height);
1652 Size original = mParameters.getPreviewSize();
1653 if (!original.equals(optimalSize)) {
1654 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
Doris Liu6432cd62013-06-13 17:20:31 -07001655
Michael Kolb8872c232013-01-29 10:33:22 -08001656 // Zoom related settings will be changed for different preview
1657 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07001658 if (mHandler.getLooper() == Looper.myLooper()) {
1659 // On UI thread only, not when camera starts up
1660 setupPreview();
1661 } else {
1662 mCameraDevice.setParameters(mParameters);
1663 }
Michael Kolb8872c232013-01-29 10:33:22 -08001664 mParameters = mCameraDevice.getParameters();
1665 }
Doris Liu95405742013-11-05 15:25:26 -08001666
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001667 if (optimalSize.width != 0 && optimalSize.height != 0) {
Doris Liu95405742013-11-05 15:25:26 -08001668 mUI.updatePreviewAspectRatio((float) optimalSize.width
1669 / (float) optimalSize.height);
1670 }
Michael Kolb8872c232013-01-29 10:33:22 -08001671 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001672 }
Michael Kolb8872c232013-01-29 10:33:22 -08001673
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001674 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08001675 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1676 CameraProfile.QUALITY_HIGH);
1677 mParameters.setJpegQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001678 }
Michael Kolb8872c232013-01-29 10:33:22 -08001679
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001680 private void updateParametersExposureCompensation() {
1681 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolb8872c232013-01-29 10:33:22 -08001682
Erin Dahlgren635a4b82013-11-25 15:21:18 -08001683 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE));
Michael Kolb8872c232013-01-29 10:33:22 -08001684 int max = mParameters.getMaxExposureCompensation();
1685 int min = mParameters.getMinExposureCompensation();
1686 if (value >= min && value <= max) {
1687 mParameters.setExposureCompensation(value);
1688 } else {
1689 Log.w(TAG, "invalid exposure range: " + value);
1690 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001691 }
1692
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001693 private void updateParametersSceneMode() {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001694 SettingsManager settingsManager = mActivity.getSettingsManager();
1695
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001696 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001697 if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
1698 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1699 mParameters.setSceneMode(mSceneMode);
1700
1701 // Setting scene mode will change the settings of flash mode,
1702 // white balance, and focus mode. Here we read back the
1703 // parameters, so we can know those settings.
1704 mCameraDevice.setParameters(mParameters);
1705 mParameters = mCameraDevice.getParameters();
1706 }
1707 } else {
1708 mSceneMode = mParameters.getSceneMode();
1709 if (mSceneMode == null) {
1710 mSceneMode = Parameters.SCENE_MODE_AUTO;
1711 }
1712 }
1713
Michael Kolb8872c232013-01-29 10:33:22 -08001714 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1715 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001716 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001717
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001718 // Set white balance mode.
1719 updateParametersWhiteBalanceMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001720
1721 // Set focus mode.
1722 mFocusManager.overrideFocusMode(null);
1723 mParameters.setFocusMode(mFocusManager.getFocusMode());
1724 } else {
1725 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1726 }
Michael Kolb8872c232013-01-29 10:33:22 -08001727 }
1728
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001729 private void updateParametersFlashMode() {
1730 SettingsManager settingsManager = mActivity.getSettingsManager();
1731
1732 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
1733 List<String> supportedFlash = mParameters.getSupportedFlashModes();
1734 if (CameraUtil.isSupported(flashMode, supportedFlash)) {
1735 mParameters.setFlashMode(flashMode);
1736 }
1737 }
1738
1739 private void updateParametersWhiteBalanceMode() {
1740 SettingsManager settingsManager = mActivity.getSettingsManager();
1741
1742 // Set white balance parameter.
1743 String whiteBalance = settingsManager.get(SettingsManager.SETTING_WHITE_BALANCE);
1744 if (CameraUtil.isSupported(whiteBalance,
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001745 mParameters.getSupportedWhiteBalance())) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001746 mParameters.setWhiteBalance(whiteBalance);
1747 }
1748 }
1749
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001750 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001751 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001752 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07001753 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07001754 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001755 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07001756 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001757 }
1758 }
1759
1760 // We separate the parameters into several subsets, so we can update only
1761 // the subsets actually need updating. The PREFERENCE set needs extra
1762 // locking because the preference can be changed from GLThread as well.
1763 private void setCameraParameters(int updateSet) {
1764 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1765 updateCameraParametersInitialize();
1766 }
1767
1768 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1769 updateCameraParametersZoom();
1770 }
1771
1772 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001773 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08001774 }
1775
1776 mCameraDevice.setParameters(mParameters);
1777 }
1778
1779 // If the Camera is idle, update the parameters immediately, otherwise
1780 // accumulate them in mUpdateSet and update later.
1781 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1782 mUpdateSet |= additionalUpdateSet;
1783 if (mCameraDevice == null) {
1784 // We will update all the parameters when we open the device, so
1785 // we don't need to do anything now.
1786 mUpdateSet = 0;
1787 return;
1788 } else if (isCameraIdle()) {
1789 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001790 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001791 mUpdateSet = 0;
1792 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08001793 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1794 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08001795 }
1796 }
1797 }
1798
ztenghui7b265a62013-09-09 14:58:44 -07001799 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001800 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001801 return (mCameraState == IDLE) ||
1802 (mCameraState == PREVIEW_STOPPED) ||
1803 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001804 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08001805 }
1806
ztenghui7b265a62013-09-09 14:58:44 -07001807 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001808 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001809 String action = mActivity.getIntent().getAction();
1810 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001811 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08001812 }
1813
1814 private void setupCaptureParams() {
1815 Bundle myExtras = mActivity.getIntent().getExtras();
1816 if (myExtras != null) {
1817 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1818 mCropValue = myExtras.getString("crop");
1819 }
1820 }
1821
Michael Kolb8872c232013-01-29 10:33:22 -08001822 private void initializeCapabilities() {
1823 mInitialParams = mCameraDevice.getParameters();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001824 mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams);
1825 mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams);
1826 mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams);
1827 mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams);
Angus Kongdcccc512013-08-08 17:06:03 -07001828 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07001829 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08001830 }
1831
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001832 private void setShutterEnabled(boolean enabled) {
1833 mShutterEnabled = enabled;
1834 mUI.enableShutter(enabled);
1835 }
1836
Marco Nelissen0744e4a2013-11-22 01:47:37 +00001837 // TODO: Remove this
Michael Kolb8872c232013-01-29 10:33:22 -08001838 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001839 public int onZoomChanged(int index) {
1840 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001841 if (mPaused) {
1842 return index;
1843 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001844 mZoomValue = index;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001845 if (mParameters == null || mCameraDevice == null) {
1846 return index;
1847 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001848 // Set zoom parameters asynchronously
1849 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07001850 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08001851 Parameters p = mCameraDevice.getParameters();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001852 if (p != null) {
1853 return p.getZoom();
1854 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001855 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08001856 }
1857
1858 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001859 public int getCameraState() {
1860 return mCameraState;
1861 }
1862
1863 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001864 public void onMemoryStateChanged(int state) {
1865 setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08001866 }
Angus Kong86d36312013-01-31 18:22:44 -08001867
1868 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001869 public void onLowMemory() {
1870 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08001871 }
Angus Kong0d00a892013-03-26 11:40:40 -07001872
1873 @Override
1874 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1875 }
1876
1877 @Override
1878 public void onSensorChanged(SensorEvent event) {
1879 int type = event.sensor.getType();
1880 float[] data;
1881 if (type == Sensor.TYPE_ACCELEROMETER) {
1882 data = mGData;
1883 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
1884 data = mMData;
1885 } else {
1886 // we should not be here.
1887 return;
1888 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001889 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07001890 data[i] = event.values[i];
1891 }
1892 float[] orientation = new float[3];
1893 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
1894 SensorManager.getOrientation(mR, orientation);
1895 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
1896 if (mHeading < 0) {
1897 mHeading += 360;
1898 }
Angus Kong0d00a892013-03-26 11:40:40 -07001899 }
Doris Liu6432cd62013-06-13 17:20:31 -07001900
Ruben Brunkd217ed02013-10-08 23:31:13 -07001901 // For debugging only.
1902 public void setDebugUri(Uri uri) {
1903 mDebugUri = uri;
1904 }
1905
1906 // For debugging only.
1907 private void saveToDebugUri(byte[] data) {
1908 if (mDebugUri != null) {
1909 OutputStream outputStream = null;
1910 try {
1911 outputStream = mContentResolver.openOutputStream(mDebugUri);
1912 outputStream.write(data);
1913 outputStream.close();
1914 } catch (IOException e) {
1915 Log.e(TAG, "Exception while writing debug jpeg file", e);
1916 } finally {
1917 CameraUtil.closeSilently(outputStream);
1918 }
1919 }
1920 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001921
1922 @Override
1923 public void onRemoteShutterPress() {
1924 capture();
1925 }
Michael Kolb8872c232013-01-29 10:33:22 -08001926}