blob: 8f42014bddb6d0621690d537923b60090a862ef1 [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;
Angus Kong454d63f2014-05-06 14:45:59 -070025import android.graphics.BitmapFactory;
Michael Kolb8872c232013-01-29 10:33:22 -080026import android.graphics.SurfaceTexture;
Angus Kong0d00a892013-03-26 11:40:40 -070027import android.hardware.Sensor;
28import android.hardware.SensorEvent;
29import android.hardware.SensorEventListener;
30import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080031import android.location.Location;
32import android.media.CameraProfile;
33import android.net.Uri;
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -070034import android.os.AsyncTask;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070035import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080036import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080037import android.os.Handler;
38import android.os.Looper;
39import android.os.Message;
40import android.os.MessageQueue;
41import android.os.SystemClock;
42import android.provider.MediaStore;
Michael Kolb8872c232013-01-29 10:33:22 -080043import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080044import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080045import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080046
Sameer Padala2c8cc452013-11-05 18:49:12 -080047import com.android.camera.PhotoModule.NamedImages.NamedEntity;
48import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080049import com.android.camera.app.CameraAppUI;
Angus Kong0b9eb5b2014-04-30 15:03:33 -070050import com.android.camera.app.CameraProvider;
Sascha Haeberlingf9cba4e2014-04-22 09:11:28 -070051import com.android.camera.app.MediaSaver;
52import com.android.camera.app.MemoryManager;
53import com.android.camera.app.MemoryManager.MemoryListener;
Kevin Gabayanfb333362014-06-02 14:48:20 -070054import com.android.camera.app.MotionManager;
Angus Kong2bca2102014-03-11 16:27:30 -070055import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070056import com.android.camera.exif.ExifInterface;
57import com.android.camera.exif.ExifTag;
58import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080059import com.android.camera.hardware.HardwareSpec;
60import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080061import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080062import com.android.camera.remote.RemoteCameraModule;
Andy Huibers90c7ad52014-05-20 22:13:54 -070063import com.android.camera.settings.CameraPictureSizesCacher;
Erin Dahlgren6190c362014-06-13 14:12:08 -070064import com.android.camera.settings.Keys;
Angus Kong454d63f2014-05-06 14:45:59 -070065import com.android.camera.settings.ResolutionUtil;
Erin Dahlgren357b7672013-11-20 17:38:14 -080066import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010067import com.android.camera.settings.SettingsUtil;
Doris Liu6c751642014-05-05 18:43:26 -070068import com.android.camera.ui.CountDownView;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070069import com.android.camera.ui.TouchCoordinate;
Angus Kongdcccc512013-08-08 17:06:03 -070070import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070071import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070072import com.android.camera.util.GcamHelper;
Sol Boucher44ce4b22014-08-04 23:41:38 -070073import com.android.camera.util.GservicesHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070074import com.android.camera.util.SessionStatsCollector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070075import com.android.camera.util.UsageStatistics;
Doris Liudb8f9752014-05-12 15:25:13 -070076import com.android.camera.widget.AspectRatioSelector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070077import com.android.camera2.R;
Spike Sprague3c3b31d2014-09-08 10:43:04 -070078import com.android.ex.camera2.portability.CameraAgent;
Sol Boucher5a344962014-06-17 14:05:08 -070079import com.android.ex.camera2.portability.CameraAgent.CameraAFCallback;
80import com.android.ex.camera2.portability.CameraAgent.CameraAFMoveCallback;
81import com.android.ex.camera2.portability.CameraAgent.CameraPictureCallback;
82import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
83import com.android.ex.camera2.portability.CameraAgent.CameraShutterCallback;
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -070084import com.android.ex.camera2.portability.CameraCapabilities;
Sol Boucher43e18132014-06-19 15:03:18 -070085import com.android.ex.camera2.portability.CameraDeviceInfo.Characteristics;
Sol Boucher5a344962014-06-17 14:05:08 -070086import com.android.ex.camera2.portability.CameraSettings;
87import com.android.ex.camera2.portability.Size;
Seth Raphael5e09d012013-12-18 13:45:03 -080088import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080089
Angus Kong454d63f2014-05-06 14:45:59 -070090import java.io.ByteArrayOutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070091import java.io.File;
92import java.io.FileNotFoundException;
93import java.io.FileOutputStream;
94import java.io.IOException;
95import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010096import java.lang.ref.WeakReference;
Angus Kong831347d2014-06-16 16:07:28 -070097import java.util.ArrayList;
Angus Kongdcccc512013-08-08 17:06:03 -070098import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070099import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -0700100
Michael Kolb8872c232013-01-29 10:33:22 -0800101public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800102 extends CameraModule
103 implements PhotoController,
104 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800105 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800106 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -0800107 SensorEventListener,
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800108 SettingsManager.OnSettingChangedListener,
Doris Liu6c751642014-05-05 18:43:26 -0700109 RemoteCameraModule,
110 CountDownView.OnCountDownStatusListener {
Michael Kolb8872c232013-01-29 10:33:22 -0800111
Alan Newberger00a390e2014-08-18 18:40:47 -0700112 public static final String PHOTO_MODULE_STRING_ID = "PhotoModule";
Erin Dahlgren6190c362014-06-13 14:12:08 -0700113
114 private static final Log.Tag TAG = new Log.Tag(PHOTO_MODULE_STRING_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800115
116 // We number the request code from 1000 to avoid collision with Gallery.
117 private static final int REQUEST_CROP = 1000;
118
Angus Kong13e87c42013-11-25 10:02:47 -0800119 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800120 private static final int MSG_FIRST_TIME_INIT = 1;
121 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800122
123 // The subset of parameters we need to update in setCameraParameters().
124 private static final int UPDATE_PARAM_INITIALIZE = 1;
125 private static final int UPDATE_PARAM_ZOOM = 2;
126 private static final int UPDATE_PARAM_PREFERENCE = 4;
127 private static final int UPDATE_PARAM_ALL = -1;
128
Andy Huibersdef975d2013-11-22 09:13:39 -0800129 // This is the delay before we execute onResume tasks when coming
130 // from the lock screen, to allow time for onPause to execute.
131 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800132
Ruben Brunkd7488272013-10-10 18:45:53 -0700133 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
134
Michael Kolb8872c232013-01-29 10:33:22 -0800135 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800136 private CameraProxy mCameraDevice;
137 private int mCameraId;
Angus Kong88289042014-04-22 16:39:42 -0700138 private CameraCapabilities mCameraCapabilities;
Angus Kong6607dae2014-06-10 16:07:45 -0700139 private CameraSettings mCameraSettings;
Michael Kolb8872c232013-01-29 10:33:22 -0800140 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800141
142 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800143
Michael Kolb8872c232013-01-29 10:33:22 -0800144 // The activity is going to switch to the specified camera id. This is
145 // needed because texture copy is done in GL thread. -1 means camera is not
146 // switching.
147 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800148
149 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
150 // needed to be updated in mUpdateSet.
151 private int mUpdateSet;
152
Sol Boucher2192fba2014-08-19 17:24:07 -0700153 private float mZoomValue; // The current zoom ratio.
Andy Huibers547d7c82014-05-20 23:03:18 -0700154 private int mTimerDuration;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700155 /** Set when a volume button is clicked to take photo */
156 private boolean mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800157
Michael Kolb8872c232013-01-29 10:33:22 -0800158 private boolean mFocusAreaSupported;
159 private boolean mMeteringAreaSupported;
160 private boolean mAeLockSupported;
161 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700162 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800163
Puneet Lall70a96522014-09-11 17:33:21 -0700164 /*
165 * If true, attempts to start the preview will be denied. This ensures that
166 * we never call startPreview multiple times when making changes to
167 * settings.
168 */
169 private boolean mStartPreviewLock = false;
170
Michael Kolb8872c232013-01-29 10:33:22 -0800171 // The degrees of the device rotated clockwise from its natural orientation.
172 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800173
174 private static final String sTempCropFilename = "crop-temp";
175
Michael Kolb8872c232013-01-29 10:33:22 -0800176 private boolean mFaceDetectionStarted = false;
177
Michael Kolb8872c232013-01-29 10:33:22 -0800178 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
179 private String mCropValue;
180 private Uri mSaveUri;
181
Ruben Brunkd217ed02013-10-08 23:31:13 -0700182 private Uri mDebugUri;
183
Angus Kongce5480e2013-01-29 17:43:48 -0800184 // We use a queue to generated names of the images to be used later
185 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800186 private NamedImages mNamedImages;
187
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800188 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800189 @Override
190 public void run() {
191 onShutterButtonClick();
192 }
193 };
194
Michael Kolb8872c232013-01-29 10:33:22 -0800195 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800196 * An unpublished intent flag requesting to return as soon as capturing is
197 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800198 */
199 private static final String EXTRA_QUICK_CAPTURE =
200 "android.intent.extra.quickCapture";
201
202 // The display rotation in degrees. This is only valid when mCameraState is
203 // not PREVIEW_STOPPED.
204 private int mDisplayRotation;
205 // The value for android.hardware.Camera.setDisplayOrientation.
206 private int mCameraDisplayOrientation;
207 // The value for UI components like indicators.
208 private int mDisplayOrientation;
Angus Kong831347d2014-06-16 16:07:28 -0700209 // The value for cameradevice.CameraSettings.setPhotoRotationDegrees.
Michael Kolb8872c232013-01-29 10:33:22 -0800210 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700211 // Indicates whether we are using front camera
212 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800213 private boolean mFirstTimeInitialized;
214 private boolean mIsImageCaptureIntent;
215
Michael Kolb8872c232013-01-29 10:33:22 -0800216 private int mCameraState = PREVIEW_STOPPED;
217 private boolean mSnapshotOnIdle = false;
218
219 private ContentResolver mContentResolver;
220
Doris Liu2b906b82013-12-10 16:34:08 -0800221 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800222
Michael Kolb8872c232013-01-29 10:33:22 -0800223 private final PostViewPictureCallback mPostViewPictureCallback =
224 new PostViewPictureCallback();
225 private final RawPictureCallback mRawPictureCallback =
226 new RawPictureCallback();
227 private final AutoFocusCallback mAutoFocusCallback =
228 new AutoFocusCallback();
229 private final Object mAutoFocusMoveCallback =
230 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700231 ? new AutoFocusMoveCallback()
232 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800233
234 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
235
236 private long mFocusStartTime;
237 private long mShutterCallbackTime;
238 private long mPostViewPictureCallbackTime;
239 private long mRawPictureCallbackTime;
240 private long mJpegPictureCallbackTime;
241 private long mOnResumeTime;
242 private byte[] mJpegImageData;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700243 /** Touch coordinate for shutter button press. */
244 private TouchCoordinate mShutterTouchCoordinate;
245
Michael Kolb8872c232013-01-29 10:33:22 -0800246
247 // These latency time are for the CameraLatency test.
248 public long mAutoFocusTime;
249 public long mShutterLag;
250 public long mShutterToPictureDisplayedTime;
251 public long mPictureDisplayedToJpegCallbackTime;
252 public long mJpegCallbackFinishTime;
253 public long mCaptureStartTime;
254
255 // This handles everything about focus.
256 private FocusOverlayManager mFocusManager;
257
Doris Liubd1b8f92014-01-03 17:59:51 -0800258 private final int mGcamModeIndex;
Andy Huibersa31162c2014-09-02 11:27:11 -0700259 private SoundPlayer mCountdownSoundPlayer;
Doris Liubd1b8f92014-01-03 17:59:51 -0800260
Angus Kong6607dae2014-06-10 16:07:45 -0700261 private CameraCapabilities.SceneMode mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800262
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100263 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800264
Michael Kolb8872c232013-01-29 10:33:22 -0800265 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700266 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800267 private final float[] mGData = new float[3];
268 private final float[] mMData = new float[3];
269 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700270 private int mHeading = -1;
271
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800272 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700273 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700274
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800275 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800276 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800277 @Override
278 public void onMediaSaved(Uri uri) {
279 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700280 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800281 }
282 }
283 };
Angus Kong454d63f2014-05-06 14:45:59 -0700284 private boolean mShouldResizeTo16x9 = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800285
Angus Kong0b9eb5b2014-04-30 15:03:33 -0700286 private final Runnable mResumeTaskRunnable = new Runnable() {
287 @Override
288 public void run() {
289 onResumeTasks();
290 }
291 };
292
Doris Liudb8f9752014-05-12 15:25:13 -0700293 /**
Angus Kong3699c412014-05-23 15:31:09 -0700294 * We keep the flash setting before entering scene modes (HDR)
295 * and restore it after HDR is off.
296 */
297 private String mFlashModeBeforeSceneMode;
298
299 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700300 * This callback gets called when user select whether or not to
301 * turn on geo-tagging.
302 */
303 public interface LocationDialogCallback {
304 /**
305 * Gets called after user selected/unselected geo-tagging feature.
306 *
307 * @param selected whether or not geo-tagging feature is selected
308 */
309 public void onLocationTaggingSelected(boolean selected);
310 }
311
312 /**
313 * This callback defines the text that is shown in the aspect ratio selection
314 * dialog, provides the current aspect ratio, and gets notified when user changes
315 * aspect ratio selection in the dialog.
316 */
317 public interface AspectRatioDialogCallback {
318 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700319 * Returns current aspect ratio that is being used to set as default.
320 */
321 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
322
323 /**
324 * Gets notified when user has made the aspect ratio selection.
325 *
326 * @param newAspectRatio aspect ratio that user has selected
Doris Liu08b0cdd2014-05-13 19:19:55 -0700327 * @param dialogHandlingFinishedRunnable runnable to run when the operations
328 * needed to handle changes from dialog
329 * are finished.
Doris Liudb8f9752014-05-12 15:25:13 -0700330 */
Doris Liu08b0cdd2014-05-13 19:19:55 -0700331 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
332 Runnable dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700333 }
334
Angus Kongdcccc512013-08-08 17:06:03 -0700335 private void checkDisplayRotation() {
336 // Set the display orientation if display rotation has changed.
337 // Sometimes this happens when the device is held upside
338 // down and camera app is opened. Rotation animation will
339 // take some time and the rotation value we have got may be
340 // wrong. Framework does not have a callback for this now.
341 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
342 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800343 }
Angus Kongdcccc512013-08-08 17:06:03 -0700344 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
345 mHandler.postDelayed(new Runnable() {
346 @Override
347 public void run() {
348 checkDisplayRotation();
349 }
350 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800351 }
352 }
353
354 /**
355 * This Handler is used to post message back onto the main thread of the
356 * application
357 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100358 private static class MainHandler extends Handler {
359 private final WeakReference<PhotoModule> mModule;
360
361 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800362 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100363 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800364 }
365
Michael Kolb8872c232013-01-29 10:33:22 -0800366 @Override
367 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100368 PhotoModule module = mModule.get();
369 if (module == null) {
370 return;
371 }
Michael Kolb8872c232013-01-29 10:33:22 -0800372 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800373 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100374 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800375 break;
376 }
377
Angus Kong13e87c42013-11-25 10:02:47 -0800378 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100379 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800380 break;
381 }
Michael Kolb8872c232013-01-29 10:33:22 -0800382 }
383 }
384 }
385
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700386 private void switchToGcamCapture() {
387 if (mActivity != null && mGcamModeIndex != 0) {
388 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700389 settingsManager.set(SettingsManager.SCOPE_GLOBAL,
390 Keys.KEY_CAMERA_HDR_PLUS, true);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700391
392 // Disable the HDR+ button to prevent callbacks from being
393 // queued before the correct callback is attached to the button
394 // in the new module. The new module will set the enabled/disabled
395 // of this button when the module's preferred camera becomes available.
396 ButtonManager buttonManager = mActivity.getButtonManager();
Spike Sprague66d3d0d2014-08-18 14:11:33 -0700397
398 buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700399
Seth Raphael274f6e92014-05-21 17:11:53 -0700400 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
401
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700402 // Do not post this to avoid this module switch getting interleaved with
403 // other button callbacks.
404 mActivity.onModeSelected(mGcamModeIndex);
Spike Sprague66d3d0d2014-08-18 14:11:33 -0700405
406 buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700407 }
408 }
409
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800410 /**
411 * Constructs a new photo module.
412 */
Angus Kongc4e66562013-11-22 23:03:21 -0800413 public PhotoModule(AppController app) {
414 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800415 mGcamModeIndex = app.getAndroidContext().getResources()
416 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800417 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700418
Michael Kolb8872c232013-01-29 10:33:22 -0800419 @Override
Spike Sprague159e6e92014-05-27 18:26:30 -0700420 public String getPeekAccessibilityString() {
421 return mAppController.getAndroidContext()
422 .getResources().getString(R.string.photo_accessibility_peek);
423 }
424
425 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -0700426 public String getModuleStringIdentifier() {
427 return PHOTO_MODULE_STRING_ID;
428 }
429
430 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100431 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
432 mActivity = activity;
433 // TODO: Need to look at the controller interface to see if we can get
434 // rid of passing in the activity directly.
435 mAppController = mActivity;
436
437 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
438 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800439
440 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700441 mCameraId = settingsManager.getInteger(mAppController.getModuleScope(),
442 Keys.KEY_CAMERA_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800443
Doris Liudb8f9752014-05-12 15:25:13 -0700444 // TODO: Move this to SettingsManager as a part of upgrade procedure.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700445 if (!settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
446 Keys.KEY_USER_SELECTED_ASPECT_RATIO)) {
Doris Liudb8f9752014-05-12 15:25:13 -0700447 // Switch to back camera to set aspect ratio.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700448 mCameraId = settingsManager.getIntegerDefault(Keys.KEY_CAMERA_ID);
Doris Liudb8f9752014-05-12 15:25:13 -0700449 }
450
Michael Kolb8872c232013-01-29 10:33:22 -0800451 mContentResolver = mActivity.getContentResolver();
452
Michael Kolb8872c232013-01-29 10:33:22 -0800453 // Surface texture is from camera screen nail and startPreview needs it.
454 // This must be done before startPreview.
455 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800456
Michael Kolb8872c232013-01-29 10:33:22 -0800457 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800458 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Doris Liu6c751642014-05-05 18:43:26 -0700459 mUI.setCountdownFinishedListener(this);
Andy Huibersa31162c2014-09-02 11:27:11 -0700460 mCountdownSoundPlayer = new SoundPlayer(mAppController.getAndroidContext());
Doris Liu6c751642014-05-05 18:43:26 -0700461
462 // TODO: Make this a part of app controller API.
463 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
464 cancelButton.setOnClickListener(new View.OnClickListener() {
465 @Override
466 public void onClick(View view) {
467 cancelCountDown();
468 }
469 });
470 }
471
472 private void cancelCountDown() {
473 if (mUI.isCountingDown()) {
474 // Cancel on-going countdown.
475 mUI.cancelCountDown();
476 }
477 mAppController.getCameraAppUI().transitionToCapture();
478 mAppController.getCameraAppUI().showModeOptions();
Michael Kolbd6954f32013-03-08 20:43:01 -0800479 }
480
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800481 @Override
482 public boolean isUsingBottomBar() {
483 return true;
484 }
485
Michael Kolbd6954f32013-03-08 20:43:01 -0800486 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800487 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800488 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800489 setupCaptureParams();
490 }
491 }
492
493 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800494 mAppController.onPreviewStarted();
Alan Newbergerffc395d2014-09-10 13:07:18 -0700495 mAppController.setShutterEnabled(true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800496 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800497 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700498 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800499 }
500
Doris Liudb8f9752014-05-12 15:25:13 -0700501 /**
502 * Prompt the user to pick to record location and choose aspect ratio for the
503 * very first run of camera only.
504 */
505 private void settingsFirstRun() {
506 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100507
Doris Liudb8f9752014-05-12 15:25:13 -0700508 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800509 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800510 }
Doris Liudb8f9752014-05-12 15:25:13 -0700511
Erin Dahlgren6190c362014-06-13 14:12:08 -0700512 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SCOPE_GLOBAL,
513 Keys.KEY_RECORD_LOCATION);
Doris Liudb8f9752014-05-12 15:25:13 -0700514 boolean aspectRatioPrompt = !settingsManager.getBoolean(
Erin Dahlgren6190c362014-06-13 14:12:08 -0700515 SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO);
Doris Liudb8f9752014-05-12 15:25:13 -0700516 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800517 return;
518 }
Doris Liudb8f9752014-05-12 15:25:13 -0700519
Michael Kolb8872c232013-01-29 10:33:22 -0800520 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800521 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800522 if (backCameraId == -1) {
523 // If there is no back camera, do not show the prompt.
524 return;
525 }
Doris Liudb8f9752014-05-12 15:25:13 -0700526
527 if (locationPrompt) {
528 // Show both location and aspect ratio selection dialog.
529 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700530 @Override
Doris Liudb8f9752014-05-12 15:25:13 -0700531 public void onLocationTaggingSelected(boolean selected) {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700532 Keys.setLocation(mActivity.getSettingsManager(), selected,
533 mActivity.getLocationManager());
Doris Liudb8f9752014-05-12 15:25:13 -0700534 }
535 }, createAspectRatioDialogCallback());
536 } else {
537 // App upgrade. Only show aspect ratio selection.
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700538 boolean wasShown = mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
539 if (!wasShown) {
540 // If the dialog was not shown, set this flag to true so that we
541 // never have to check for it again. It means that we don't need
542 // to show the dialog on this device.
543 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
544 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
545 }
Doris Liudb8f9752014-05-12 15:25:13 -0700546 }
547 }
548
549 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -0700550 Size currentSize = mCameraSettings.getCurrentPhotoSize();
Doris Liudb8f9752014-05-12 15:25:13 -0700551 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
552 if (aspectRatio < 1f) {
553 aspectRatio = 1 / aspectRatio;
554 }
555 final AspectRatioSelector.AspectRatio currentAspectRatio;
556 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
557 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
558 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
559 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
560 } else {
561 // TODO: Log error and not show dialog.
562 return null;
563 }
564
Angus Kong6607dae2014-06-10 16:07:45 -0700565 List<Size> sizes = mCameraCapabilities.getSupportedPhotoSizes();
Doris Liudb8f9752014-05-12 15:25:13 -0700566 List<Size> pictureSizes = ResolutionUtil
567 .getDisplayableSizesFromSupported(sizes, true);
568
569 // This logic below finds the largest resolution for each aspect ratio.
570 // TODO: Move this somewhere that can be shared with SettingsActivity
571 int aspectRatio4x3Resolution = 0;
572 int aspectRatio16x9Resolution = 0;
573 Size largestSize4x3 = new Size(0, 0);
574 Size largestSize16x9 = new Size(0, 0);
575 for (Size size : pictureSizes) {
576 float pictureAspectRatio = (float) size.width() / (float) size.height();
577 pictureAspectRatio = pictureAspectRatio < 1 ?
578 1f / pictureAspectRatio : pictureAspectRatio;
579 int resolution = size.width() * size.height();
580 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
581 if (resolution > aspectRatio4x3Resolution) {
582 aspectRatio4x3Resolution = resolution;
583 largestSize4x3 = size;
584 }
585 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
586 if (resolution > aspectRatio16x9Resolution) {
587 aspectRatio16x9Resolution = resolution;
588 largestSize16x9 = size;
589 }
590 }
591 }
Doris Liudb8f9752014-05-12 15:25:13 -0700592
Doris Liu08b0cdd2014-05-13 19:19:55 -0700593 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
594 final Size size4x3ToSelect = largestSize4x3;
595 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700596
Doris Liudb8f9752014-05-12 15:25:13 -0700597 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
Doris Liudb8f9752014-05-12 15:25:13 -0700598
599 @Override
600 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
601 return currentAspectRatio;
602 }
603
604 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700605 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
606 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700607 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700608 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700609 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
610 Keys.KEY_PICTURE_SIZE_BACK,
611 largestSize4x3Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700612 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700613 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700614 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
615 Keys.KEY_PICTURE_SIZE_BACK,
616 largestSize16x9Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700617 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700618 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
619 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
620 String aspectRatio = mActivity.getSettingsManager().getString(
621 SettingsManager.SCOPE_GLOBAL,
622 Keys.KEY_USER_SELECTED_ASPECT_RATIO);
623 Log.e(TAG, "aspect ratio after setting it to true=" + aspectRatio);
Doris Liudb8f9752014-05-12 15:25:13 -0700624 if (newAspectRatio != currentAspectRatio) {
Doris Liudb8f9752014-05-12 15:25:13 -0700625 stopPreview();
626 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700627 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
628 } else {
629 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700630 }
631 }
632 };
633 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700634 }
Michael Kolb8872c232013-01-29 10:33:22 -0800635
ztenghui7b265a62013-09-09 14:58:44 -0700636 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700637 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800638 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700639 }
640
641 @Override
642 public void onPreviewUIDestroyed() {
643 if (mCameraDevice == null) {
644 return;
645 }
646 mCameraDevice.setPreviewTexture(null);
647 stopPreview();
648 }
649
Doris Liu1dfe7822013-12-12 00:02:08 -0800650 @Override
651 public void startPreCaptureAnimation() {
652 mAppController.startPreCaptureAnimation();
653 }
654
Michael Kolbd6954f32013-03-08 20:43:01 -0800655 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800656 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800657 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800658 }
659
Michael Kolbd6954f32013-03-08 20:43:01 -0800660 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800661 if (mPaused) {
662 return;
663 }
Doris Liu6c751642014-05-05 18:43:26 -0700664 cancelCountDown();
Doris Liu6809eb82014-05-21 13:16:59 -0700665
666 mAppController.freezeScreenUntilPreviewReady();
Erin Dahlgren357b7672013-11-20 17:38:14 -0800667 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800668
Alan Newbergerd41766f2014-04-09 18:25:34 -0700669 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800670 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800671 mCameraId = mPendingSwitchCameraId;
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700672
Erin Dahlgren6190c362014-06-13 14:12:08 -0700673 settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId);
Sol Boucher44ce4b22014-08-04 23:41:38 -0700674 requestCameraOpen();
Michael Kolbd6954f32013-03-08 20:43:01 -0800675 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800676 if (mFocusManager != null) {
677 mFocusManager.removeMessages();
678 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800679
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700680 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700681 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700682 // Start switch camera animation. Post a message because
683 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800684 }
685
Sol Boucher44ce4b22014-08-04 23:41:38 -0700686 /**
687 * Uses the {@link CameraProvider} to open the currently-selected camera
688 * device, using {@link GservicesHelper} to choose between API-1 and API-2.
689 */
690 private void requestCameraOpen() {
Alan Newberger29a009c2014-09-05 12:47:42 -0700691 Log.v(TAG, "requestCameraOpen");
Sol Boucher44ce4b22014-08-04 23:41:38 -0700692 mActivity.getCameraProvider().requestCamera(mCameraId,
693 GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity));
694 }
695
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800696 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100697 new ButtonManager.ButtonCallback() {
698 @Override
699 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800700 // At the time this callback is fired, the camera id
701 // has be set to the desired camera.
702
Angus Kong97e282a2014-03-04 18:44:49 -0800703 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100704 return;
705 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800706 // If switching to back camera, and HDR+ is still on,
707 // switch back to gcam, otherwise handle callback normally.
708 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700709 if (Keys.isCameraBackFacing(settingsManager,
710 mAppController.getModuleScope())) {
711 if (Keys.requestsReturnToHdrPlus(settingsManager,
712 mAppController.getModuleScope())) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700713 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800714 return;
715 }
716 }
717
Sascha Haeberlingde303232014-02-07 02:30:53 +0100718 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800719
Alan Newbergerd41766f2014-04-09 18:25:34 -0700720 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100721 // We need to keep a preview frame for the animation before
722 // releasing the camera. This will trigger
723 // onPreviewTextureCopied.
724 // TODO: Need to animate the camera switch
725 switchCamera();
726 }
727 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800728
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800729 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100730 new ButtonManager.ButtonCallback() {
731 @Override
732 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700733 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700734 if (GcamHelper.hasGcamAsSeparateModule()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100735 // Set the camera setting to default backfacing.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700736 settingsManager.setToDefault(mAppController.getModuleScope(),
737 Keys.KEY_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700738 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100739 } else {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700740 if (Keys.isHdrOn(settingsManager)) {
Angus Kong831347d2014-06-16 16:07:28 -0700741 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
742 mCameraCapabilities.getStringifier().stringify(
743 CameraCapabilities.SceneMode.HDR));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700744 } else {
Angus Kong831347d2014-06-16 16:07:28 -0700745 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
746 mCameraCapabilities.getStringifier().stringify(
747 CameraCapabilities.SceneMode.AUTO));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700748 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100749 updateParametersSceneMode();
Angus Kong6607dae2014-06-10 16:07:45 -0700750 mCameraDevice.applySettings(mCameraSettings);
Doris Liu8ad8ad42014-03-27 14:43:31 -0700751 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100752 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800753 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100754 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800755
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800756 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
757 @Override
758 public void onClick(View v) {
759 onCaptureCancelled();
760 }
761 };
762
763 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
764 @Override
765 public void onClick(View v) {
766 onCaptureDone();
767 }
768 };
769
770 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
771 @Override
772 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800773 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800774 onCaptureRetake();
775 }
776 };
777
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800778 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700779 public void hardResetSettings(SettingsManager settingsManager) {
Erin Dahlgrene1547932014-06-23 15:17:07 -0700780 // PhotoModule should hard reset HDR+ to off,
781 // and HDR to off if HDR+ is supported.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700782 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700783 if (GcamHelper.hasGcamAsSeparateModule()) {
Erin Dahlgren21a62362014-06-24 10:13:01 -0700784 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false);
Erin Dahlgrene1547932014-06-23 15:17:07 -0700785 }
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700786 }
787
788 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800789 public HardwareSpec getHardwareSpec() {
Angus Kong831347d2014-06-16 16:07:28 -0700790 return (mCameraSettings != null ?
791 new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800792 }
793
794 @Override
795 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
796 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
797
798 bottomBarSpec.enableCamera = true;
799 bottomBarSpec.cameraCallback = mCameraCallback;
Erin Dahlgren6190c362014-06-13 14:12:08 -0700800 bottomBarSpec.enableFlash = !mAppController.getSettingsManager()
801 .getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR);
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800802 bottomBarSpec.enableHdr = true;
803 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800804 bottomBarSpec.enableGridLines = true;
Spike Sprague8f7425c2014-05-12 11:08:34 -0700805 if (mCameraCapabilities != null) {
806 bottomBarSpec.enableExposureCompensation = true;
807 bottomBarSpec.exposureCompensationSetCallback =
808 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
809 @Override
810 public void setExposure(int value) {
811 setExposureCompensation(value);
812 }
813 };
814 bottomBarSpec.minExposureCompensation =
815 mCameraCapabilities.getMinExposureCompensation();
816 bottomBarSpec.maxExposureCompensation =
817 mCameraCapabilities.getMaxExposureCompensation();
818 bottomBarSpec.exposureCompensationStep =
819 mCameraCapabilities.getExposureCompensationStep();
820 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800821
Doris Liu6c751642014-05-05 18:43:26 -0700822 bottomBarSpec.enableSelfTimer = true;
Sascha Haeberling4333fac2014-05-20 16:28:11 -0700823 bottomBarSpec.showSelfTimer = true;
Doris Liu6c751642014-05-05 18:43:26 -0700824
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800825 if (isImageCaptureIntent()) {
826 bottomBarSpec.showCancel = true;
827 bottomBarSpec.cancelCallback = mCancelCallback;
828 bottomBarSpec.showDone = true;
829 bottomBarSpec.doneCallback = mDoneCallback;
830 bottomBarSpec.showRetake = true;
831 bottomBarSpec.retakeCallback = mRetakeCallback;
832 }
833
834 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800835 }
836
Michael Kolbd6954f32013-03-08 20:43:01 -0800837 // either open a new camera or switch cameras
838 private void openCameraCommon() {
Angus Kong6607dae2014-06-10 16:07:45 -0700839 mUI.onCameraOpened(mCameraCapabilities, mCameraSettings);
Angus Kong0fb819b2013-10-08 13:44:19 -0700840 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800841 // Set hdr plus to default: off.
842 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700843 settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL,
844 Keys.KEY_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700845 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800846 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800847 }
848
ztenghui7b265a62013-09-09 14:58:44 -0700849 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800850 public void updatePreviewAspectRatio(float aspectRatio) {
851 mAppController.updatePreviewAspectRatio(aspectRatio);
852 }
853
Michael Kolb8872c232013-01-29 10:33:22 -0800854 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800855 SettingsManager settingsManager = mActivity.getSettingsManager();
856 if (settingsManager == null) {
857 Log.e(TAG, "Settings manager is null!");
858 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800859 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700860 settingsManager.setToDefault(mAppController.getCameraScope(),
861 Keys.KEY_EXPOSURE);
Michael Kolb8872c232013-01-29 10:33:22 -0800862 }
863
Michael Kolb8872c232013-01-29 10:33:22 -0800864 // Snapshots can only be taken after this is called. It should be called
865 // once only. We could have done these things in onCreate() but we want to
866 // make preview screen appear as soon as possible.
867 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700868 if (mFirstTimeInitialized || mPaused) {
869 return;
870 }
Michael Kolb8872c232013-01-29 10:33:22 -0800871
Michael Kolbd6954f32013-03-08 20:43:01 -0800872 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800873
Angus Kong86d36312013-01-31 18:22:44 -0800874 // We set the listener only when both service and shutterbutton
875 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800876 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800877
Michael Kolb8872c232013-01-29 10:33:22 -0800878 mNamedImages = new NamedImages();
879
880 mFirstTimeInitialized = true;
881 addIdleHandler();
882
Spike Spraguee6374b72014-04-25 17:24:32 -0700883 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800884 }
885
Michael Kolbd6954f32013-03-08 20:43:01 -0800886 // If the activity is paused and resumed, this method will be called in
887 // onResume.
888 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800889 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800890 mNamedImages = new NamedImages();
Angus Kong6607dae2014-06-10 16:07:45 -0700891 mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings);
Michael Kolbd6954f32013-03-08 20:43:01 -0800892 }
893
Michael Kolb8872c232013-01-29 10:33:22 -0800894 private void addIdleHandler() {
895 MessageQueue queue = Looper.myQueue();
896 queue.addIdleHandler(new MessageQueue.IdleHandler() {
897 @Override
898 public boolean queueIdle() {
899 Storage.ensureOSXCompatible();
900 return false;
901 }
902 });
903 }
904
Michael Kolb8872c232013-01-29 10:33:22 -0800905 @Override
906 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800907 if (mFaceDetectionStarted) {
908 return;
909 }
Angus Kong88289042014-04-22 16:39:42 -0700910 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800911 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700912 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700913 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800914 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700915 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800916 }
917 }
918
Michael Kolb8872c232013-01-29 10:33:22 -0800919 @Override
920 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800921 if (!mFaceDetectionStarted) {
922 return;
923 }
Angus Kong88289042014-04-22 16:39:42 -0700924 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800925 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700926 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800927 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800928 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700929 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800930 }
931 }
932
Michael Kolb8872c232013-01-29 10:33:22 -0800933 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700934 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700935
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800936 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700937
Sascha Haeberling37f36112013-08-06 14:31:52 -0700938 public ShutterCallback(boolean needsAnimation) {
939 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700940 }
941
Michael Kolb8872c232013-01-29 10:33:22 -0800942 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700943 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800944 mShutterCallbackTime = System.currentTimeMillis();
945 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
946 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700947 if (mNeedsAnimation) {
948 mActivity.runOnUiThread(new Runnable() {
949 @Override
950 public void run() {
951 animateAfterShutter();
952 }
953 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700954 }
Michael Kolb8872c232013-01-29 10:33:22 -0800955 }
956 }
957
Angus Kong9ef99252013-07-18 18:04:19 -0700958 private final class PostViewPictureCallback
959 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800960 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800961 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800962 mPostViewPictureCallbackTime = System.currentTimeMillis();
963 Log.v(TAG, "mShutterToPostViewCallbackTime = "
964 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
965 + "ms");
966 }
967 }
968
Angus Kong9ef99252013-07-18 18:04:19 -0700969 private final class RawPictureCallback
970 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800971 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800972 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800973 mRawPictureCallbackTime = System.currentTimeMillis();
974 Log.v(TAG, "mShutterToRawCallbackTime = "
975 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
976 }
977 }
978
Angus Kong454d63f2014-05-06 14:45:59 -0700979 private static class ResizeBundle {
980 byte[] jpegData;
981 float targetAspectRatio;
982 ExifInterface exif;
983 }
984
985 /**
986 * @return Cropped image if the target aspect ratio is larger than the jpeg
987 * aspect ratio on the long axis. The original jpeg otherwise.
988 */
989 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
990
991 final byte[] jpegData = dataBundle.jpegData;
992 final ExifInterface exif = dataBundle.exif;
993 float targetAspectRatio = dataBundle.targetAspectRatio;
994
995 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
996 int originalWidth = original.getWidth();
997 int originalHeight = original.getHeight();
998 int newWidth;
999 int newHeight;
1000
1001 if (originalWidth > originalHeight) {
1002 newHeight = (int) (originalWidth / targetAspectRatio);
1003 newWidth = originalWidth;
1004 } else {
1005 newWidth = (int) (originalHeight / targetAspectRatio);
1006 newHeight = originalHeight;
1007 }
1008 int xOffset = (originalWidth - newWidth)/2;
1009 int yOffset = (originalHeight - newHeight)/2;
1010
Andy Huibers0f54d282014-09-09 12:52:29 -07001011 // For some reason L needs this to work.
1012 // This code is only run on the Nexus 5.
1013 // TODO: Determine why this is needed.
1014 if (Build.VERSION.SDK_INT >= 21 || Build.VERSION.CODENAME.equals("L")) {
1015 Log.v(TAG,"xOffset = " + xOffset);
1016 Log.v(TAG,"yOffset = " + yOffset);
1017 xOffset *= 2;
1018 yOffset = 0;
1019 Log.v(TAG,"new xOffset = " + xOffset);
1020 Log.v(TAG,"new yOffset = " + yOffset);
1021 }
1022
Angus Kong454d63f2014-05-06 14:45:59 -07001023 if (xOffset < 0 || yOffset < 0) {
1024 return dataBundle;
1025 }
1026
1027 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
1028 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
1029 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
1030
1031 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1032
1033 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
1034 dataBundle.jpegData = stream.toByteArray();
1035 return dataBundle;
1036 }
1037
Angus Kong9ef99252013-07-18 18:04:19 -07001038 private final class JpegPictureCallback
1039 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001040 Location mLocation;
1041
1042 public JpegPictureCallback(Location loc) {
1043 mLocation = loc;
1044 }
1045
1046 @Override
Angus Kong454d63f2014-05-06 14:45:59 -07001047 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07001048 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001049 if (mPaused) {
1050 return;
1051 }
Doris Liu6432cd62013-06-13 17:20:31 -07001052 if (mIsImageCaptureIntent) {
1053 stopPreview();
1054 }
Angus Kong6607dae2014-06-10 16:07:45 -07001055 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001056 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001057 }
1058
1059 mJpegPictureCallbackTime = System.currentTimeMillis();
1060 // If postview callback has arrived, the captured image is displayed
1061 // in postview callback. If not, the captured image is displayed in
1062 // raw picture callback.
1063 if (mPostViewPictureCallbackTime != 0) {
1064 mShutterToPictureDisplayedTime =
1065 mPostViewPictureCallbackTime - mShutterCallbackTime;
1066 mPictureDisplayedToJpegCallbackTime =
1067 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
1068 } else {
1069 mShutterToPictureDisplayedTime =
1070 mRawPictureCallbackTime - mShutterCallbackTime;
1071 mPictureDisplayedToJpegCallbackTime =
1072 mJpegPictureCallbackTime - mRawPictureCallbackTime;
1073 }
1074 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
1075 + mPictureDisplayedToJpegCallbackTime + "ms");
1076
Michael Kolb8872c232013-01-29 10:33:22 -08001077 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
1078 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001079 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001080 }
1081
Angus Kong454d63f2014-05-06 14:45:59 -07001082 long now = System.currentTimeMillis();
1083 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1084 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1085 mJpegPictureCallbackTime = 0;
1086
1087 final ExifInterface exif = Exif.getExif(originalJpegData);
1088
1089 if (mShouldResizeTo16x9) {
1090 final ResizeBundle dataBundle = new ResizeBundle();
1091 dataBundle.jpegData = originalJpegData;
1092 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1093 dataBundle.exif = exif;
1094 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1095
1096 @Override
1097 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1098 return cropJpegDataToAspectRatio(resizeBundles[0]);
1099 }
1100
1101 @Override
1102 protected void onPostExecute(ResizeBundle result) {
1103 saveFinalPhoto(result.jpegData, result.exif, camera);
1104 }
1105 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1106
1107 } else {
1108 saveFinalPhoto(originalJpegData, exif, camera);
1109 }
1110 }
1111
1112 void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1113
Doris Liu36e56fb2013-09-11 17:38:08 -07001114 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001115
Sol Boucher2192fba2014-08-19 17:24:07 -07001116 float zoomValue = 1.0f;
Angus Kong88289042014-04-22 16:39:42 -07001117 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07001118 zoomValue = mCameraSettings.getCurrentZoomRatio();
Andy Huiberse08bc042014-04-11 19:26:47 -07001119 }
Angus Kong6607dae2014-06-10 16:07:45 -07001120 boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
Andy Huibers203abe52014-05-19 13:59:01 -07001121 String flashSetting =
Erin Dahlgren6190c362014-06-13 14:12:08 -07001122 mActivity.getSettingsManager().getString(mAppController.getCameraScope(),
1123 Keys.KEY_FLASH_MODE);
1124 boolean gridLinesOn = Keys.areGridLinesOn(mActivity.getSettingsManager());
Andy Huibers10c58162014-03-29 14:06:54 -07001125 UsageStatistics.instance().photoCaptureDoneEvent(
1126 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1127 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
Andy Huibers547d7c82014-05-20 23:03:18 -07001128 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001129 (float) mTimerDuration, mShutterTouchCoordinate, mVolumeButtonClickedFlag);
1130 mShutterTouchCoordinate = null;
1131 mVolumeButtonClickedFlag = false;
Ruben Brunkd217ed02013-10-08 23:31:13 -07001132
Ruben Brunkd7488272013-10-10 18:45:53 -07001133 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001134 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001135 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1136 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001137 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001138 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1139 width = exifWidth;
1140 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001141 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001142 Size s;
Angus Kong6607dae2014-06-10 16:07:45 -07001143 s = mCameraSettings.getCurrentPhotoSize();
Angus Kong454d63f2014-05-06 14:45:59 -07001144 if ((mJpegRotation + orientation) % 180 == 0) {
1145 width = s.width();
1146 height = s.height();
1147 } else {
1148 width = s.height();
1149 height = s.width();
1150 }
Michael Kolb8872c232013-01-29 10:33:22 -08001151 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001152 NamedEntity name = mNamedImages.getNextNameEntity();
1153 String title = (name == null) ? null : name.title;
1154 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001155
1156 // Handle debug mode outputs
1157 if (mDebugUri != null) {
1158 // If using a debug uri, save jpeg there.
1159 saveToDebugUri(jpegData);
1160
1161 // Adjust the title of the debug image shown in mediastore.
1162 if (title != null) {
1163 title = DEBUG_IMAGE_PREFIX + title;
1164 }
1165 }
1166
Michael Kolb8872c232013-01-29 10:33:22 -08001167 if (title == null) {
1168 Log.e(TAG, "Unbalanced name/data pair");
1169 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001170 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001171 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001172 }
Angus Kong0d00a892013-03-26 11:40:40 -07001173 if (mHeading >= 0) {
1174 // heading direction has been updated by the sensor.
1175 ExifTag directionRefTag = exif.buildTag(
1176 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1177 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1178 ExifTag directionTag = exif.buildTag(
1179 ExifInterface.TAG_GPS_IMG_DIRECTION,
1180 new Rational(mHeading, 1));
1181 exif.setTag(directionRefTag);
1182 exif.setTag(directionTag);
1183 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001184 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001185 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001186 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001187 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001188 // Animate capture with real jpeg data instead of a preview
1189 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001190 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001191 } else {
1192 mJpegImageData = jpegData;
1193 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001194 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001195 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001196 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001197 }
1198 }
1199
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001200 // Send the taken photo to remote shutter listeners, if any are
1201 // registered.
Sascha Haeberling3c6de142014-07-14 12:23:36 -07001202 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001203
Michael Kolb8872c232013-01-29 10:33:22 -08001204 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001205 // latency. It's true that someone else could write to the SD card
1206 // in the mean time and fill it, but that could have happened
1207 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001208 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001209 }
1210 }
1211
Angus Kong9ef99252013-07-18 18:04:19 -07001212 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001213 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001214 public void onAutoFocus(boolean focused, CameraProxy camera) {
1215 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001216 if (mPaused) {
1217 return;
1218 }
Michael Kolb8872c232013-01-29 10:33:22 -08001219
1220 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001221 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001222 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001223 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001224 }
1225 }
1226
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001227 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001228 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001229 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001230 @Override
1231 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001232 boolean moving, CameraProxy camera) {
1233 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001234 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001235 }
1236 }
1237
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001238 /**
1239 * This class is just a thread-safe queue for name,date holder objects.
1240 */
1241 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001242 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001243
1244 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001245 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001246 }
1247
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001248 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001249 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001250 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001251 r.date = date;
1252 mQueue.add(r);
1253 }
1254
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001255 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001256 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001257 if (!mQueue.isEmpty()) {
1258 return mQueue.remove(0);
1259 }
Michael Kolb8872c232013-01-29 10:33:22 -08001260 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001261 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001262 }
1263
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001264 public static class NamedEntity {
1265 public String title;
1266 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001267 }
1268 }
1269
1270 private void setCameraState(int state) {
1271 mCameraState = state;
1272 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001273 case PREVIEW_STOPPED:
1274 case SNAPSHOT_IN_PROGRESS:
1275 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001276 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001277 break;
1278 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001279 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001280 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001281 }
1282 }
1283
Sascha Haeberling37f36112013-08-06 14:31:52 -07001284 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001285 // Only animate when in full screen capture mode
1286 // i.e. If monkey/a user swipes to the gallery during picture taking,
1287 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001288 if (!mIsImageCaptureIntent) {
1289 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001290 }
Michael Kolb8872c232013-01-29 10:33:22 -08001291 }
1292
1293 @Override
1294 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001295 // If we are already in the middle of taking a snapshot or the image
1296 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001297 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -07001298 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -08001299 return false;
1300 }
1301 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001302
Michael Kolb8872c232013-01-29 10:33:22 -08001303 mPostViewPictureCallbackTime = 0;
1304 mJpegImageData = null;
1305
Angus Kong6607dae2014-06-10 16:07:45 -07001306 final boolean animateBefore = (mSceneMode == CameraCapabilities.SceneMode.HDR);
Michael Kolb8872c232013-01-29 10:33:22 -08001307
1308 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001309 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -08001310 }
1311
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -08001312 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kong6607dae2014-06-10 16:07:45 -07001313 CameraUtil.setGpsParameters(mCameraSettings, loc);
1314 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb8872c232013-01-29 10:33:22 -08001315
Senpo Hubb1c72f2014-09-08 01:07:27 -07001316 // Set JPEG orientation. Even if screen UI is locked in portrait, camera orientation should
1317 // still match device orientation (e.g., users should always get landscape photos while
1318 // capturing by putting device in landscape.)
1319 int orientation = mActivity.isAutoRotateScreen() ? mDisplayRotation : mOrientation;
1320 Characteristics info = mActivity.getCameraProvider().getCharacteristics(mCameraId);
1321 mJpegRotation = info.getJpegOrientation(orientation);
1322 mCameraDevice.setJpegOrientation(mJpegRotation);
1323
Sascha Haeberling88901942013-08-28 17:49:00 -07001324 // We don't want user to press the button again while taking a
1325 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -07001326 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -07001327 mCameraDevice.takePicture(mHandler,
1328 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001329 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001330 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001331
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001332 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001333
1334 mFaceDetectionStarted = false;
1335 setCameraState(SNAPSHOT_IN_PROGRESS);
1336 return true;
1337 }
1338
1339 @Override
1340 public void setFocusParameters() {
1341 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1342 }
1343
Michael Kolbd6954f32013-03-08 20:43:01 -08001344 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001345 // If scene mode is set, we cannot set flash mode, white balance, and
1346 // focus mode, instead, we read it from driver
Angus Kong6607dae2014-06-10 16:07:45 -07001347 if (CameraCapabilities.SceneMode.AUTO != mSceneMode) {
1348 overrideCameraSettings(mCameraSettings.getCurrentFlashMode(),
1349 mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001350 }
1351 }
1352
Angus Kong6607dae2014-06-10 16:07:45 -07001353 private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode,
1354 CameraCapabilities.FocusMode focusMode) {
1355 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrene419b192013-12-03 13:10:27 -08001356 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07001357 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE,
1358 stringifier.stringify(flashMode));
1359 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE,
1360 stringifier.stringify(focusMode));
Michael Kolb8872c232013-01-29 10:33:22 -08001361 }
1362
1363 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001364 public void onOrientationChanged(int orientation) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001365 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1366 return;
1367 }
Senpo Hubb1c72f2014-09-08 01:07:27 -07001368
1369 // TODO: Document orientation compute logic and unify them in OrientationManagerImpl.
1370 // b/17443789
1371 // Flip to counter-clockwise orientation.
1372 mOrientation = (360 - orientation) % 360;
Michael Kolb8872c232013-01-29 10:33:22 -08001373 }
1374
1375 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001376 public void onCameraAvailable(CameraProxy cameraProxy) {
Alan Newberger29a009c2014-09-05 12:47:42 -07001377 Log.v(TAG, "onCameraAvailable");
Angus Kong20fad242013-11-11 18:23:46 -08001378 if (mPaused) {
1379 return;
1380 }
1381 mCameraDevice = cameraProxy;
1382
1383 initializeCapabilities();
1384
1385 // Reset zoom value index.
Sol Boucher2192fba2014-08-19 17:24:07 -07001386 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001387 if (mFocusManager == null) {
1388 initializeFocusManager();
1389 }
Angus Kong831347d2014-06-16 16:07:28 -07001390 mFocusManager.updateCapabilities(mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001391
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001392 // Do camera parameter dependent initialization.
Angus Kong6607dae2014-06-10 16:07:45 -07001393 mCameraSettings = mCameraDevice.getSettings();
Puneet Lall70a96522014-09-11 17:33:21 -07001394 // HACK: The call to setCameraParameters(UPDATE_PARAM_ALL) may
1395 // eventually recurse back into startPreview().
1396 // To avoid calling startPreview() twice, first acquire
1397 // mStartPreviewLock.
1398 mStartPreviewLock = true;
1399 try {
1400 setCameraParameters(UPDATE_PARAM_ALL);
1401 // Set a listener which updates camera parameters based
1402 // on changed settings.
1403 SettingsManager settingsManager = mActivity.getSettingsManager();
1404 settingsManager.addListener(this);
1405 mCameraPreviewParamsReady = true;
1406 } finally {
1407 mStartPreviewLock = false;
1408 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001409
Angus Kong20fad242013-11-11 18:23:46 -08001410 startPreview();
1411
1412 onCameraOpened();
1413 }
1414
1415 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001416 public void onCaptureCancelled() {
1417 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1418 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001419 }
1420
Michael Kolbd6954f32013-03-08 20:43:01 -08001421 @Override
1422 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001423 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001424 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001425 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001426 mUI.hidePostCaptureAlert();
Spike Spragueb4a22222014-05-22 14:40:53 -07001427 mUI.hideIntentReviewImageView();
Michael Kolb8872c232013-01-29 10:33:22 -08001428 setupPreview();
1429 }
1430
Michael Kolbd6954f32013-03-08 20:43:01 -08001431 @Override
1432 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001433 if (mPaused) {
1434 return;
1435 }
1436
1437 byte[] data = mJpegImageData;
1438
1439 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001440 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001441 // caller specifies a "save uri" then write the data to its
1442 // stream. Otherwise, pass back a scaled down version of the bitmap
1443 // directly in the extras.
1444 if (mSaveUri != null) {
1445 OutputStream outputStream = null;
1446 try {
1447 outputStream = mContentResolver.openOutputStream(mSaveUri);
1448 outputStream.write(data);
1449 outputStream.close();
1450
Alan Newbergerf974ba72014-09-08 14:44:30 -07001451 Log.v(TAG, "saved result to URI: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001452 mActivity.setResultEx(Activity.RESULT_OK);
1453 mActivity.finish();
1454 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001455 Log.w(TAG, "exception saving result to URI: " + mSaveUri, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001456 // ignore exception
1457 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001458 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001459 }
1460 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001461 ExifInterface exif = Exif.getExif(data);
1462 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001463 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1464 bitmap = CameraUtil.rotate(bitmap, orientation);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001465 Log.v(TAG, "inlined bitmap into capture intent result");
Michael Kolb8872c232013-01-29 10:33:22 -08001466 mActivity.setResultEx(Activity.RESULT_OK,
1467 new Intent("inline-data").putExtra("data", bitmap));
1468 mActivity.finish();
1469 }
1470 } else {
1471 // Save the image to a temp file and invoke the cropper
1472 Uri tempUri = null;
1473 FileOutputStream tempStream = null;
1474 try {
1475 File path = mActivity.getFileStreamPath(sTempCropFilename);
1476 path.delete();
1477 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1478 tempStream.write(data);
1479 tempStream.close();
1480 tempUri = Uri.fromFile(path);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001481 Log.v(TAG, "wrote temp file for cropping to: " + sTempCropFilename);
Michael Kolb8872c232013-01-29 10:33:22 -08001482 } catch (FileNotFoundException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001483 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001484 mActivity.setResultEx(Activity.RESULT_CANCELED);
1485 mActivity.finish();
1486 return;
1487 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001488 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001489 mActivity.setResultEx(Activity.RESULT_CANCELED);
1490 mActivity.finish();
1491 return;
1492 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001493 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001494 }
1495
1496 Bundle newExtras = new Bundle();
1497 if (mCropValue.equals("circle")) {
1498 newExtras.putString("circleCrop", "true");
1499 }
1500 if (mSaveUri != null) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001501 Log.v(TAG, "setting output of cropped file to: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001502 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1503 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001504 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001505 }
1506 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001507 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001508 }
1509
Sascha Haeberling37f36112013-08-06 14:31:52 -07001510 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001511 final String CROP_ACTION = "com.android.camera.action.CROP";
1512 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001513
1514 cropIntent.setData(tempUri);
1515 cropIntent.putExtras(newExtras);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001516 Log.v(TAG, "starting CROP intent for capture");
Michael Kolb8872c232013-01-29 10:33:22 -08001517 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1518 }
1519 }
1520
Michael Kolb8872c232013-01-29 10:33:22 -08001521 @Override
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001522 public void onShutterCoordinate(TouchCoordinate coord) {
1523 mShutterTouchCoordinate = coord;
1524 }
1525
1526 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001527 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001528 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001529 }
1530
1531 @Override
1532 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001533 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001534 || (mCameraState == PREVIEW_STOPPED)) {
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001535 mVolumeButtonClickedFlag = false;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001536 return;
1537 }
Michael Kolb8872c232013-01-29 10:33:22 -08001538
1539 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001540 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001541 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001542 + mActivity.getStorageSpaceBytes());
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001543 mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001544 return;
1545 }
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001546 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
1547 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
Michael Kolb8872c232013-01-29 10:33:22 -08001548
Erin Dahlgren6190c362014-06-13 14:12:08 -07001549 int countDownDuration = mActivity.getSettingsManager()
1550 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
Andy Huibers547d7c82014-05-20 23:03:18 -07001551 mTimerDuration = countDownDuration;
Doris Liu6c751642014-05-05 18:43:26 -07001552 if (countDownDuration > 0) {
1553 // Start count down.
1554 mAppController.getCameraAppUI().transitionToCancel();
1555 mAppController.getCameraAppUI().hideModeOptions();
1556 mUI.startCountdown(countDownDuration);
1557 return;
1558 } else {
1559 focusAndCapture();
1560 }
1561 }
1562
1563 private void focusAndCapture() {
Angus Kong6607dae2014-06-10 16:07:45 -07001564 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001565 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001566 }
Michael Kolb8872c232013-01-29 10:33:22 -08001567 // If the user wants to do a snapshot while the previous one is still
1568 // in progress, remember the fact and do it after we finish the previous
1569 // one and re-start the preview. Snapshot in progress also includes the
1570 // state that autofocus is focusing and a picture will be taken when
1571 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001572 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1573 if (!mIsImageCaptureIntent) {
1574 mSnapshotOnIdle = true;
1575 }
Michael Kolb8872c232013-01-29 10:33:22 -08001576 return;
1577 }
1578
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001579 mSnapshotOnIdle = false;
Angus Kong831347d2014-06-16 16:07:28 -07001580 mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001581 }
1582
Doris Liu6c751642014-05-05 18:43:26 -07001583 @Override
1584 public void onRemainingSecondsChanged(int remainingSeconds) {
Andy Huibersa31162c2014-09-02 11:27:11 -07001585 if (remainingSeconds == 1) {
1586 mCountdownSoundPlayer.play(R.raw.beep_twice, 0.6f);
1587 } else if (remainingSeconds == 2 || remainingSeconds == 3) {
1588 mCountdownSoundPlayer.play(R.raw.beep_once, 0.6f);
1589 }
Doris Liu6c751642014-05-05 18:43:26 -07001590 }
1591
1592 @Override
1593 public void onCountDownFinished() {
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001594 if (mIsImageCaptureIntent) {
1595 mAppController.getCameraAppUI().transitionToIntentReviewLayout();
1596 } else {
1597 mAppController.getCameraAppUI().transitionToCapture();
1598 }
Doris Liu6c751642014-05-05 18:43:26 -07001599 mAppController.getCameraAppUI().showModeOptions();
1600 if (mPaused) {
1601 return;
1602 }
1603 focusAndCapture();
1604 }
1605
Andy Huibersdef975d2013-11-22 09:13:39 -08001606 private void onResumeTasks() {
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001607 if (mPaused) {
1608 return;
1609 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001610 Log.v(TAG, "Executing onResumeTasks.");
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001611
Andy Huibersa31162c2014-09-02 11:27:11 -07001612 mCountdownSoundPlayer.loadSound(R.raw.beep_once);
1613 mCountdownSoundPlayer.loadSound(R.raw.beep_twice);
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001614 if (mFocusManager != null) {
1615 // If camera is not open when resume is called, focus manager will
1616 // not be initialized yet, in which case it will start listening to
1617 // preview area size change later in the initialization.
1618 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1619 }
1620 mAppController.addPreviewAreaSizeChangedListener(mUI);
1621
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001622 CameraProvider camProvider = mActivity.getCameraProvider();
1623 if (camProvider == null) {
1624 // No camera provider, the Activity is destroyed already.
1625 return;
1626 }
Sol Boucher44ce4b22014-08-04 23:41:38 -07001627 requestCameraOpen();
Angus Kong20fad242013-11-11 18:23:46 -08001628
Michael Kolb8872c232013-01-29 10:33:22 -08001629 mJpegPictureCallbackTime = 0;
Sol Boucher2192fba2014-08-19 17:24:07 -07001630 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001631
1632 mOnResumeTime = SystemClock.uptimeMillis();
1633 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001634
1635 // If first time initialization is not finished, put it in the
1636 // message queue.
1637 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001638 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001639 } else {
1640 initializeSecondTime();
1641 }
Michael Kolb8872c232013-01-29 10:33:22 -08001642
Angus Kong0d00a892013-03-26 11:40:40 -07001643 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1644 if (gsensor != null) {
1645 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1646 }
1647
1648 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1649 if (msensor != null) {
1650 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1651 }
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001652
1653 getServices().getRemoteShutterListener().onModuleReady(this);
1654 SessionStatsCollector.instance().sessionActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001655 }
1656
Angus Kongc4e66562013-11-22 23:03:21 -08001657 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001658 * @return Whether the currently active camera is front-facing.
1659 */
1660 private boolean isCameraFrontFacing() {
Sol Boucher43e18132014-06-19 15:03:18 -07001661 return mAppController.getCameraProvider().getCharacteristics(mCameraId)
1662 .isFacingFront();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001663 }
1664
1665 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001666 * The focus manager is the first UI related element to get initialized, and
1667 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001668 */
1669 private void initializeFocusManager() {
1670 // Create FocusManager object. startPreview needs it.
1671 // if mFocusManager not null, reuse it
1672 // otherwise create a new instance
1673 if (mFocusManager != null) {
1674 mFocusManager.removeMessages();
1675 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001676 mMirror = isCameraFrontFacing();
Angus Kong831347d2014-06-16 16:07:28 -07001677 String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
Angus Kongc4e66562013-11-22 23:03:21 -08001678 R.array.pref_camera_focusmode_default_array);
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -07001679 ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
1680 new ArrayList<CameraCapabilities.FocusMode>();
Angus Kong831347d2014-06-16 16:07:28 -07001681 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
1682 for (String modeString : defaultFocusModesStrings) {
1683 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
1684 if (mode != null) {
1685 defaultFocusModes.add(mode);
1686 }
1687 }
1688 mFocusManager =
1689 new FocusOverlayManager(mAppController, defaultFocusModes,
1690 mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
1691 mUI.getFocusUI());
Kevin Gabayanfb333362014-06-02 14:48:20 -07001692 MotionManager motionManager = getServices().getMotionManager();
1693 if (motionManager != null) {
1694 motionManager.addListener(mFocusManager);
1695 }
Angus Kongc4e66562013-11-22 23:03:21 -08001696 }
Doris Liu482de022013-12-18 19:18:16 -08001697 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001698 }
1699
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001700 /**
1701 * @return Whether we are resuming from within the lockscreen.
1702 */
1703 private boolean isResumeFromLockscreen() {
1704 String action = mActivity.getIntent().getAction();
1705 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1706 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
1707 }
1708
Angus Kong20fad242013-11-11 18:23:46 -08001709 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001710 public void resume() {
1711 mPaused = false;
Doris Liu59401042014-01-14 17:51:32 -08001712
Angus Kongc4e66562013-11-22 23:03:21 -08001713 // Add delay on resume from lock screen only, in order to to speed up
1714 // the onResume --> onPause --> onResume cycle from lock screen.
1715 // Don't do always because letting go of thread can cause delay.
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001716 if (isResumeFromLockscreen()) {
Angus Kongc4e66562013-11-22 23:03:21 -08001717 Log.v(TAG, "On resume, from lock screen.");
1718 // Note: onPauseAfterSuper() will delete this runnable, so we will
1719 // at most have 1 copy queued up.
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001720 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
Angus Kongc4e66562013-11-22 23:03:21 -08001721 } else {
1722 Log.v(TAG, "On resume.");
1723 onResumeTasks();
1724 }
1725 }
1726
1727 @Override
1728 public void pause() {
Michael Kolb8872c232013-01-29 10:33:22 -08001729 mPaused = true;
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001730 mHandler.removeCallbacks(mResumeTaskRunnable);
Sascha Haeberling1bf08892014-06-18 09:38:06 -07001731 getServices().getRemoteShutterListener().onModuleExit();
Andy Huibers10c58162014-03-29 14:06:54 -07001732 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001733
Angus Kong0d00a892013-03-26 11:40:40 -07001734 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1735 if (gsensor != null) {
1736 mSensorManager.unregisterListener(this, gsensor);
1737 }
1738
1739 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1740 if (msensor != null) {
1741 mSensorManager.unregisterListener(this, msensor);
1742 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001743
Michael Kolb8872c232013-01-29 10:33:22 -08001744 // Reset the focus first. Camera CTS does not guarantee that
1745 // cancelAutoFocus is allowed after preview stops.
1746 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1747 mCameraDevice.cancelAutoFocus();
1748 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001749
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001750 // If the camera has not been opened asynchronously yet,
1751 // and startPreview hasn't been called, then this is a no-op.
1752 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001753 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001754 cancelCountDown();
1755 mCountdownSoundPlayer.release();
Michael Kolb8872c232013-01-29 10:33:22 -08001756
Angus Kongce5480e2013-01-29 17:43:48 -08001757 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001758 // If we are in an image capture intent and has taken
1759 // a picture, we just clear it in onPause.
1760 mJpegImageData = null;
1761
Angus Kongdcccc512013-08-08 17:06:03 -07001762 // Remove the messages and runnables in the queue.
1763 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001764
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001765 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001766 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001767 mUI.onPause();
1768
Michael Kolb8872c232013-01-29 10:33:22 -08001769 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001770 if (mFocusManager != null) {
1771 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001772 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001773 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001774 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001775 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001776
1777 SettingsManager settingsManager = mActivity.getSettingsManager();
1778 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001779 }
1780
Angus Kong20fad242013-11-11 18:23:46 -08001781 @Override
1782 public void destroy() {
1783 // TODO: implement this.
1784 }
1785
1786 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001787 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001788 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001789 }
1790
1791 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001792 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001793 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001794 setDisplayOrientation();
1795 }
1796 }
1797
Michael Kolb8872c232013-01-29 10:33:22 -08001798 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001799 return isCameraIdle()
1800 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001801 }
1802
1803 @Override
1804 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001805 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001806 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001807 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001808 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001809 setCameraState(FOCUSING);
1810 }
1811
1812 @Override
1813 public void cancelAutoFocus() {
1814 mCameraDevice.cancelAutoFocus();
1815 setCameraState(IDLE);
1816 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1817 }
1818
Michael Kolb8872c232013-01-29 10:33:22 -08001819 @Override
1820 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001821 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1822 || mCameraState == SNAPSHOT_IN_PROGRESS
1823 || mCameraState == SWITCHING_CAMERA
1824 || mCameraState == PREVIEW_STOPPED) {
1825 return;
1826 }
1827
1828 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001829 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1830 return;
1831 }
Doris Liu482de022013-12-18 19:18:16 -08001832 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001833 }
1834
1835 @Override
1836 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001837 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001838 }
1839
1840 @Override
1841 public boolean onKeyDown(int keyCode, KeyEvent event) {
1842 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001843 case KeyEvent.KEYCODE_VOLUME_UP:
1844 case KeyEvent.KEYCODE_VOLUME_DOWN:
1845 case KeyEvent.KEYCODE_FOCUS:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001846 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1847 !mActivity.getCameraAppUI().isInIntentReview()) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001848 if (event.getRepeatCount() == 0) {
1849 onShutterButtonFocus(true);
1850 }
1851 return true;
1852 }
1853 return false;
1854 case KeyEvent.KEYCODE_CAMERA:
1855 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1856 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001857 }
1858 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001859 case KeyEvent.KEYCODE_DPAD_CENTER:
1860 // If we get a dpad center event without any focused view, move
1861 // the focus to the shutter button and press it.
1862 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1863 // Start auto-focus immediately to reduce shutter lag. After
1864 // the shutter button gets the focus, onShutterButtonFocus()
1865 // will be called again but it is fine.
1866 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001867 }
1868 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001869 }
1870 return false;
1871 }
1872
1873 @Override
1874 public boolean onKeyUp(int keyCode, KeyEvent event) {
1875 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001876 case KeyEvent.KEYCODE_VOLUME_UP:
1877 case KeyEvent.KEYCODE_VOLUME_DOWN:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001878 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1879 !mActivity.getCameraAppUI().isInIntentReview()) {
1880 if (mUI.isCountingDown()) {
1881 cancelCountDown();
1882 } else {
1883 mVolumeButtonClickedFlag = true;
1884 onShutterButtonClick();
1885 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001886 return true;
1887 }
1888 return false;
1889 case KeyEvent.KEYCODE_FOCUS:
1890 if (mFirstTimeInitialized) {
1891 onShutterButtonFocus(false);
1892 }
Michael Kolb8872c232013-01-29 10:33:22 -08001893 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001894 }
1895 return false;
1896 }
1897
Michael Kolb8872c232013-01-29 10:33:22 -08001898 private void closeCamera() {
1899 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001900 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001901 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001902 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001903 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001904
Michael Kolb8872c232013-01-29 10:33:22 -08001905 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001906 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001907 mCameraDevice = null;
1908 setCameraState(PREVIEW_STOPPED);
1909 mFocusManager.onCameraReleased();
1910 }
1911 }
1912
1913 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001914 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
Sol Boucher43e18132014-06-19 15:03:18 -07001915 Characteristics info =
1916 mActivity.getCameraProvider().getCharacteristics(mCameraId);
Sol Boucher73ec9852014-07-24 23:59:21 -07001917 mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001918 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001919 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001920 if (mFocusManager != null) {
1921 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1922 }
Doris Liu6432cd62013-06-13 17:20:31 -07001923 // Change the camera display orientation
1924 if (mCameraDevice != null) {
Sol Boucher73ec9852014-07-24 23:59:21 -07001925 mCameraDevice.setDisplayOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001926 }
Michael Kolb8872c232013-01-29 10:33:22 -08001927 }
1928
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001929 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001930 private void setupPreview() {
1931 mFocusManager.resetTouchFocus();
1932 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001933 }
1934
Angus Kong20fad242013-11-11 18:23:46 -08001935 /**
1936 * Returns whether we can/should start the preview or not.
1937 */
1938 private boolean checkPreviewPreconditions() {
1939 if (mPaused) {
1940 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001941 }
Michael Kolb8872c232013-01-29 10:33:22 -08001942
Angus Kong20fad242013-11-11 18:23:46 -08001943 if (mCameraDevice == null) {
1944 Log.w(TAG, "startPreview: camera device not ready yet.");
1945 return false;
1946 }
1947
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001948 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001949 if (st == null) {
1950 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001951 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001952 }
1953
1954 if (!mCameraPreviewParamsReady) {
1955 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001956 return false;
1957 }
1958 return true;
1959 }
1960
1961 /**
1962 * The start/stop preview should only run on the UI thread.
1963 */
1964 private void startPreview() {
Puneet Lall70a96522014-09-11 17:33:21 -07001965 // HACK: The call to setCameraParameters(UPDATE_PARAM_ALL) may
1966 // eventually recurse back into startPreview().
1967 // To avoid calling startPreview() twice, we must acquire
1968 // mStartPreviewLock.
1969 if (mStartPreviewLock) {
1970 // do nothing
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001971 return;
1972 }
Puneet Lall70a96522014-09-11 17:33:21 -07001973 mStartPreviewLock = true;
1974 try {
1975 if (!checkPreviewPreconditions()) {
1976 return;
Michael Kolb8872c232013-01-29 10:33:22 -08001977 }
Michael Kolb8872c232013-01-29 10:33:22 -08001978
Puneet Lall70a96522014-09-11 17:33:21 -07001979 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
1980 setDisplayOrientation();
Angus Kong20fad242013-11-11 18:23:46 -08001981
Puneet Lall70a96522014-09-11 17:33:21 -07001982 if (!mSnapshotOnIdle) {
1983 // If the focus mode is continuous autofocus, call cancelAutoFocus
1984 // to resume it because it may have been paused by autoFocus call.
1985 if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
1986 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
1987 mCameraDevice.cancelAutoFocus();
1988 }
1989 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1990 }
1991 setCameraParameters(UPDATE_PARAM_ALL);
1992 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
1993
1994 Log.i(TAG, "startPreview");
Spike Sprague963775c2014-09-18 19:44:07 -07001995 // If we're using API2 in portability layers, don't use startPreviewWithCallback()
1996 // b/17576554
1997 CameraAgent.CameraStartPreviewCallback startPreviewCallback =
1998 new CameraAgent.CameraStartPreviewCallback() {
1999 @Override
2000 public void onPreviewStarted() {
2001 mFocusManager.onPreviewStarted();
2002 PhotoModule.this.onPreviewStarted();
2003 SessionStatsCollector.instance().previewActive(true);
2004 if (mSnapshotOnIdle) {
2005 mHandler.post(mDoSnapRunnable);
2006 }
Spike Sprague3c3b31d2014-09-08 10:43:04 -07002007 }
Spike Sprague963775c2014-09-18 19:44:07 -07002008 };
2009 if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity)) {
2010 mCameraDevice.startPreview();
2011 startPreviewCallback.onPreviewStarted();
2012 } else {
2013 mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
2014 startPreviewCallback);
2015 }
Puneet Lall70a96522014-09-11 17:33:21 -07002016 } finally {
2017 mStartPreviewLock = false;
Michael Kolb8872c232013-01-29 10:33:22 -08002018 }
2019 }
2020
Michael Kolbd6954f32013-03-08 20:43:01 -08002021 @Override
2022 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08002023 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07002024 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08002025 mCameraDevice.stopPreview();
2026 mFaceDetectionStarted = false;
2027 }
2028 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002029 if (mFocusManager != null) {
2030 mFocusManager.onPreviewStopped();
2031 }
Andy Huibers10c58162014-03-29 14:06:54 -07002032 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002033 }
2034
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002035 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -07002036 public void onSettingChanged(SettingsManager settingsManager, String key) {
2037 if (key.equals(Keys.KEY_FLASH_MODE)) {
2038 updateParametersFlashMode();
2039 }
2040 if (key.equals(Keys.KEY_CAMERA_HDR)) {
2041 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2042 Keys.KEY_CAMERA_HDR)) {
2043 // HDR is on.
2044 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
2045 mFlashModeBeforeSceneMode = settingsManager.getString(
2046 mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
2047 } else {
2048 if (mFlashModeBeforeSceneMode != null) {
2049 settingsManager.set(mAppController.getCameraScope(),
2050 Keys.KEY_FLASH_MODE,
2051 mFlashModeBeforeSceneMode);
2052 updateParametersFlashMode();
2053 mFlashModeBeforeSceneMode = null;
Angus Kong8c07fe92014-05-22 14:42:45 -07002054 }
Erin Dahlgren6190c362014-06-13 14:12:08 -07002055 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002056 }
2057 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08002058
2059 if (mCameraDevice != null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002060 mCameraDevice.applySettings(mCameraSettings);
Erin Dahlgren1648c362014-01-06 15:06:04 -08002061 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002062 }
2063
Michael Kolb8872c232013-01-29 10:33:22 -08002064 private void updateCameraParametersInitialize() {
2065 // Reset preview frame rate to the maximum because it may be lowered by
2066 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07002067 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07002068 if (fpsRange != null && fpsRange.length > 0) {
Angus Kong831347d2014-06-16 16:07:28 -07002069 mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
Michael Kolb8872c232013-01-29 10:33:22 -08002070 }
2071
Angus Kong831347d2014-06-16 16:07:28 -07002072 mCameraSettings.setRecordingHintEnabled(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002073
Angus Kong6607dae2014-06-10 16:07:45 -07002074 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
2075 mCameraSettings.setVideoStabilization(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002076 }
2077 }
2078
2079 private void updateCameraParametersZoom() {
2080 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07002081 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002082 mCameraSettings.setZoomRatio(mZoomValue);
Michael Kolb8872c232013-01-29 10:33:22 -08002083 }
2084 }
2085
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002086 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002087 private void setAutoExposureLockIfSupported() {
2088 if (mAeLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002089 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002090 }
2091 }
2092
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002093 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002094 private void setAutoWhiteBalanceLockIfSupported() {
2095 if (mAwbLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002096 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002097 }
2098 }
2099
Michael Kolb8872c232013-01-29 10:33:22 -08002100 private void setFocusAreasIfSupported() {
2101 if (mFocusAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002102 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002103 }
2104 }
2105
Michael Kolb8872c232013-01-29 10:33:22 -08002106 private void setMeteringAreasIfSupported() {
2107 if (mMeteringAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002108 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002109 }
2110 }
2111
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002112 private void updateCameraParametersPreference() {
Spike Spragueae56c122014-09-19 11:18:46 -07002113 // some monkey tests can get here when shutting the app down
2114 // make sure mCameraDevice is still valid, b/17580046
2115 if (mCameraDevice == null) {
2116 return;
2117 }
2118
Michael Kolb8872c232013-01-29 10:33:22 -08002119 setAutoExposureLockIfSupported();
2120 setAutoWhiteBalanceLockIfSupported();
2121 setFocusAreasIfSupported();
2122 setMeteringAreasIfSupported();
2123
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002124 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07002125 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002126 mCameraSettings
2127 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Andy Huibers10c58162014-03-29 14:06:54 -07002128 SessionStatsCollector.instance().autofocusActive(
Angus Kong831347d2014-06-16 16:07:28 -07002129 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
2130 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
2131 );
Michael Kolbd3253f22013-07-12 11:36:47 -07002132
Michael Kolb8872c232013-01-29 10:33:22 -08002133 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002134 updateParametersPictureSize();
2135
2136 // Set JPEG quality.
2137 updateParametersPictureQuality();
2138
2139 // For the following settings, we need to check if the settings are
2140 // still supported by latest driver, if not, ignore the settings.
2141
2142 // Set exposure compensation
2143 updateParametersExposureCompensation();
2144
2145 // Set the scene mode: also sets flash and white balance.
2146 updateParametersSceneMode();
2147
2148 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2149 updateAutoFocusMoveCallback();
2150 }
2151 }
2152
2153 private void updateParametersPictureSize() {
2154 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002155 String pictureSizeKey = isCameraFrontFacing() ? Keys.KEY_PICTURE_SIZE_FRONT
2156 : Keys.KEY_PICTURE_SIZE_BACK;
2157 String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL,
2158 pictureSizeKey);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01002159
Angus Kong6607dae2014-06-10 16:07:45 -07002160 List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes();
Andy Huibers90c7ad52014-05-20 22:13:54 -07002161 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2162 mCameraDevice.getCameraId(), supported);
Angus Kong6607dae2014-06-10 16:07:45 -07002163 SettingsUtil.setCameraPictureSize(pictureSize, supported, mCameraSettings,
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002164 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07002165
2166 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2167 mCameraDevice.getCameraId());
2168 if (ApiHelper.IS_NEXUS_5) {
2169 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2170 mShouldResizeTo16x9 = true;
2171 } else {
2172 mShouldResizeTo16x9 = false;
2173 }
2174 }
Michael Kolb8872c232013-01-29 10:33:22 -08002175
2176 // Set a preview size that is closest to the viewfinder height and has
2177 // the right aspect ratio.
Angus Kong6607dae2014-06-10 16:07:45 -07002178 List<Size> sizes = mCameraCapabilities.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07002179 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002180 (double) size.width() / size.height());
Angus Kong6607dae2014-06-10 16:07:45 -07002181 Size original = mCameraSettings.getCurrentPreviewSize();
2182 if (!optimalSize.equals(original)) {
Alan Newberger73514152014-09-10 15:03:27 -07002183 Log.v(TAG, "setting preview size");
Angus Kong6607dae2014-06-10 16:07:45 -07002184 mCameraSettings.setPreviewSize(optimalSize);
Doris Liu6432cd62013-06-13 17:20:31 -07002185
Michael Kolb8872c232013-01-29 10:33:22 -08002186 // Zoom related settings will be changed for different preview
2187 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002188 if (mHandler.getLooper() == Looper.myLooper()) {
2189 // On UI thread only, not when camera starts up
2190 setupPreview();
2191 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002192 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002193 }
Angus Kong6607dae2014-06-10 16:07:45 -07002194 mCameraSettings = mCameraDevice.getSettings();
Michael Kolb8872c232013-01-29 10:33:22 -08002195 }
Doris Liu95405742013-11-05 15:25:26 -08002196
Angus Kong00b7b102014-04-24 15:46:52 -07002197 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
Alan Newberger73514152014-09-10 15:03:27 -07002198 Log.v(TAG, "updating aspect ratio");
Angus Kong00b7b102014-04-24 15:46:52 -07002199 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2200 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002201 }
Alan Newberger73514152014-09-10 15:03:27 -07002202 Log.d(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002203 }
Michael Kolb8872c232013-01-29 10:33:22 -08002204
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002205 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002206 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2207 CameraProfile.QUALITY_HIGH);
Angus Kong6607dae2014-06-10 16:07:45 -07002208 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002209 }
Michael Kolb8872c232013-01-29 10:33:22 -08002210
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002211 private void updateParametersExposureCompensation() {
2212 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002213 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2214 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2215 int value = settingsManager.getInteger(mAppController.getCameraScope(),
2216 Keys.KEY_EXPOSURE);
Angus Kong88289042014-04-22 16:39:42 -07002217 int max = mCameraCapabilities.getMaxExposureCompensation();
2218 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002219 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002220 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002221 } else {
2222 Log.w(TAG, "invalid exposure range: " + value);
2223 }
Michael Kolb8872c232013-01-29 10:33:22 -08002224 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002225 // If exposure compensation is not enabled, reset the exposure compensation value.
2226 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002227 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002228
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002229 }
2230
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002231 private void updateParametersSceneMode() {
Angus Kong6607dae2014-06-10 16:07:45 -07002232 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002233 SettingsManager settingsManager = mActivity.getSettingsManager();
2234
Erin Dahlgren6190c362014-06-13 14:12:08 -07002235 mSceneMode = stringifier.
2236 sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2237 Keys.KEY_SCENE_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002238 if (mCameraCapabilities.supports(mSceneMode)) {
2239 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
2240 mCameraSettings.setSceneMode(mSceneMode);
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002241
2242 // Setting scene mode will change the settings of flash mode,
2243 // white balance, and focus mode. Here we read back the
2244 // parameters, so we can know those settings.
Angus Kong6607dae2014-06-10 16:07:45 -07002245 mCameraDevice.applySettings(mCameraSettings);
2246 mCameraSettings = mCameraDevice.getSettings();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002247 }
2248 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002249 mSceneMode = mCameraSettings.getCurrentSceneMode();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002250 if (mSceneMode == null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002251 mSceneMode = CameraCapabilities.SceneMode.AUTO;
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002252 }
2253 }
2254
Angus Kong6607dae2014-06-10 16:07:45 -07002255 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
Michael Kolb8872c232013-01-29 10:33:22 -08002256 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002257 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002258
Michael Kolb8872c232013-01-29 10:33:22 -08002259 // Set focus mode.
2260 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002261 mCameraSettings.setFocusMode(
2262 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Michael Kolb8872c232013-01-29 10:33:22 -08002263 } else {
Angus Kong831347d2014-06-16 16:07:28 -07002264 mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08002265 }
Michael Kolb8872c232013-01-29 10:33:22 -08002266 }
2267
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002268 private void updateParametersFlashMode() {
2269 SettingsManager settingsManager = mActivity.getSettingsManager();
2270
Angus Kong6607dae2014-06-10 16:07:45 -07002271 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
Erin Dahlgren6190c362014-06-13 14:12:08 -07002272 .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2273 Keys.KEY_FLASH_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002274 if (mCameraCapabilities.supports(flashMode)) {
2275 mCameraSettings.setFlashMode(flashMode);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002276 }
2277 }
2278
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002279 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002280 private void updateAutoFocusMoveCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -07002281 if (mCameraSettings.getCurrentFocusMode() ==
2282 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
Angus Kong9ef99252013-07-18 18:04:19 -07002283 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002284 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002285 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002286 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002287 }
2288 }
2289
Spike Spragueabf54e22014-03-27 15:41:28 -07002290 /**
2291 * Sets the exposure compensation to the given value and also updates settings.
2292 *
2293 * @param value exposure compensation value to be set
2294 */
2295 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002296 int max = mCameraCapabilities.getMaxExposureCompensation();
2297 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002298 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002299 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002300 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002301 settingsManager.set(mAppController.getCameraScope(),
2302 Keys.KEY_EXPOSURE, value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002303 } else {
2304 Log.w(TAG, "invalid exposure range: " + value);
2305 }
2306 }
2307
Michael Kolb8872c232013-01-29 10:33:22 -08002308 // We separate the parameters into several subsets, so we can update only
2309 // the subsets actually need updating. The PREFERENCE set needs extra
2310 // locking because the preference can be changed from GLThread as well.
2311 private void setCameraParameters(int updateSet) {
2312 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2313 updateCameraParametersInitialize();
2314 }
2315
2316 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2317 updateCameraParametersZoom();
2318 }
2319
2320 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002321 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002322 }
2323
Spike Sprague5cc48d62014-09-22 10:52:32 -07002324 // some monkey tests can get here when shutting the app down
2325 // make sure mCameraDevice is still valid, b/17604028
2326 if (mCameraDevice != null) {
2327 mCameraDevice.applySettings(mCameraSettings);
2328 }
Michael Kolb8872c232013-01-29 10:33:22 -08002329 }
2330
2331 // If the Camera is idle, update the parameters immediately, otherwise
2332 // accumulate them in mUpdateSet and update later.
2333 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2334 mUpdateSet |= additionalUpdateSet;
2335 if (mCameraDevice == null) {
2336 // We will update all the parameters when we open the device, so
2337 // we don't need to do anything now.
2338 mUpdateSet = 0;
2339 return;
2340 } else if (isCameraIdle()) {
2341 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002342 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002343 mUpdateSet = 0;
2344 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002345 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2346 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002347 }
2348 }
2349 }
2350
ztenghui7b265a62013-09-09 14:58:44 -07002351 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002352 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002353 return (mCameraState == IDLE) ||
2354 (mCameraState == PREVIEW_STOPPED) ||
2355 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002356 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002357 }
2358
ztenghui7b265a62013-09-09 14:58:44 -07002359 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002360 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002361 String action = mActivity.getIntent().getAction();
2362 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002363 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002364 }
2365
2366 private void setupCaptureParams() {
2367 Bundle myExtras = mActivity.getIntent().getExtras();
2368 if (myExtras != null) {
2369 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2370 mCropValue = myExtras.getString("crop");
2371 }
2372 }
2373
Michael Kolb8872c232013-01-29 10:33:22 -08002374 private void initializeCapabilities() {
Angus Kong88289042014-04-22 16:39:42 -07002375 mCameraCapabilities = mCameraDevice.getCapabilities();
2376 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2377 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2378 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2379 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kong6607dae2014-06-10 16:07:45 -07002380 mContinuousFocusSupported =
2381 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002382 }
2383
Michael Kolb8872c232013-01-29 10:33:22 -08002384 @Override
Sol Boucher2192fba2014-08-19 17:24:07 -07002385 public void onZoomChanged(float ratio) {
Michael Kolbd6954f32013-03-08 20:43:01 -08002386 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002387 if (mPaused) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002388 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002389 }
Sol Boucher2192fba2014-08-19 17:24:07 -07002390 mZoomValue = ratio;
Angus Kong6607dae2014-06-10 16:07:45 -07002391 if (mCameraSettings == null || mCameraDevice == null) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002392 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002393 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002394 // Set zoom parameters asynchronously
Sol Boucher2192fba2014-08-19 17:24:07 -07002395 mCameraSettings.setZoomRatio(mZoomValue);
Angus Kong6607dae2014-06-10 16:07:45 -07002396 mCameraDevice.applySettings(mCameraSettings);
Angus Kongce5480e2013-01-29 17:43:48 -08002397 }
2398
2399 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002400 public int getCameraState() {
2401 return mCameraState;
2402 }
2403
2404 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002405 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002406 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002407 }
Angus Kong86d36312013-01-31 18:22:44 -08002408
2409 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002410 public void onLowMemory() {
2411 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002412 }
Angus Kong0d00a892013-03-26 11:40:40 -07002413
2414 @Override
2415 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2416 }
2417
2418 @Override
2419 public void onSensorChanged(SensorEvent event) {
2420 int type = event.sensor.getType();
2421 float[] data;
2422 if (type == Sensor.TYPE_ACCELEROMETER) {
2423 data = mGData;
2424 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2425 data = mMData;
2426 } else {
2427 // we should not be here.
2428 return;
2429 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002430 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002431 data[i] = event.values[i];
2432 }
2433 float[] orientation = new float[3];
2434 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2435 SensorManager.getOrientation(mR, orientation);
2436 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2437 if (mHeading < 0) {
2438 mHeading += 360;
2439 }
Angus Kong0d00a892013-03-26 11:40:40 -07002440 }
Doris Liu6432cd62013-06-13 17:20:31 -07002441
Ruben Brunkd217ed02013-10-08 23:31:13 -07002442 // For debugging only.
2443 public void setDebugUri(Uri uri) {
2444 mDebugUri = uri;
2445 }
2446
2447 // For debugging only.
2448 private void saveToDebugUri(byte[] data) {
2449 if (mDebugUri != null) {
2450 OutputStream outputStream = null;
2451 try {
2452 outputStream = mContentResolver.openOutputStream(mDebugUri);
2453 outputStream.write(data);
2454 outputStream.close();
2455 } catch (IOException e) {
2456 Log.e(TAG, "Exception while writing debug jpeg file", e);
2457 } finally {
2458 CameraUtil.closeSilently(outputStream);
2459 }
2460 }
2461 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002462
2463 @Override
2464 public void onRemoteShutterPress() {
Sascha Haeberling57d4f742014-06-25 11:05:49 -07002465 mHandler.post(new Runnable() {
2466 @Override
2467 public void run() {
2468 focusAndCapture();
2469 }
2470 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002471 }
Michael Kolb8872c232013-01-29 10:33:22 -08002472}