blob: 4e0ebff16e0557c5a3d4c8d43615c8402a4dc382 [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.View;
Michael Kolb8872c232013-01-29 10:33:22 -080045
Sameer Padala2c8cc452013-11-05 18:49:12 -080046import com.android.camera.PhotoModule.NamedImages.NamedEntity;
47import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080048import com.android.camera.app.CameraAppUI;
Angus Kong0b9eb5b2014-04-30 15:03:33 -070049import com.android.camera.app.CameraProvider;
Sascha Haeberlingf9cba4e2014-04-22 09:11:28 -070050import com.android.camera.app.MediaSaver;
51import com.android.camera.app.MemoryManager;
52import com.android.camera.app.MemoryManager.MemoryListener;
Kevin Gabayanfb333362014-06-02 14:48:20 -070053import com.android.camera.app.MotionManager;
Angus Kong2bca2102014-03-11 16:27:30 -070054import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070055import com.android.camera.exif.ExifInterface;
56import com.android.camera.exif.ExifTag;
57import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080058import com.android.camera.hardware.HardwareSpec;
59import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080060import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080061import com.android.camera.remote.RemoteCameraModule;
Andy Huibers90c7ad52014-05-20 22:13:54 -070062import com.android.camera.settings.CameraPictureSizesCacher;
Erin Dahlgren6190c362014-06-13 14:12:08 -070063import com.android.camera.settings.Keys;
Angus Kong454d63f2014-05-06 14:45:59 -070064import com.android.camera.settings.ResolutionUtil;
Erin Dahlgren357b7672013-11-20 17:38:14 -080065import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010066import com.android.camera.settings.SettingsUtil;
Doris Liu6c751642014-05-05 18:43:26 -070067import com.android.camera.ui.CountDownView;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070068import com.android.camera.ui.TouchCoordinate;
Angus Kongdcccc512013-08-08 17:06:03 -070069import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070070import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070071import com.android.camera.util.GcamHelper;
Sol Boucher44ce4b22014-08-04 23:41:38 -070072import com.android.camera.util.GservicesHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070073import com.android.camera.util.SessionStatsCollector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070074import com.android.camera.util.UsageStatistics;
Doris Liudb8f9752014-05-12 15:25:13 -070075import com.android.camera.widget.AspectRatioSelector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070076import com.android.camera2.R;
Spike Sprague3c3b31d2014-09-08 10:43:04 -070077import com.android.ex.camera2.portability.CameraAgent;
Sol Boucher5a344962014-06-17 14:05:08 -070078import com.android.ex.camera2.portability.CameraAgent.CameraAFCallback;
79import com.android.ex.camera2.portability.CameraAgent.CameraAFMoveCallback;
80import com.android.ex.camera2.portability.CameraAgent.CameraPictureCallback;
81import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
82import com.android.ex.camera2.portability.CameraAgent.CameraShutterCallback;
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -070083import com.android.ex.camera2.portability.CameraCapabilities;
Sol Boucher43e18132014-06-19 15:03:18 -070084import com.android.ex.camera2.portability.CameraDeviceInfo.Characteristics;
Sol Boucher5a344962014-06-17 14:05:08 -070085import com.android.ex.camera2.portability.CameraSettings;
86import com.android.ex.camera2.portability.Size;
Seth Raphael5e09d012013-12-18 13:45:03 -080087import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080088
Angus Kong454d63f2014-05-06 14:45:59 -070089import java.io.ByteArrayOutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070090import java.io.File;
91import java.io.FileNotFoundException;
92import java.io.FileOutputStream;
93import java.io.IOException;
94import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010095import java.lang.ref.WeakReference;
Angus Kong831347d2014-06-16 16:07:28 -070096import java.util.ArrayList;
Angus Kongdcccc512013-08-08 17:06:03 -070097import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070098import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070099
Michael Kolb8872c232013-01-29 10:33:22 -0800100public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800101 extends CameraModule
102 implements PhotoController,
103 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800104 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800105 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -0800106 SensorEventListener,
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800107 SettingsManager.OnSettingChangedListener,
Doris Liu6c751642014-05-05 18:43:26 -0700108 RemoteCameraModule,
109 CountDownView.OnCountDownStatusListener {
Michael Kolb8872c232013-01-29 10:33:22 -0800110
Alan Newberger00a390e2014-08-18 18:40:47 -0700111 public static final String PHOTO_MODULE_STRING_ID = "PhotoModule";
Erin Dahlgren6190c362014-06-13 14:12:08 -0700112
113 private static final Log.Tag TAG = new Log.Tag(PHOTO_MODULE_STRING_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800114
115 // We number the request code from 1000 to avoid collision with Gallery.
116 private static final int REQUEST_CROP = 1000;
117
Angus Kong13e87c42013-11-25 10:02:47 -0800118 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800119 private static final int MSG_FIRST_TIME_INIT = 1;
120 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800121
122 // The subset of parameters we need to update in setCameraParameters().
123 private static final int UPDATE_PARAM_INITIALIZE = 1;
124 private static final int UPDATE_PARAM_ZOOM = 2;
125 private static final int UPDATE_PARAM_PREFERENCE = 4;
126 private static final int UPDATE_PARAM_ALL = -1;
127
Ruben Brunkd7488272013-10-10 18:45:53 -0700128 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
129
Michael Kolb8872c232013-01-29 10:33:22 -0800130 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800131 private CameraProxy mCameraDevice;
132 private int mCameraId;
Angus Kong88289042014-04-22 16:39:42 -0700133 private CameraCapabilities mCameraCapabilities;
Angus Kong6607dae2014-06-10 16:07:45 -0700134 private CameraSettings mCameraSettings;
Michael Kolb8872c232013-01-29 10:33:22 -0800135 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800136
137 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800138
Michael Kolb8872c232013-01-29 10:33:22 -0800139 // The activity is going to switch to the specified camera id. This is
140 // needed because texture copy is done in GL thread. -1 means camera is not
141 // switching.
142 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800143
144 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
145 // needed to be updated in mUpdateSet.
146 private int mUpdateSet;
147
Sol Boucher2192fba2014-08-19 17:24:07 -0700148 private float mZoomValue; // The current zoom ratio.
Andy Huibers547d7c82014-05-20 23:03:18 -0700149 private int mTimerDuration;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700150 /** Set when a volume button is clicked to take photo */
151 private boolean mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800152
Michael Kolb8872c232013-01-29 10:33:22 -0800153 private boolean mFocusAreaSupported;
154 private boolean mMeteringAreaSupported;
155 private boolean mAeLockSupported;
156 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700157 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800158
Michael Kolb8872c232013-01-29 10:33:22 -0800159 private static final String sTempCropFilename = "crop-temp";
160
Michael Kolb8872c232013-01-29 10:33:22 -0800161 private boolean mFaceDetectionStarted = false;
162
Michael Kolb8872c232013-01-29 10:33:22 -0800163 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
164 private String mCropValue;
165 private Uri mSaveUri;
166
Ruben Brunkd217ed02013-10-08 23:31:13 -0700167 private Uri mDebugUri;
168
Angus Kongce5480e2013-01-29 17:43:48 -0800169 // We use a queue to generated names of the images to be used later
170 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800171 private NamedImages mNamedImages;
172
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800173 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800174 @Override
175 public void run() {
176 onShutterButtonClick();
177 }
178 };
179
Michael Kolb8872c232013-01-29 10:33:22 -0800180 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800181 * An unpublished intent flag requesting to return as soon as capturing is
182 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800183 */
184 private static final String EXTRA_QUICK_CAPTURE =
185 "android.intent.extra.quickCapture";
186
187 // The display rotation in degrees. This is only valid when mCameraState is
188 // not PREVIEW_STOPPED.
189 private int mDisplayRotation;
Michael Kolb8872c232013-01-29 10:33:22 -0800190 // The value for UI components like indicators.
191 private int mDisplayOrientation;
Angus Kong831347d2014-06-16 16:07:28 -0700192 // The value for cameradevice.CameraSettings.setPhotoRotationDegrees.
Michael Kolb8872c232013-01-29 10:33:22 -0800193 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700194 // Indicates whether we are using front camera
195 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800196 private boolean mFirstTimeInitialized;
197 private boolean mIsImageCaptureIntent;
198
Michael Kolb8872c232013-01-29 10:33:22 -0800199 private int mCameraState = PREVIEW_STOPPED;
200 private boolean mSnapshotOnIdle = false;
201
202 private ContentResolver mContentResolver;
203
Doris Liu2b906b82013-12-10 16:34:08 -0800204 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800205
Michael Kolb8872c232013-01-29 10:33:22 -0800206 private final PostViewPictureCallback mPostViewPictureCallback =
207 new PostViewPictureCallback();
208 private final RawPictureCallback mRawPictureCallback =
209 new RawPictureCallback();
210 private final AutoFocusCallback mAutoFocusCallback =
211 new AutoFocusCallback();
212 private final Object mAutoFocusMoveCallback =
213 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700214 ? new AutoFocusMoveCallback()
215 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800216
Michael Kolb8872c232013-01-29 10:33:22 -0800217 private long mFocusStartTime;
218 private long mShutterCallbackTime;
219 private long mPostViewPictureCallbackTime;
220 private long mRawPictureCallbackTime;
221 private long mJpegPictureCallbackTime;
222 private long mOnResumeTime;
223 private byte[] mJpegImageData;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700224 /** Touch coordinate for shutter button press. */
225 private TouchCoordinate mShutterTouchCoordinate;
226
Michael Kolb8872c232013-01-29 10:33:22 -0800227
228 // These latency time are for the CameraLatency test.
229 public long mAutoFocusTime;
230 public long mShutterLag;
231 public long mShutterToPictureDisplayedTime;
232 public long mPictureDisplayedToJpegCallbackTime;
233 public long mJpegCallbackFinishTime;
234 public long mCaptureStartTime;
235
236 // This handles everything about focus.
237 private FocusOverlayManager mFocusManager;
238
Doris Liubd1b8f92014-01-03 17:59:51 -0800239 private final int mGcamModeIndex;
Andy Huibersa31162c2014-09-02 11:27:11 -0700240 private SoundPlayer mCountdownSoundPlayer;
Doris Liubd1b8f92014-01-03 17:59:51 -0800241
Angus Kong6607dae2014-06-10 16:07:45 -0700242 private CameraCapabilities.SceneMode mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800243
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100244 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800245
Michael Kolb8872c232013-01-29 10:33:22 -0800246 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700247 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800248 private final float[] mGData = new float[3];
249 private final float[] mMData = new float[3];
250 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700251 private int mHeading = -1;
252
Sascha Haeberling375f9d12014-10-17 19:05:12 -0700253 /** Used to detect motion. We use this to release focus lock early. */
254 private MotionManager mMotionManager;
255
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800256 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700257 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700258
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800259 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800260 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800261 @Override
262 public void onMediaSaved(Uri uri) {
263 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700264 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800265 }
266 }
267 };
Angus Kong454d63f2014-05-06 14:45:59 -0700268 private boolean mShouldResizeTo16x9 = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800269
Doris Liudb8f9752014-05-12 15:25:13 -0700270 /**
Angus Kong3699c412014-05-23 15:31:09 -0700271 * We keep the flash setting before entering scene modes (HDR)
272 * and restore it after HDR is off.
273 */
274 private String mFlashModeBeforeSceneMode;
275
276 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700277 * This callback gets called when user select whether or not to
278 * turn on geo-tagging.
279 */
280 public interface LocationDialogCallback {
281 /**
282 * Gets called after user selected/unselected geo-tagging feature.
283 *
284 * @param selected whether or not geo-tagging feature is selected
285 */
286 public void onLocationTaggingSelected(boolean selected);
287 }
288
289 /**
290 * This callback defines the text that is shown in the aspect ratio selection
291 * dialog, provides the current aspect ratio, and gets notified when user changes
292 * aspect ratio selection in the dialog.
293 */
294 public interface AspectRatioDialogCallback {
295 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700296 * Returns current aspect ratio that is being used to set as default.
297 */
298 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
299
300 /**
301 * Gets notified when user has made the aspect ratio selection.
302 *
303 * @param newAspectRatio aspect ratio that user has selected
Doris Liu08b0cdd2014-05-13 19:19:55 -0700304 * @param dialogHandlingFinishedRunnable runnable to run when the operations
305 * needed to handle changes from dialog
306 * are finished.
Doris Liudb8f9752014-05-12 15:25:13 -0700307 */
Doris Liu08b0cdd2014-05-13 19:19:55 -0700308 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
309 Runnable dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700310 }
311
Angus Kongdcccc512013-08-08 17:06:03 -0700312 private void checkDisplayRotation() {
Senpo Hu9746ebe2014-10-14 11:51:56 -0700313 // Need to just be a no-op for the quick resume-pause scenario.
314 if (mPaused) {
315 return;
316 }
Angus Kongdcccc512013-08-08 17:06:03 -0700317 // Set the display orientation if display rotation has changed.
318 // Sometimes this happens when the device is held upside
319 // down and camera app is opened. Rotation animation will
320 // take some time and the rotation value we have got may be
321 // wrong. Framework does not have a callback for this now.
322 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
323 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800324 }
Angus Kongdcccc512013-08-08 17:06:03 -0700325 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
326 mHandler.postDelayed(new Runnable() {
327 @Override
328 public void run() {
329 checkDisplayRotation();
330 }
331 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800332 }
333 }
334
335 /**
336 * This Handler is used to post message back onto the main thread of the
337 * application
338 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100339 private static class MainHandler extends Handler {
340 private final WeakReference<PhotoModule> mModule;
341
342 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800343 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100344 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800345 }
346
Michael Kolb8872c232013-01-29 10:33:22 -0800347 @Override
348 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100349 PhotoModule module = mModule.get();
350 if (module == null) {
351 return;
352 }
Michael Kolb8872c232013-01-29 10:33:22 -0800353 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800354 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100355 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800356 break;
357 }
358
Angus Kong13e87c42013-11-25 10:02:47 -0800359 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100360 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800361 break;
362 }
Michael Kolb8872c232013-01-29 10:33:22 -0800363 }
364 }
365 }
366
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700367 private void switchToGcamCapture() {
368 if (mActivity != null && mGcamModeIndex != 0) {
369 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700370 settingsManager.set(SettingsManager.SCOPE_GLOBAL,
371 Keys.KEY_CAMERA_HDR_PLUS, true);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700372
373 // Disable the HDR+ button to prevent callbacks from being
374 // queued before the correct callback is attached to the button
375 // in the new module. The new module will set the enabled/disabled
376 // of this button when the module's preferred camera becomes available.
377 ButtonManager buttonManager = mActivity.getButtonManager();
Spike Sprague66d3d0d2014-08-18 14:11:33 -0700378
379 buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700380
Seth Raphael274f6e92014-05-21 17:11:53 -0700381 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
382
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700383 // Do not post this to avoid this module switch getting interleaved with
384 // other button callbacks.
385 mActivity.onModeSelected(mGcamModeIndex);
Spike Sprague66d3d0d2014-08-18 14:11:33 -0700386
387 buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700388 }
389 }
390
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800391 /**
392 * Constructs a new photo module.
393 */
Angus Kongc4e66562013-11-22 23:03:21 -0800394 public PhotoModule(AppController app) {
395 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800396 mGcamModeIndex = app.getAndroidContext().getResources()
397 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800398 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700399
Michael Kolb8872c232013-01-29 10:33:22 -0800400 @Override
Spike Sprague159e6e92014-05-27 18:26:30 -0700401 public String getPeekAccessibilityString() {
402 return mAppController.getAndroidContext()
403 .getResources().getString(R.string.photo_accessibility_peek);
404 }
405
406 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -0700407 public String getModuleStringIdentifier() {
408 return PHOTO_MODULE_STRING_ID;
409 }
410
411 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100412 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
413 mActivity = activity;
414 // TODO: Need to look at the controller interface to see if we can get
415 // rid of passing in the activity directly.
416 mAppController = mActivity;
417
418 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
419 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800420
421 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700422 mCameraId = settingsManager.getInteger(mAppController.getModuleScope(),
423 Keys.KEY_CAMERA_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800424
Doris Liudb8f9752014-05-12 15:25:13 -0700425 // TODO: Move this to SettingsManager as a part of upgrade procedure.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700426 if (!settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
427 Keys.KEY_USER_SELECTED_ASPECT_RATIO)) {
Doris Liudb8f9752014-05-12 15:25:13 -0700428 // Switch to back camera to set aspect ratio.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700429 mCameraId = settingsManager.getIntegerDefault(Keys.KEY_CAMERA_ID);
Doris Liudb8f9752014-05-12 15:25:13 -0700430 }
431
Michael Kolb8872c232013-01-29 10:33:22 -0800432 mContentResolver = mActivity.getContentResolver();
433
Michael Kolb8872c232013-01-29 10:33:22 -0800434 // Surface texture is from camera screen nail and startPreview needs it.
435 // This must be done before startPreview.
436 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800437
Michael Kolb8872c232013-01-29 10:33:22 -0800438 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800439 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Doris Liu6c751642014-05-05 18:43:26 -0700440 mUI.setCountdownFinishedListener(this);
Andy Huibersa31162c2014-09-02 11:27:11 -0700441 mCountdownSoundPlayer = new SoundPlayer(mAppController.getAndroidContext());
Doris Liu6c751642014-05-05 18:43:26 -0700442
443 // TODO: Make this a part of app controller API.
444 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
445 cancelButton.setOnClickListener(new View.OnClickListener() {
446 @Override
447 public void onClick(View view) {
448 cancelCountDown();
449 }
450 });
451 }
452
453 private void cancelCountDown() {
454 if (mUI.isCountingDown()) {
455 // Cancel on-going countdown.
456 mUI.cancelCountDown();
457 }
458 mAppController.getCameraAppUI().transitionToCapture();
459 mAppController.getCameraAppUI().showModeOptions();
Spike Sprague8d1ac992014-10-13 14:49:06 -0700460 mAppController.setShutterEnabled(true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800461 }
462
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800463 @Override
464 public boolean isUsingBottomBar() {
465 return true;
466 }
467
Michael Kolbd6954f32013-03-08 20:43:01 -0800468 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800469 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800470 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800471 setupCaptureParams();
472 }
473 }
474
475 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800476 mAppController.onPreviewStarted();
Alan Newbergerffc395d2014-09-10 13:07:18 -0700477 mAppController.setShutterEnabled(true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800478 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800479 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700480 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800481 }
482
Doris Liudb8f9752014-05-12 15:25:13 -0700483 /**
484 * Prompt the user to pick to record location and choose aspect ratio for the
485 * very first run of camera only.
486 */
487 private void settingsFirstRun() {
488 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100489
Doris Liudb8f9752014-05-12 15:25:13 -0700490 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800491 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800492 }
Doris Liudb8f9752014-05-12 15:25:13 -0700493
Erin Dahlgren6190c362014-06-13 14:12:08 -0700494 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SCOPE_GLOBAL,
495 Keys.KEY_RECORD_LOCATION);
Doris Liudb8f9752014-05-12 15:25:13 -0700496 boolean aspectRatioPrompt = !settingsManager.getBoolean(
Erin Dahlgren6190c362014-06-13 14:12:08 -0700497 SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO);
Doris Liudb8f9752014-05-12 15:25:13 -0700498 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800499 return;
500 }
Doris Liudb8f9752014-05-12 15:25:13 -0700501
Michael Kolb8872c232013-01-29 10:33:22 -0800502 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800503 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800504 if (backCameraId == -1) {
505 // If there is no back camera, do not show the prompt.
506 return;
507 }
Doris Liudb8f9752014-05-12 15:25:13 -0700508
509 if (locationPrompt) {
510 // Show both location and aspect ratio selection dialog.
511 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700512 @Override
Doris Liudb8f9752014-05-12 15:25:13 -0700513 public void onLocationTaggingSelected(boolean selected) {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700514 Keys.setLocation(mActivity.getSettingsManager(), selected,
515 mActivity.getLocationManager());
Doris Liudb8f9752014-05-12 15:25:13 -0700516 }
517 }, createAspectRatioDialogCallback());
518 } else {
519 // App upgrade. Only show aspect ratio selection.
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700520 boolean wasShown = mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
521 if (!wasShown) {
522 // If the dialog was not shown, set this flag to true so that we
523 // never have to check for it again. It means that we don't need
524 // to show the dialog on this device.
525 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
526 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
527 }
Doris Liudb8f9752014-05-12 15:25:13 -0700528 }
529 }
530
531 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -0700532 Size currentSize = mCameraSettings.getCurrentPhotoSize();
Doris Liudb8f9752014-05-12 15:25:13 -0700533 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
534 if (aspectRatio < 1f) {
535 aspectRatio = 1 / aspectRatio;
536 }
537 final AspectRatioSelector.AspectRatio currentAspectRatio;
538 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
539 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
540 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
541 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
542 } else {
543 // TODO: Log error and not show dialog.
544 return null;
545 }
546
Angus Kong6607dae2014-06-10 16:07:45 -0700547 List<Size> sizes = mCameraCapabilities.getSupportedPhotoSizes();
Doris Liudb8f9752014-05-12 15:25:13 -0700548 List<Size> pictureSizes = ResolutionUtil
549 .getDisplayableSizesFromSupported(sizes, true);
550
551 // This logic below finds the largest resolution for each aspect ratio.
552 // TODO: Move this somewhere that can be shared with SettingsActivity
553 int aspectRatio4x3Resolution = 0;
554 int aspectRatio16x9Resolution = 0;
555 Size largestSize4x3 = new Size(0, 0);
556 Size largestSize16x9 = new Size(0, 0);
557 for (Size size : pictureSizes) {
558 float pictureAspectRatio = (float) size.width() / (float) size.height();
559 pictureAspectRatio = pictureAspectRatio < 1 ?
560 1f / pictureAspectRatio : pictureAspectRatio;
561 int resolution = size.width() * size.height();
562 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
563 if (resolution > aspectRatio4x3Resolution) {
564 aspectRatio4x3Resolution = resolution;
565 largestSize4x3 = size;
566 }
567 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
568 if (resolution > aspectRatio16x9Resolution) {
569 aspectRatio16x9Resolution = resolution;
570 largestSize16x9 = size;
571 }
572 }
573 }
Doris Liudb8f9752014-05-12 15:25:13 -0700574
Doris Liu08b0cdd2014-05-13 19:19:55 -0700575 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
576 final Size size4x3ToSelect = largestSize4x3;
577 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700578
Doris Liudb8f9752014-05-12 15:25:13 -0700579 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
Doris Liudb8f9752014-05-12 15:25:13 -0700580
581 @Override
582 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
583 return currentAspectRatio;
584 }
585
586 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700587 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
588 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700589 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700590 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700591 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
592 Keys.KEY_PICTURE_SIZE_BACK,
593 largestSize4x3Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700594 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700595 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700596 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
597 Keys.KEY_PICTURE_SIZE_BACK,
598 largestSize16x9Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700599 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700600 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
601 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
602 String aspectRatio = mActivity.getSettingsManager().getString(
603 SettingsManager.SCOPE_GLOBAL,
604 Keys.KEY_USER_SELECTED_ASPECT_RATIO);
605 Log.e(TAG, "aspect ratio after setting it to true=" + aspectRatio);
Doris Liudb8f9752014-05-12 15:25:13 -0700606 if (newAspectRatio != currentAspectRatio) {
Alan Newberger19597372014-10-03 18:27:50 -0700607 Log.i(TAG, "changing aspect ratio from dialog");
Doris Liudb8f9752014-05-12 15:25:13 -0700608 stopPreview();
609 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700610 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
611 } else {
612 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700613 }
614 }
615 };
616 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700617 }
Michael Kolb8872c232013-01-29 10:33:22 -0800618
ztenghui7b265a62013-09-09 14:58:44 -0700619 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700620 public void onPreviewUIReady() {
Alan Newberger19597372014-10-03 18:27:50 -0700621 Log.i(TAG, "onPreviewUIReady");
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800622 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700623 }
624
625 @Override
626 public void onPreviewUIDestroyed() {
627 if (mCameraDevice == null) {
628 return;
629 }
630 mCameraDevice.setPreviewTexture(null);
631 stopPreview();
632 }
633
Doris Liu1dfe7822013-12-12 00:02:08 -0800634 @Override
635 public void startPreCaptureAnimation() {
Senpo Hu427a9152014-11-19 17:11:46 -0800636 mAppController.startFlashAnimation(false);
Doris Liu1dfe7822013-12-12 00:02:08 -0800637 }
638
Michael Kolbd6954f32013-03-08 20:43:01 -0800639 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800640 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800641 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800642 }
643
Michael Kolbd6954f32013-03-08 20:43:01 -0800644 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800645 if (mPaused) {
646 return;
647 }
Doris Liu6c751642014-05-05 18:43:26 -0700648 cancelCountDown();
Doris Liu6809eb82014-05-21 13:16:59 -0700649
650 mAppController.freezeScreenUntilPreviewReady();
Erin Dahlgren357b7672013-11-20 17:38:14 -0800651 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800652
Alan Newbergerd41766f2014-04-09 18:25:34 -0700653 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800654 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800655 mCameraId = mPendingSwitchCameraId;
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700656
Erin Dahlgren6190c362014-06-13 14:12:08 -0700657 settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId);
Sol Boucher44ce4b22014-08-04 23:41:38 -0700658 requestCameraOpen();
Michael Kolbd6954f32013-03-08 20:43:01 -0800659 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800660 if (mFocusManager != null) {
661 mFocusManager.removeMessages();
662 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800663
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700664 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700665 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700666 // Start switch camera animation. Post a message because
667 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800668 }
669
Sol Boucher44ce4b22014-08-04 23:41:38 -0700670 /**
671 * Uses the {@link CameraProvider} to open the currently-selected camera
672 * device, using {@link GservicesHelper} to choose between API-1 and API-2.
673 */
674 private void requestCameraOpen() {
Alan Newberger29a009c2014-09-05 12:47:42 -0700675 Log.v(TAG, "requestCameraOpen");
Sol Boucher44ce4b22014-08-04 23:41:38 -0700676 mActivity.getCameraProvider().requestCamera(mCameraId,
677 GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity));
678 }
679
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800680 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100681 new ButtonManager.ButtonCallback() {
682 @Override
683 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800684 // At the time this callback is fired, the camera id
685 // has be set to the desired camera.
686
Angus Kong97e282a2014-03-04 18:44:49 -0800687 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100688 return;
689 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800690 // If switching to back camera, and HDR+ is still on,
691 // switch back to gcam, otherwise handle callback normally.
692 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700693 if (Keys.isCameraBackFacing(settingsManager,
694 mAppController.getModuleScope())) {
695 if (Keys.requestsReturnToHdrPlus(settingsManager,
696 mAppController.getModuleScope())) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700697 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800698 return;
699 }
700 }
701
Sascha Haeberlingde303232014-02-07 02:30:53 +0100702 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800703
Alan Newbergerd41766f2014-04-09 18:25:34 -0700704 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100705 // We need to keep a preview frame for the animation before
706 // releasing the camera. This will trigger
707 // onPreviewTextureCopied.
708 // TODO: Need to animate the camera switch
709 switchCamera();
710 }
711 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800712
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800713 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100714 new ButtonManager.ButtonCallback() {
715 @Override
716 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700717 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700718 if (GcamHelper.hasGcamAsSeparateModule()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100719 // Set the camera setting to default backfacing.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700720 settingsManager.setToDefault(mAppController.getModuleScope(),
721 Keys.KEY_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700722 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100723 } else {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700724 if (Keys.isHdrOn(settingsManager)) {
Angus Kong831347d2014-06-16 16:07:28 -0700725 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
726 mCameraCapabilities.getStringifier().stringify(
727 CameraCapabilities.SceneMode.HDR));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700728 } else {
Angus Kong831347d2014-06-16 16:07:28 -0700729 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
730 mCameraCapabilities.getStringifier().stringify(
731 CameraCapabilities.SceneMode.AUTO));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700732 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100733 updateParametersSceneMode();
Senpo Huee3123b2014-09-25 10:53:12 -0700734 if (mCameraDevice != null) {
735 mCameraDevice.applySettings(mCameraSettings);
736 }
Doris Liu8ad8ad42014-03-27 14:43:31 -0700737 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100738 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800739 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100740 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800741
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800742 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
743 @Override
744 public void onClick(View v) {
745 onCaptureCancelled();
746 }
747 };
748
749 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
750 @Override
751 public void onClick(View v) {
752 onCaptureDone();
753 }
754 };
755
756 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
757 @Override
758 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800759 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800760 onCaptureRetake();
761 }
762 };
763
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800764 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700765 public void hardResetSettings(SettingsManager settingsManager) {
Erin Dahlgrene1547932014-06-23 15:17:07 -0700766 // PhotoModule should hard reset HDR+ to off,
767 // and HDR to off if HDR+ is supported.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700768 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700769 if (GcamHelper.hasGcamAsSeparateModule()) {
Erin Dahlgren21a62362014-06-24 10:13:01 -0700770 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false);
Erin Dahlgrene1547932014-06-23 15:17:07 -0700771 }
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700772 }
773
774 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800775 public HardwareSpec getHardwareSpec() {
Angus Kong831347d2014-06-16 16:07:28 -0700776 return (mCameraSettings != null ?
777 new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800778 }
779
780 @Override
781 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
782 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
783
784 bottomBarSpec.enableCamera = true;
785 bottomBarSpec.cameraCallback = mCameraCallback;
Erin Dahlgren6190c362014-06-13 14:12:08 -0700786 bottomBarSpec.enableFlash = !mAppController.getSettingsManager()
787 .getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR);
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800788 bottomBarSpec.enableHdr = true;
789 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800790 bottomBarSpec.enableGridLines = true;
Spike Sprague8f7425c2014-05-12 11:08:34 -0700791 if (mCameraCapabilities != null) {
792 bottomBarSpec.enableExposureCompensation = true;
793 bottomBarSpec.exposureCompensationSetCallback =
794 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
795 @Override
796 public void setExposure(int value) {
797 setExposureCompensation(value);
798 }
799 };
800 bottomBarSpec.minExposureCompensation =
801 mCameraCapabilities.getMinExposureCompensation();
802 bottomBarSpec.maxExposureCompensation =
803 mCameraCapabilities.getMaxExposureCompensation();
804 bottomBarSpec.exposureCompensationStep =
805 mCameraCapabilities.getExposureCompensationStep();
806 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800807
Doris Liu6c751642014-05-05 18:43:26 -0700808 bottomBarSpec.enableSelfTimer = true;
Sascha Haeberling4333fac2014-05-20 16:28:11 -0700809 bottomBarSpec.showSelfTimer = true;
Doris Liu6c751642014-05-05 18:43:26 -0700810
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800811 if (isImageCaptureIntent()) {
812 bottomBarSpec.showCancel = true;
813 bottomBarSpec.cancelCallback = mCancelCallback;
814 bottomBarSpec.showDone = true;
815 bottomBarSpec.doneCallback = mDoneCallback;
816 bottomBarSpec.showRetake = true;
817 bottomBarSpec.retakeCallback = mRetakeCallback;
818 }
819
820 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800821 }
822
Michael Kolbd6954f32013-03-08 20:43:01 -0800823 // either open a new camera or switch cameras
824 private void openCameraCommon() {
Angus Kong6607dae2014-06-10 16:07:45 -0700825 mUI.onCameraOpened(mCameraCapabilities, mCameraSettings);
Angus Kong0fb819b2013-10-08 13:44:19 -0700826 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800827 // Set hdr plus to default: off.
828 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700829 settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL,
830 Keys.KEY_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700831 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800832 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800833 }
834
ztenghui7b265a62013-09-09 14:58:44 -0700835 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800836 public void updatePreviewAspectRatio(float aspectRatio) {
837 mAppController.updatePreviewAspectRatio(aspectRatio);
838 }
839
Michael Kolb8872c232013-01-29 10:33:22 -0800840 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800841 SettingsManager settingsManager = mActivity.getSettingsManager();
842 if (settingsManager == null) {
843 Log.e(TAG, "Settings manager is null!");
844 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800845 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700846 settingsManager.setToDefault(mAppController.getCameraScope(),
847 Keys.KEY_EXPOSURE);
Michael Kolb8872c232013-01-29 10:33:22 -0800848 }
849
Michael Kolb8872c232013-01-29 10:33:22 -0800850 // Snapshots can only be taken after this is called. It should be called
851 // once only. We could have done these things in onCreate() but we want to
852 // make preview screen appear as soon as possible.
853 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700854 if (mFirstTimeInitialized || mPaused) {
855 return;
856 }
Michael Kolb8872c232013-01-29 10:33:22 -0800857
Michael Kolbd6954f32013-03-08 20:43:01 -0800858 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800859
Angus Kong86d36312013-01-31 18:22:44 -0800860 // We set the listener only when both service and shutterbutton
861 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800862 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800863
Michael Kolb8872c232013-01-29 10:33:22 -0800864 mNamedImages = new NamedImages();
865
866 mFirstTimeInitialized = true;
867 addIdleHandler();
868
Spike Spraguee6374b72014-04-25 17:24:32 -0700869 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800870 }
871
Michael Kolbd6954f32013-03-08 20:43:01 -0800872 // If the activity is paused and resumed, this method will be called in
873 // onResume.
874 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800875 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800876 mNamedImages = new NamedImages();
Angus Kong6607dae2014-06-10 16:07:45 -0700877 mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings);
Michael Kolbd6954f32013-03-08 20:43:01 -0800878 }
879
Michael Kolb8872c232013-01-29 10:33:22 -0800880 private void addIdleHandler() {
881 MessageQueue queue = Looper.myQueue();
882 queue.addIdleHandler(new MessageQueue.IdleHandler() {
883 @Override
884 public boolean queueIdle() {
885 Storage.ensureOSXCompatible();
886 return false;
887 }
888 });
889 }
890
Michael Kolb8872c232013-01-29 10:33:22 -0800891 @Override
892 public void startFaceDetection() {
Senpo Huee3123b2014-09-25 10:53:12 -0700893 if (mFaceDetectionStarted || mCameraDevice == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800894 return;
895 }
Angus Kong88289042014-04-22 16:39:42 -0700896 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800897 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700898 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700899 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800900 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700901 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800902 }
903 }
904
Michael Kolb8872c232013-01-29 10:33:22 -0800905 @Override
906 public void stopFaceDetection() {
Senpo Huee3123b2014-09-25 10:53:12 -0700907 if (!mFaceDetectionStarted || mCameraDevice == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800908 return;
909 }
Angus Kong88289042014-04-22 16:39:42 -0700910 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800911 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700912 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800913 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800914 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700915 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800916 }
917 }
918
Michael Kolb8872c232013-01-29 10:33:22 -0800919 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700920 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700921
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800922 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700923
Sascha Haeberling37f36112013-08-06 14:31:52 -0700924 public ShutterCallback(boolean needsAnimation) {
925 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700926 }
927
Michael Kolb8872c232013-01-29 10:33:22 -0800928 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700929 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800930 mShutterCallbackTime = System.currentTimeMillis();
931 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
932 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700933 if (mNeedsAnimation) {
934 mActivity.runOnUiThread(new Runnable() {
935 @Override
936 public void run() {
937 animateAfterShutter();
938 }
939 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700940 }
Michael Kolb8872c232013-01-29 10:33:22 -0800941 }
942 }
943
Angus Kong9ef99252013-07-18 18:04:19 -0700944 private final class PostViewPictureCallback
945 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800946 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800947 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800948 mPostViewPictureCallbackTime = System.currentTimeMillis();
949 Log.v(TAG, "mShutterToPostViewCallbackTime = "
950 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
951 + "ms");
952 }
953 }
954
Angus Kong9ef99252013-07-18 18:04:19 -0700955 private final class RawPictureCallback
956 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800957 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800958 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800959 mRawPictureCallbackTime = System.currentTimeMillis();
960 Log.v(TAG, "mShutterToRawCallbackTime = "
961 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
962 }
963 }
964
Angus Kong454d63f2014-05-06 14:45:59 -0700965 private static class ResizeBundle {
966 byte[] jpegData;
967 float targetAspectRatio;
968 ExifInterface exif;
969 }
970
971 /**
972 * @return Cropped image if the target aspect ratio is larger than the jpeg
973 * aspect ratio on the long axis. The original jpeg otherwise.
974 */
975 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
976
977 final byte[] jpegData = dataBundle.jpegData;
978 final ExifInterface exif = dataBundle.exif;
979 float targetAspectRatio = dataBundle.targetAspectRatio;
980
981 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
982 int originalWidth = original.getWidth();
983 int originalHeight = original.getHeight();
984 int newWidth;
985 int newHeight;
986
987 if (originalWidth > originalHeight) {
988 newHeight = (int) (originalWidth / targetAspectRatio);
989 newWidth = originalWidth;
990 } else {
991 newWidth = (int) (originalHeight / targetAspectRatio);
992 newHeight = originalHeight;
993 }
994 int xOffset = (originalWidth - newWidth)/2;
995 int yOffset = (originalHeight - newHeight)/2;
996
997 if (xOffset < 0 || yOffset < 0) {
998 return dataBundle;
999 }
1000
1001 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
1002 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
1003 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
1004
1005 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1006
1007 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
1008 dataBundle.jpegData = stream.toByteArray();
1009 return dataBundle;
1010 }
1011
Angus Kong9ef99252013-07-18 18:04:19 -07001012 private final class JpegPictureCallback
1013 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001014 Location mLocation;
1015
1016 public JpegPictureCallback(Location loc) {
1017 mLocation = loc;
1018 }
1019
1020 @Override
Angus Kong454d63f2014-05-06 14:45:59 -07001021 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Alan Newberger19597372014-10-03 18:27:50 -07001022 Log.i(TAG, "onPictureTaken");
Erin Dahlgren667630d2014-04-01 14:03:25 -07001023 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001024 if (mPaused) {
1025 return;
1026 }
Doris Liu6432cd62013-06-13 17:20:31 -07001027 if (mIsImageCaptureIntent) {
1028 stopPreview();
1029 }
Angus Kong6607dae2014-06-10 16:07:45 -07001030 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001031 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001032 }
1033
1034 mJpegPictureCallbackTime = System.currentTimeMillis();
1035 // If postview callback has arrived, the captured image is displayed
1036 // in postview callback. If not, the captured image is displayed in
1037 // raw picture callback.
1038 if (mPostViewPictureCallbackTime != 0) {
1039 mShutterToPictureDisplayedTime =
1040 mPostViewPictureCallbackTime - mShutterCallbackTime;
1041 mPictureDisplayedToJpegCallbackTime =
1042 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
1043 } else {
1044 mShutterToPictureDisplayedTime =
1045 mRawPictureCallbackTime - mShutterCallbackTime;
1046 mPictureDisplayedToJpegCallbackTime =
1047 mJpegPictureCallbackTime - mRawPictureCallbackTime;
1048 }
1049 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
1050 + mPictureDisplayedToJpegCallbackTime + "ms");
1051
Michael Kolb8872c232013-01-29 10:33:22 -08001052 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001053 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001054 }
1055
Angus Kong454d63f2014-05-06 14:45:59 -07001056 long now = System.currentTimeMillis();
1057 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1058 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1059 mJpegPictureCallbackTime = 0;
1060
1061 final ExifInterface exif = Exif.getExif(originalJpegData);
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001062 final NamedEntity name = mNamedImages.getNextNameEntity();
Angus Kong454d63f2014-05-06 14:45:59 -07001063 if (mShouldResizeTo16x9) {
1064 final ResizeBundle dataBundle = new ResizeBundle();
1065 dataBundle.jpegData = originalJpegData;
1066 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1067 dataBundle.exif = exif;
1068 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1069
1070 @Override
1071 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1072 return cropJpegDataToAspectRatio(resizeBundles[0]);
1073 }
1074
1075 @Override
1076 protected void onPostExecute(ResizeBundle result) {
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001077 saveFinalPhoto(result.jpegData, name, result.exif, camera);
Angus Kong454d63f2014-05-06 14:45:59 -07001078 }
1079 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1080
1081 } else {
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001082 saveFinalPhoto(originalJpegData, name, exif, camera);
Angus Kong454d63f2014-05-06 14:45:59 -07001083 }
1084 }
1085
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001086 void saveFinalPhoto(final byte[] jpegData, NamedEntity name, final ExifInterface exif,
1087 CameraProxy camera) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001088 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001089
Sol Boucher2192fba2014-08-19 17:24:07 -07001090 float zoomValue = 1.0f;
Angus Kong88289042014-04-22 16:39:42 -07001091 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07001092 zoomValue = mCameraSettings.getCurrentZoomRatio();
Andy Huiberse08bc042014-04-11 19:26:47 -07001093 }
Angus Kong6607dae2014-06-10 16:07:45 -07001094 boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
Andy Huibers203abe52014-05-19 13:59:01 -07001095 String flashSetting =
Erin Dahlgren6190c362014-06-13 14:12:08 -07001096 mActivity.getSettingsManager().getString(mAppController.getCameraScope(),
1097 Keys.KEY_FLASH_MODE);
1098 boolean gridLinesOn = Keys.areGridLinesOn(mActivity.getSettingsManager());
Andy Huibers10c58162014-03-29 14:06:54 -07001099 UsageStatistics.instance().photoCaptureDoneEvent(
1100 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001101 name.title + ".jpg", exif,
Andy Huibers547d7c82014-05-20 23:03:18 -07001102 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001103 (float) mTimerDuration, mShutterTouchCoordinate, mVolumeButtonClickedFlag);
1104 mShutterTouchCoordinate = null;
1105 mVolumeButtonClickedFlag = false;
Ruben Brunkd217ed02013-10-08 23:31:13 -07001106
Ruben Brunkd7488272013-10-10 18:45:53 -07001107 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001108 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001109 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1110 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001111 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001112 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1113 width = exifWidth;
1114 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001115 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001116 Size s;
Angus Kong6607dae2014-06-10 16:07:45 -07001117 s = mCameraSettings.getCurrentPhotoSize();
Angus Kong454d63f2014-05-06 14:45:59 -07001118 if ((mJpegRotation + orientation) % 180 == 0) {
1119 width = s.width();
1120 height = s.height();
1121 } else {
1122 width = s.height();
1123 height = s.width();
1124 }
Michael Kolb8872c232013-01-29 10:33:22 -08001125 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001126 String title = (name == null) ? null : name.title;
1127 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001128
1129 // Handle debug mode outputs
1130 if (mDebugUri != null) {
1131 // If using a debug uri, save jpeg there.
1132 saveToDebugUri(jpegData);
1133
1134 // Adjust the title of the debug image shown in mediastore.
1135 if (title != null) {
1136 title = DEBUG_IMAGE_PREFIX + title;
1137 }
1138 }
1139
Michael Kolb8872c232013-01-29 10:33:22 -08001140 if (title == null) {
1141 Log.e(TAG, "Unbalanced name/data pair");
1142 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001143 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001144 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001145 }
Angus Kong0d00a892013-03-26 11:40:40 -07001146 if (mHeading >= 0) {
1147 // heading direction has been updated by the sensor.
1148 ExifTag directionRefTag = exif.buildTag(
1149 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1150 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1151 ExifTag directionTag = exif.buildTag(
1152 ExifInterface.TAG_GPS_IMG_DIRECTION,
1153 new Rational(mHeading, 1));
1154 exif.setTag(directionRefTag);
1155 exif.setTag(directionTag);
1156 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001157 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001158 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001159 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001160 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001161 // Animate capture with real jpeg data instead of a preview
1162 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001163 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001164 } else {
1165 mJpegImageData = jpegData;
1166 if (!mQuickCapture) {
Alan Newberger7dabda62014-10-15 15:28:01 -07001167 Log.v(TAG, "showing UI");
Doris Liu36e56fb2013-09-11 17:38:08 -07001168 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001169 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001170 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001171 }
1172 }
1173
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001174 // Send the taken photo to remote shutter listeners, if any are
1175 // registered.
Sascha Haeberling3c6de142014-07-14 12:23:36 -07001176 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001177
Michael Kolb8872c232013-01-29 10:33:22 -08001178 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001179 // latency. It's true that someone else could write to the SD card
1180 // in the mean time and fill it, but that could have happened
1181 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001182 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001183 }
1184 }
1185
Angus Kong9ef99252013-07-18 18:04:19 -07001186 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001187 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001188 public void onAutoFocus(boolean focused, CameraProxy camera) {
1189 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001190 if (mPaused) {
1191 return;
1192 }
Michael Kolb8872c232013-01-29 10:33:22 -08001193
1194 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001195 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001196 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001197 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001198 }
1199 }
1200
Michael Kolb8872c232013-01-29 10:33:22 -08001201 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001202 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001203 @Override
1204 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001205 boolean moving, CameraProxy camera) {
1206 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001207 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001208 }
1209 }
1210
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001211 /**
1212 * This class is just a thread-safe queue for name,date holder objects.
1213 */
1214 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001215 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001216
1217 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001218 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001219 }
1220
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001221 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001222 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001223 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001224 r.date = date;
1225 mQueue.add(r);
1226 }
1227
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001228 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001229 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001230 if (!mQueue.isEmpty()) {
1231 return mQueue.remove(0);
1232 }
Michael Kolb8872c232013-01-29 10:33:22 -08001233 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001234 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001235 }
1236
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001237 public static class NamedEntity {
1238 public String title;
1239 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001240 }
1241 }
1242
1243 private void setCameraState(int state) {
1244 mCameraState = state;
1245 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001246 case PREVIEW_STOPPED:
1247 case SNAPSHOT_IN_PROGRESS:
1248 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001249 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001250 break;
1251 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001252 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001253 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001254 }
1255 }
1256
Sascha Haeberling37f36112013-08-06 14:31:52 -07001257 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001258 // Only animate when in full screen capture mode
1259 // i.e. If monkey/a user swipes to the gallery during picture taking,
1260 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001261 if (!mIsImageCaptureIntent) {
1262 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001263 }
Michael Kolb8872c232013-01-29 10:33:22 -08001264 }
1265
1266 @Override
1267 public boolean capture() {
Alan Newberger7dabda62014-10-15 15:28:01 -07001268 Log.i(TAG, "capture");
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001269 // If we are already in the middle of taking a snapshot or the image
1270 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001271 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Spike Sprague8d1ac992014-10-13 14:49:06 -07001272 || mCameraState == SWITCHING_CAMERA) {
Michael Kolb8872c232013-01-29 10:33:22 -08001273 return false;
1274 }
Spike Sprague8d1ac992014-10-13 14:49:06 -07001275 setCameraState(SNAPSHOT_IN_PROGRESS);
1276
Michael Kolb8872c232013-01-29 10:33:22 -08001277 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001278
Michael Kolb8872c232013-01-29 10:33:22 -08001279 mPostViewPictureCallbackTime = 0;
1280 mJpegImageData = null;
1281
Angus Kong6607dae2014-06-10 16:07:45 -07001282 final boolean animateBefore = (mSceneMode == CameraCapabilities.SceneMode.HDR);
Michael Kolb8872c232013-01-29 10:33:22 -08001283
1284 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001285 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -08001286 }
1287
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -08001288 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kong6607dae2014-06-10 16:07:45 -07001289 CameraUtil.setGpsParameters(mCameraSettings, loc);
1290 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb8872c232013-01-29 10:33:22 -08001291
Senpo Hubb1c72f2014-09-08 01:07:27 -07001292 // Set JPEG orientation. Even if screen UI is locked in portrait, camera orientation should
1293 // still match device orientation (e.g., users should always get landscape photos while
1294 // capturing by putting device in landscape.)
Senpo Hubb1c72f2014-09-08 01:07:27 -07001295 Characteristics info = mActivity.getCameraProvider().getCharacteristics(mCameraId);
Senpo Hu0bdc4b52014-11-17 22:56:31 -08001296 int sensorOrientation = info.getSensorOrientation();
1297 int deviceOrientation =
1298 mAppController.getOrientationManager().getDeviceOrientation().getDegrees();
1299 boolean isFrontCamera = info.isFacingFront();
1300 mJpegRotation =
1301 CameraUtil.getImageRotation(sensorOrientation, deviceOrientation, isFrontCamera);
Senpo Hubb1c72f2014-09-08 01:07:27 -07001302 mCameraDevice.setJpegOrientation(mJpegRotation);
1303
Angus Kong9ef99252013-07-18 18:04:19 -07001304 mCameraDevice.takePicture(mHandler,
1305 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001306 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001307 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001308
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001309 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001310
1311 mFaceDetectionStarted = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001312 return true;
1313 }
1314
1315 @Override
1316 public void setFocusParameters() {
1317 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1318 }
1319
Michael Kolbd6954f32013-03-08 20:43:01 -08001320 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001321 // If scene mode is set, we cannot set flash mode, white balance, and
Alan Newberger529d5f62014-12-04 10:46:49 -08001322 // focus mode, instead, we read it from driver. Some devices don't have
1323 // any scene modes, so we must check both NO_SCENE_MODE in addition to
1324 // AUTO to check where there is no actual scene mode set.
1325 if (!(CameraCapabilities.SceneMode.AUTO == mSceneMode ||
1326 CameraCapabilities.SceneMode.NO_SCENE_MODE == mSceneMode)) {
Angus Kong6607dae2014-06-10 16:07:45 -07001327 overrideCameraSettings(mCameraSettings.getCurrentFlashMode(),
1328 mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001329 }
1330 }
1331
Angus Kong6607dae2014-06-10 16:07:45 -07001332 private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode,
1333 CameraCapabilities.FocusMode focusMode) {
1334 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrene419b192013-12-03 13:10:27 -08001335 SettingsManager settingsManager = mActivity.getSettingsManager();
Alan Newberger347dccb2014-09-24 17:19:25 -07001336 if (!CameraCapabilities.FlashMode.NO_FLASH.equals(flashMode)) {
Alan Newbergere29804f2014-11-24 17:11:55 -08001337 String flashModeString = stringifier.stringify(flashMode);
1338 Log.v(TAG, "override flash setting to: " + flashModeString);
Alan Newberger347dccb2014-09-24 17:19:25 -07001339 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE,
Alan Newbergere29804f2014-11-24 17:11:55 -08001340 flashModeString);
1341 } else {
1342 Log.v(TAG, "skip setting flash mode on override due to NO_FLASH");
Alan Newberger347dccb2014-09-24 17:19:25 -07001343 }
Alan Newbergere29804f2014-11-24 17:11:55 -08001344 String focusModeString = stringifier.stringify(focusMode);
1345 Log.v(TAG, "override focus setting to: " + focusModeString);
Erin Dahlgren6190c362014-06-13 14:12:08 -07001346 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE,
Alan Newbergere29804f2014-11-24 17:11:55 -08001347 focusModeString);
Michael Kolb8872c232013-01-29 10:33:22 -08001348 }
1349
1350 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001351 public void onCameraAvailable(CameraProxy cameraProxy) {
Alan Newberger19597372014-10-03 18:27:50 -07001352 Log.i(TAG, "onCameraAvailable");
Angus Kong20fad242013-11-11 18:23:46 -08001353 if (mPaused) {
1354 return;
1355 }
1356 mCameraDevice = cameraProxy;
1357
1358 initializeCapabilities();
1359
1360 // Reset zoom value index.
Sol Boucher2192fba2014-08-19 17:24:07 -07001361 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001362 if (mFocusManager == null) {
1363 initializeFocusManager();
1364 }
Angus Kong831347d2014-06-16 16:07:28 -07001365 mFocusManager.updateCapabilities(mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001366
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001367 // Do camera parameter dependent initialization.
Angus Kong6607dae2014-06-10 16:07:45 -07001368 mCameraSettings = mCameraDevice.getSettings();
Alan Newberger19597372014-10-03 18:27:50 -07001369
1370 setCameraParameters(UPDATE_PARAM_ALL);
1371 // Set a listener which updates camera parameters based
1372 // on changed settings.
1373 SettingsManager settingsManager = mActivity.getSettingsManager();
1374 settingsManager.addListener(this);
1375 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001376
Angus Kong20fad242013-11-11 18:23:46 -08001377 startPreview();
1378
1379 onCameraOpened();
1380 }
1381
1382 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001383 public void onCaptureCancelled() {
1384 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1385 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001386 }
1387
Michael Kolbd6954f32013-03-08 20:43:01 -08001388 @Override
1389 public void onCaptureRetake() {
Alan Newberger19597372014-10-03 18:27:50 -07001390 Log.i(TAG, "onCaptureRetake");
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001391 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001392 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001393 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001394 mUI.hidePostCaptureAlert();
Spike Spragueb4a22222014-05-22 14:40:53 -07001395 mUI.hideIntentReviewImageView();
Michael Kolb8872c232013-01-29 10:33:22 -08001396 setupPreview();
1397 }
1398
Michael Kolbd6954f32013-03-08 20:43:01 -08001399 @Override
1400 public void onCaptureDone() {
Alan Newberger7dabda62014-10-15 15:28:01 -07001401 Log.i(TAG, "onCaptureDone");
Michael Kolb8872c232013-01-29 10:33:22 -08001402 if (mPaused) {
1403 return;
1404 }
1405
1406 byte[] data = mJpegImageData;
1407
1408 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001409 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001410 // caller specifies a "save uri" then write the data to its
1411 // stream. Otherwise, pass back a scaled down version of the bitmap
1412 // directly in the extras.
1413 if (mSaveUri != null) {
1414 OutputStream outputStream = null;
1415 try {
1416 outputStream = mContentResolver.openOutputStream(mSaveUri);
1417 outputStream.write(data);
1418 outputStream.close();
1419
Alan Newbergerf974ba72014-09-08 14:44:30 -07001420 Log.v(TAG, "saved result to URI: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001421 mActivity.setResultEx(Activity.RESULT_OK);
1422 mActivity.finish();
1423 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001424 Log.w(TAG, "exception saving result to URI: " + mSaveUri, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001425 // ignore exception
1426 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001427 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001428 }
1429 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001430 ExifInterface exif = Exif.getExif(data);
1431 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001432 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1433 bitmap = CameraUtil.rotate(bitmap, orientation);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001434 Log.v(TAG, "inlined bitmap into capture intent result");
Michael Kolb8872c232013-01-29 10:33:22 -08001435 mActivity.setResultEx(Activity.RESULT_OK,
1436 new Intent("inline-data").putExtra("data", bitmap));
1437 mActivity.finish();
1438 }
1439 } else {
1440 // Save the image to a temp file and invoke the cropper
1441 Uri tempUri = null;
1442 FileOutputStream tempStream = null;
1443 try {
1444 File path = mActivity.getFileStreamPath(sTempCropFilename);
1445 path.delete();
1446 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1447 tempStream.write(data);
1448 tempStream.close();
1449 tempUri = Uri.fromFile(path);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001450 Log.v(TAG, "wrote temp file for cropping to: " + sTempCropFilename);
Michael Kolb8872c232013-01-29 10:33:22 -08001451 } catch (FileNotFoundException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001452 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001453 mActivity.setResultEx(Activity.RESULT_CANCELED);
1454 mActivity.finish();
1455 return;
1456 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001457 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001458 mActivity.setResultEx(Activity.RESULT_CANCELED);
1459 mActivity.finish();
1460 return;
1461 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001462 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001463 }
1464
1465 Bundle newExtras = new Bundle();
1466 if (mCropValue.equals("circle")) {
1467 newExtras.putString("circleCrop", "true");
1468 }
1469 if (mSaveUri != null) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001470 Log.v(TAG, "setting output of cropped file to: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001471 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1472 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001473 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001474 }
1475 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001476 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001477 }
1478
Sascha Haeberling37f36112013-08-06 14:31:52 -07001479 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001480 final String CROP_ACTION = "com.android.camera.action.CROP";
1481 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001482
1483 cropIntent.setData(tempUri);
1484 cropIntent.putExtras(newExtras);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001485 Log.v(TAG, "starting CROP intent for capture");
Michael Kolb8872c232013-01-29 10:33:22 -08001486 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1487 }
1488 }
1489
Michael Kolb8872c232013-01-29 10:33:22 -08001490 @Override
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001491 public void onShutterCoordinate(TouchCoordinate coord) {
1492 mShutterTouchCoordinate = coord;
1493 }
1494
1495 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001496 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001497 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001498 }
1499
1500 @Override
1501 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001502 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Spike Sprague8d1ac992014-10-13 14:49:06 -07001503 || (mCameraState == PREVIEW_STOPPED)
1504 || !mAppController.isShutterEnabled()) {
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001505 mVolumeButtonClickedFlag = false;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001506 return;
1507 }
Michael Kolb8872c232013-01-29 10:33:22 -08001508
1509 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001510 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001511 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001512 + mActivity.getStorageSpaceBytes());
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001513 mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001514 return;
1515 }
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001516 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
1517 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
Michael Kolb8872c232013-01-29 10:33:22 -08001518
Spike Sprague8d1ac992014-10-13 14:49:06 -07001519 mAppController.setShutterEnabled(false);
1520
Erin Dahlgren6190c362014-06-13 14:12:08 -07001521 int countDownDuration = mActivity.getSettingsManager()
1522 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
Andy Huibers547d7c82014-05-20 23:03:18 -07001523 mTimerDuration = countDownDuration;
Doris Liu6c751642014-05-05 18:43:26 -07001524 if (countDownDuration > 0) {
1525 // Start count down.
1526 mAppController.getCameraAppUI().transitionToCancel();
1527 mAppController.getCameraAppUI().hideModeOptions();
1528 mUI.startCountdown(countDownDuration);
1529 return;
1530 } else {
1531 focusAndCapture();
1532 }
1533 }
1534
1535 private void focusAndCapture() {
Angus Kong6607dae2014-06-10 16:07:45 -07001536 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001537 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001538 }
Michael Kolb8872c232013-01-29 10:33:22 -08001539 // If the user wants to do a snapshot while the previous one is still
1540 // in progress, remember the fact and do it after we finish the previous
1541 // one and re-start the preview. Snapshot in progress also includes the
1542 // state that autofocus is focusing and a picture will be taken when
1543 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001544 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1545 if (!mIsImageCaptureIntent) {
1546 mSnapshotOnIdle = true;
1547 }
Michael Kolb8872c232013-01-29 10:33:22 -08001548 return;
1549 }
1550
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001551 mSnapshotOnIdle = false;
Angus Kong831347d2014-06-16 16:07:28 -07001552 mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001553 }
1554
Doris Liu6c751642014-05-05 18:43:26 -07001555 @Override
1556 public void onRemainingSecondsChanged(int remainingSeconds) {
Andy Huibersa31162c2014-09-02 11:27:11 -07001557 if (remainingSeconds == 1) {
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001558 mCountdownSoundPlayer.play(R.raw.timer_final_second, 0.6f);
Andy Huibersa31162c2014-09-02 11:27:11 -07001559 } else if (remainingSeconds == 2 || remainingSeconds == 3) {
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001560 mCountdownSoundPlayer.play(R.raw.timer_increment, 0.6f);
Andy Huibersa31162c2014-09-02 11:27:11 -07001561 }
Doris Liu6c751642014-05-05 18:43:26 -07001562 }
1563
1564 @Override
1565 public void onCountDownFinished() {
Alan Newberger7dabda62014-10-15 15:28:01 -07001566 mAppController.getCameraAppUI().transitionToCapture();
Doris Liu6c751642014-05-05 18:43:26 -07001567 mAppController.getCameraAppUI().showModeOptions();
1568 if (mPaused) {
1569 return;
1570 }
1571 focusAndCapture();
1572 }
1573
Puneet Lall4de9b722014-09-25 15:05:47 -07001574 @Override
1575 public void resume() {
1576 mPaused = false;
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001577
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001578 mCountdownSoundPlayer.loadSound(R.raw.timer_final_second);
1579 mCountdownSoundPlayer.loadSound(R.raw.timer_increment);
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001580 if (mFocusManager != null) {
1581 // If camera is not open when resume is called, focus manager will
1582 // not be initialized yet, in which case it will start listening to
1583 // preview area size change later in the initialization.
1584 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1585 }
1586 mAppController.addPreviewAreaSizeChangedListener(mUI);
1587
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001588 CameraProvider camProvider = mActivity.getCameraProvider();
1589 if (camProvider == null) {
1590 // No camera provider, the Activity is destroyed already.
1591 return;
1592 }
Sol Boucher44ce4b22014-08-04 23:41:38 -07001593 requestCameraOpen();
Angus Kong20fad242013-11-11 18:23:46 -08001594
Michael Kolb8872c232013-01-29 10:33:22 -08001595 mJpegPictureCallbackTime = 0;
Sol Boucher2192fba2014-08-19 17:24:07 -07001596 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001597
1598 mOnResumeTime = SystemClock.uptimeMillis();
1599 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001600
1601 // If first time initialization is not finished, put it in the
1602 // message queue.
1603 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001604 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001605 } else {
1606 initializeSecondTime();
1607 }
Michael Kolb8872c232013-01-29 10:33:22 -08001608
Angus Kong0d00a892013-03-26 11:40:40 -07001609 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1610 if (gsensor != null) {
1611 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1612 }
1613
1614 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1615 if (msensor != null) {
1616 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1617 }
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001618
1619 getServices().getRemoteShutterListener().onModuleReady(this);
1620 SessionStatsCollector.instance().sessionActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001621 }
1622
Angus Kongc4e66562013-11-22 23:03:21 -08001623 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001624 * @return Whether the currently active camera is front-facing.
1625 */
1626 private boolean isCameraFrontFacing() {
Sol Boucher43e18132014-06-19 15:03:18 -07001627 return mAppController.getCameraProvider().getCharacteristics(mCameraId)
1628 .isFacingFront();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001629 }
1630
1631 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001632 * The focus manager is the first UI related element to get initialized, and
1633 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001634 */
1635 private void initializeFocusManager() {
1636 // Create FocusManager object. startPreview needs it.
1637 // if mFocusManager not null, reuse it
1638 // otherwise create a new instance
1639 if (mFocusManager != null) {
1640 mFocusManager.removeMessages();
1641 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001642 mMirror = isCameraFrontFacing();
Angus Kong831347d2014-06-16 16:07:28 -07001643 String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
Angus Kongc4e66562013-11-22 23:03:21 -08001644 R.array.pref_camera_focusmode_default_array);
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -07001645 ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
1646 new ArrayList<CameraCapabilities.FocusMode>();
Angus Kong831347d2014-06-16 16:07:28 -07001647 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
1648 for (String modeString : defaultFocusModesStrings) {
1649 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
1650 if (mode != null) {
1651 defaultFocusModes.add(mode);
1652 }
1653 }
1654 mFocusManager =
1655 new FocusOverlayManager(mAppController, defaultFocusModes,
1656 mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
Paul Rohde987ee642014-12-05 12:17:15 -08001657 mUI.getFocusRing());
Sascha Haeberling375f9d12014-10-17 19:05:12 -07001658 mMotionManager = getServices().getMotionManager();
1659 if (mMotionManager != null) {
1660 mMotionManager.addListener(mFocusManager);
Kevin Gabayanfb333362014-06-02 14:48:20 -07001661 }
Angus Kongc4e66562013-11-22 23:03:21 -08001662 }
Doris Liu482de022013-12-18 19:18:16 -08001663 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001664 }
1665
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001666 /**
1667 * @return Whether we are resuming from within the lockscreen.
1668 */
1669 private boolean isResumeFromLockscreen() {
1670 String action = mActivity.getIntent().getAction();
1671 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1672 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
1673 }
1674
Angus Kong20fad242013-11-11 18:23:46 -08001675 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001676 public void pause() {
Alan Newberger7dabda62014-10-15 15:28:01 -07001677 Log.v(TAG, "pause");
Michael Kolb8872c232013-01-29 10:33:22 -08001678 mPaused = true;
Sascha Haeberling1bf08892014-06-18 09:38:06 -07001679 getServices().getRemoteShutterListener().onModuleExit();
Andy Huibers10c58162014-03-29 14:06:54 -07001680 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001681
Angus Kong0d00a892013-03-26 11:40:40 -07001682 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1683 if (gsensor != null) {
1684 mSensorManager.unregisterListener(this, gsensor);
1685 }
1686
1687 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1688 if (msensor != null) {
1689 mSensorManager.unregisterListener(this, msensor);
1690 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001691
Michael Kolb8872c232013-01-29 10:33:22 -08001692 // Reset the focus first. Camera CTS does not guarantee that
1693 // cancelAutoFocus is allowed after preview stops.
1694 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1695 mCameraDevice.cancelAutoFocus();
1696 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001697
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001698 // If the camera has not been opened asynchronously yet,
1699 // and startPreview hasn't been called, then this is a no-op.
1700 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001701 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001702 cancelCountDown();
Andy Huibers877c2b62014-10-03 15:22:47 -07001703 mCountdownSoundPlayer.unloadSound(R.raw.timer_final_second);
1704 mCountdownSoundPlayer.unloadSound(R.raw.timer_increment);
Michael Kolb8872c232013-01-29 10:33:22 -08001705
Angus Kongce5480e2013-01-29 17:43:48 -08001706 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001707 // If we are in an image capture intent and has taken
1708 // a picture, we just clear it in onPause.
1709 mJpegImageData = null;
1710
Angus Kongdcccc512013-08-08 17:06:03 -07001711 // Remove the messages and runnables in the queue.
1712 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001713
Sascha Haeberling375f9d12014-10-17 19:05:12 -07001714 if (mMotionManager != null) {
1715 mMotionManager.removeListener(mFocusManager);
1716 mMotionManager = null;
1717 }
1718
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001719 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001720 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001721 mUI.onPause();
1722
Michael Kolb8872c232013-01-29 10:33:22 -08001723 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001724 if (mFocusManager != null) {
1725 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001726 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001727 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001728 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001729 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001730
1731 SettingsManager settingsManager = mActivity.getSettingsManager();
1732 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001733 }
1734
Angus Kong20fad242013-11-11 18:23:46 -08001735 @Override
1736 public void destroy() {
Andy Huibers877c2b62014-10-03 15:22:47 -07001737 mCountdownSoundPlayer.release();
Angus Kong20fad242013-11-11 18:23:46 -08001738 }
1739
1740 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001741 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001742 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001743 }
1744
1745 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001746 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001747 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001748 setDisplayOrientation();
1749 }
1750 }
1751
Michael Kolb8872c232013-01-29 10:33:22 -08001752 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001753 return isCameraIdle()
1754 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001755 }
1756
1757 @Override
1758 public void autoFocus() {
Senpo Hu4ba28292014-09-26 11:05:08 -07001759 if (mCameraDevice == null) {
1760 return;
1761 }
Andy Huibers10c58162014-03-29 14:06:54 -07001762 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001763 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001764 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001765 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001766 setCameraState(FOCUSING);
1767 }
1768
1769 @Override
1770 public void cancelAutoFocus() {
Senpo Hu4ba28292014-09-26 11:05:08 -07001771 if (mCameraDevice == null) {
1772 return;
1773 }
Michael Kolb8872c232013-01-29 10:33:22 -08001774 mCameraDevice.cancelAutoFocus();
1775 setCameraState(IDLE);
1776 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1777 }
1778
Michael Kolb8872c232013-01-29 10:33:22 -08001779 @Override
1780 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001781 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1782 || mCameraState == SNAPSHOT_IN_PROGRESS
1783 || mCameraState == SWITCHING_CAMERA
1784 || mCameraState == PREVIEW_STOPPED) {
1785 return;
1786 }
1787
1788 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001789 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1790 return;
1791 }
Doris Liu482de022013-12-18 19:18:16 -08001792 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001793 }
1794
1795 @Override
1796 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001797 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001798 }
1799
1800 @Override
1801 public boolean onKeyDown(int keyCode, KeyEvent event) {
1802 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001803 case KeyEvent.KEYCODE_VOLUME_UP:
1804 case KeyEvent.KEYCODE_VOLUME_DOWN:
1805 case KeyEvent.KEYCODE_FOCUS:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001806 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1807 !mActivity.getCameraAppUI().isInIntentReview()) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001808 if (event.getRepeatCount() == 0) {
1809 onShutterButtonFocus(true);
1810 }
1811 return true;
1812 }
1813 return false;
1814 case KeyEvent.KEYCODE_CAMERA:
1815 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1816 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001817 }
1818 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001819 case KeyEvent.KEYCODE_DPAD_CENTER:
1820 // If we get a dpad center event without any focused view, move
1821 // the focus to the shutter button and press it.
1822 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1823 // Start auto-focus immediately to reduce shutter lag. After
1824 // the shutter button gets the focus, onShutterButtonFocus()
1825 // will be called again but it is fine.
1826 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001827 }
1828 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001829 }
1830 return false;
1831 }
1832
1833 @Override
1834 public boolean onKeyUp(int keyCode, KeyEvent event) {
1835 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001836 case KeyEvent.KEYCODE_VOLUME_UP:
1837 case KeyEvent.KEYCODE_VOLUME_DOWN:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001838 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1839 !mActivity.getCameraAppUI().isInIntentReview()) {
1840 if (mUI.isCountingDown()) {
1841 cancelCountDown();
1842 } else {
1843 mVolumeButtonClickedFlag = true;
1844 onShutterButtonClick();
1845 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001846 return true;
1847 }
1848 return false;
1849 case KeyEvent.KEYCODE_FOCUS:
1850 if (mFirstTimeInitialized) {
1851 onShutterButtonFocus(false);
1852 }
Michael Kolb8872c232013-01-29 10:33:22 -08001853 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001854 }
1855 return false;
1856 }
1857
Michael Kolb8872c232013-01-29 10:33:22 -08001858 private void closeCamera() {
1859 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001860 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001861 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001862 mCameraDevice.setFaceDetectionCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001863
Michael Kolb8872c232013-01-29 10:33:22 -08001864 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001865 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001866 mCameraDevice = null;
1867 setCameraState(PREVIEW_STOPPED);
1868 mFocusManager.onCameraReleased();
1869 }
1870 }
1871
1872 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001873 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
Sol Boucher43e18132014-06-19 15:03:18 -07001874 Characteristics info =
1875 mActivity.getCameraProvider().getCharacteristics(mCameraId);
Sol Boucher73ec9852014-07-24 23:59:21 -07001876 mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
Michael Kolbd6954f32013-03-08 20:43:01 -08001877 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001878 if (mFocusManager != null) {
1879 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1880 }
Doris Liu6432cd62013-06-13 17:20:31 -07001881 // Change the camera display orientation
1882 if (mCameraDevice != null) {
Sol Boucher73ec9852014-07-24 23:59:21 -07001883 mCameraDevice.setDisplayOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001884 }
Alan Newbergere9f7b2d2014-10-22 17:42:47 -07001885 Log.v(TAG, "setDisplayOrientation (screen:preview) " +
1886 mDisplayRotation + ":" + mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001887 }
1888
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001889 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001890 private void setupPreview() {
Alan Newberger19597372014-10-03 18:27:50 -07001891 Log.i(TAG, "setupPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001892 mFocusManager.resetTouchFocus();
1893 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001894 }
1895
Angus Kong20fad242013-11-11 18:23:46 -08001896 /**
1897 * Returns whether we can/should start the preview or not.
1898 */
1899 private boolean checkPreviewPreconditions() {
1900 if (mPaused) {
1901 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001902 }
Michael Kolb8872c232013-01-29 10:33:22 -08001903
Angus Kong20fad242013-11-11 18:23:46 -08001904 if (mCameraDevice == null) {
1905 Log.w(TAG, "startPreview: camera device not ready yet.");
1906 return false;
1907 }
1908
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001909 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001910 if (st == null) {
1911 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001912 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001913 }
1914
1915 if (!mCameraPreviewParamsReady) {
1916 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001917 return false;
1918 }
1919 return true;
1920 }
1921
1922 /**
1923 * The start/stop preview should only run on the UI thread.
1924 */
1925 private void startPreview() {
Alan Newberger19597372014-10-03 18:27:50 -07001926 if (mCameraDevice == null) {
1927 Log.i(TAG, "attempted to start preview before camera device");
Puneet Lall70a96522014-09-11 17:33:21 -07001928 // do nothing
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001929 return;
1930 }
Alan Newberger19597372014-10-03 18:27:50 -07001931
1932 if (!checkPreviewPreconditions()) {
1933 return;
1934 }
1935
Alan Newberger19597372014-10-03 18:27:50 -07001936 setDisplayOrientation();
1937
1938 if (!mSnapshotOnIdle) {
1939 // If the focus mode is continuous autofocus, call cancelAutoFocus
1940 // to resume it because it may have been paused by autoFocus call.
1941 if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
1942 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
1943 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001944 }
Alan Newberger19597372014-10-03 18:27:50 -07001945 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1946 }
Michael Kolb8872c232013-01-29 10:33:22 -08001947
Alan Newberger5a64d112014-11-03 22:44:14 -08001948 // Nexus 4 must have picture size set to > 640x480 before other
1949 // parameters are set in setCameraParameters, b/18227551. This call to
1950 // updateParametersPictureSize should occur before setCameraParameters
1951 // to address the issue.
Alan Newberger19597372014-10-03 18:27:50 -07001952 updateParametersPictureSize();
Angus Kong20fad242013-11-11 18:23:46 -08001953
Alan Newberger5a64d112014-11-03 22:44:14 -08001954 setCameraParameters(UPDATE_PARAM_ALL);
1955
Alan Newberger19597372014-10-03 18:27:50 -07001956 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Puneet Lall70a96522014-09-11 17:33:21 -07001957
Alan Newberger19597372014-10-03 18:27:50 -07001958 Log.i(TAG, "startPreview");
1959 // If we're using API2 in portability layers, don't use startPreviewWithCallback()
1960 // b/17576554
1961 CameraAgent.CameraStartPreviewCallback startPreviewCallback =
1962 new CameraAgent.CameraStartPreviewCallback() {
1963 @Override
1964 public void onPreviewStarted() {
1965 mFocusManager.onPreviewStarted();
1966 PhotoModule.this.onPreviewStarted();
1967 SessionStatsCollector.instance().previewActive(true);
1968 if (mSnapshotOnIdle) {
1969 mHandler.post(mDoSnapRunnable);
Spike Sprague3c3b31d2014-09-08 10:43:04 -07001970 }
Alan Newberger19597372014-10-03 18:27:50 -07001971 }
1972 };
1973 if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity)) {
1974 mCameraDevice.startPreview();
1975 startPreviewCallback.onPreviewStarted();
1976 } else {
1977 mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
1978 startPreviewCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001979 }
1980 }
1981
Michael Kolbd6954f32013-03-08 20:43:01 -08001982 @Override
1983 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001984 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001985 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001986 mCameraDevice.stopPreview();
1987 mFaceDetectionStarted = false;
1988 }
1989 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001990 if (mFocusManager != null) {
1991 mFocusManager.onPreviewStopped();
1992 }
Andy Huibers10c58162014-03-29 14:06:54 -07001993 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001994 }
1995
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001996 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -07001997 public void onSettingChanged(SettingsManager settingsManager, String key) {
1998 if (key.equals(Keys.KEY_FLASH_MODE)) {
1999 updateParametersFlashMode();
2000 }
2001 if (key.equals(Keys.KEY_CAMERA_HDR)) {
2002 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2003 Keys.KEY_CAMERA_HDR)) {
2004 // HDR is on.
2005 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
2006 mFlashModeBeforeSceneMode = settingsManager.getString(
2007 mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
2008 } else {
2009 if (mFlashModeBeforeSceneMode != null) {
2010 settingsManager.set(mAppController.getCameraScope(),
2011 Keys.KEY_FLASH_MODE,
2012 mFlashModeBeforeSceneMode);
2013 updateParametersFlashMode();
2014 mFlashModeBeforeSceneMode = null;
Angus Kong8c07fe92014-05-22 14:42:45 -07002015 }
Erin Dahlgren6190c362014-06-13 14:12:08 -07002016 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002017 }
2018 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08002019
2020 if (mCameraDevice != null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002021 mCameraDevice.applySettings(mCameraSettings);
Erin Dahlgren1648c362014-01-06 15:06:04 -08002022 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002023 }
2024
Michael Kolb8872c232013-01-29 10:33:22 -08002025 private void updateCameraParametersInitialize() {
2026 // Reset preview frame rate to the maximum because it may be lowered by
2027 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07002028 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07002029 if (fpsRange != null && fpsRange.length > 0) {
Angus Kong831347d2014-06-16 16:07:28 -07002030 mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
Michael Kolb8872c232013-01-29 10:33:22 -08002031 }
2032
Angus Kong831347d2014-06-16 16:07:28 -07002033 mCameraSettings.setRecordingHintEnabled(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002034
Angus Kong6607dae2014-06-10 16:07:45 -07002035 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
2036 mCameraSettings.setVideoStabilization(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002037 }
2038 }
2039
2040 private void updateCameraParametersZoom() {
2041 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07002042 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002043 mCameraSettings.setZoomRatio(mZoomValue);
Michael Kolb8872c232013-01-29 10:33:22 -08002044 }
2045 }
2046
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002047 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002048 private void setAutoExposureLockIfSupported() {
2049 if (mAeLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002050 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002051 }
2052 }
2053
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002054 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002055 private void setAutoWhiteBalanceLockIfSupported() {
2056 if (mAwbLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002057 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002058 }
2059 }
2060
Michael Kolb8872c232013-01-29 10:33:22 -08002061 private void setFocusAreasIfSupported() {
2062 if (mFocusAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002063 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002064 }
2065 }
2066
Michael Kolb8872c232013-01-29 10:33:22 -08002067 private void setMeteringAreasIfSupported() {
2068 if (mMeteringAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002069 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002070 }
2071 }
2072
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002073 private void updateCameraParametersPreference() {
Spike Spragueae56c122014-09-19 11:18:46 -07002074 // some monkey tests can get here when shutting the app down
2075 // make sure mCameraDevice is still valid, b/17580046
2076 if (mCameraDevice == null) {
2077 return;
2078 }
2079
Michael Kolb8872c232013-01-29 10:33:22 -08002080 setAutoExposureLockIfSupported();
2081 setAutoWhiteBalanceLockIfSupported();
2082 setFocusAreasIfSupported();
2083 setMeteringAreasIfSupported();
2084
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002085 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07002086 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002087 mCameraSettings
2088 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Andy Huibers10c58162014-03-29 14:06:54 -07002089 SessionStatsCollector.instance().autofocusActive(
Angus Kong831347d2014-06-16 16:07:28 -07002090 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
2091 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
2092 );
Michael Kolbd3253f22013-07-12 11:36:47 -07002093
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002094 // Set JPEG quality.
2095 updateParametersPictureQuality();
2096
2097 // For the following settings, we need to check if the settings are
2098 // still supported by latest driver, if not, ignore the settings.
2099
2100 // Set exposure compensation
2101 updateParametersExposureCompensation();
2102
2103 // Set the scene mode: also sets flash and white balance.
2104 updateParametersSceneMode();
2105
2106 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2107 updateAutoFocusMoveCallback();
2108 }
2109 }
2110
Alan Newberger19597372014-10-03 18:27:50 -07002111 /**
2112 * This method sets picture size parameters. Size parameters should only be
2113 * set when the preview is stopped, and so this method is only invoked in
2114 * {@link #startPreview()} just before starting the preview.
2115 */
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002116 private void updateParametersPictureSize() {
Senpo Huee3123b2014-09-25 10:53:12 -07002117 if (mCameraDevice == null) {
Alan Newberger19597372014-10-03 18:27:50 -07002118 Log.w(TAG, "attempting to set picture size without caemra device");
Senpo Huee3123b2014-09-25 10:53:12 -07002119 return;
2120 }
2121
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002122 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002123 String pictureSizeKey = isCameraFrontFacing() ? Keys.KEY_PICTURE_SIZE_FRONT
2124 : Keys.KEY_PICTURE_SIZE_BACK;
2125 String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL,
2126 pictureSizeKey);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01002127
Angus Kong6607dae2014-06-10 16:07:45 -07002128 List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes();
Andy Huibers90c7ad52014-05-20 22:13:54 -07002129 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2130 mCameraDevice.getCameraId(), supported);
Angus Kong6607dae2014-06-10 16:07:45 -07002131 SettingsUtil.setCameraPictureSize(pictureSize, supported, mCameraSettings,
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002132 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07002133
2134 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2135 mCameraDevice.getCameraId());
2136 if (ApiHelper.IS_NEXUS_5) {
2137 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2138 mShouldResizeTo16x9 = true;
2139 } else {
2140 mShouldResizeTo16x9 = false;
2141 }
2142 }
Michael Kolb8872c232013-01-29 10:33:22 -08002143
2144 // Set a preview size that is closest to the viewfinder height and has
2145 // the right aspect ratio.
Angus Kong6607dae2014-06-10 16:07:45 -07002146 List<Size> sizes = mCameraCapabilities.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07002147 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002148 (double) size.width() / size.height());
Angus Kong6607dae2014-06-10 16:07:45 -07002149 Size original = mCameraSettings.getCurrentPreviewSize();
2150 if (!optimalSize.equals(original)) {
Alan Newberger2631a152014-09-24 14:23:30 -07002151 Log.v(TAG, "setting preview size. optimal: " + optimalSize + "original: " + original);
Angus Kong6607dae2014-06-10 16:07:45 -07002152 mCameraSettings.setPreviewSize(optimalSize);
Doris Liu6432cd62013-06-13 17:20:31 -07002153
Alan Newberger19597372014-10-03 18:27:50 -07002154 mCameraDevice.applySettings(mCameraSettings);
Angus Kong6607dae2014-06-10 16:07:45 -07002155 mCameraSettings = mCameraDevice.getSettings();
Michael Kolb8872c232013-01-29 10:33:22 -08002156 }
Doris Liu95405742013-11-05 15:25:26 -08002157
Angus Kong00b7b102014-04-24 15:46:52 -07002158 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
Alan Newberger73514152014-09-10 15:03:27 -07002159 Log.v(TAG, "updating aspect ratio");
Angus Kong00b7b102014-04-24 15:46:52 -07002160 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2161 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002162 }
Alan Newberger73514152014-09-10 15:03:27 -07002163 Log.d(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002164 }
Michael Kolb8872c232013-01-29 10:33:22 -08002165
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002166 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002167 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2168 CameraProfile.QUALITY_HIGH);
Angus Kong6607dae2014-06-10 16:07:45 -07002169 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002170 }
Michael Kolb8872c232013-01-29 10:33:22 -08002171
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002172 private void updateParametersExposureCompensation() {
2173 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002174 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2175 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2176 int value = settingsManager.getInteger(mAppController.getCameraScope(),
2177 Keys.KEY_EXPOSURE);
Angus Kong88289042014-04-22 16:39:42 -07002178 int max = mCameraCapabilities.getMaxExposureCompensation();
2179 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002180 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002181 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002182 } else {
2183 Log.w(TAG, "invalid exposure range: " + value);
2184 }
Michael Kolb8872c232013-01-29 10:33:22 -08002185 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002186 // If exposure compensation is not enabled, reset the exposure compensation value.
2187 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002188 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002189
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002190 }
2191
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002192 private void updateParametersSceneMode() {
Angus Kong6607dae2014-06-10 16:07:45 -07002193 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002194 SettingsManager settingsManager = mActivity.getSettingsManager();
2195
Erin Dahlgren6190c362014-06-13 14:12:08 -07002196 mSceneMode = stringifier.
2197 sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2198 Keys.KEY_SCENE_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002199 if (mCameraCapabilities.supports(mSceneMode)) {
2200 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
2201 mCameraSettings.setSceneMode(mSceneMode);
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002202
2203 // Setting scene mode will change the settings of flash mode,
2204 // white balance, and focus mode. Here we read back the
2205 // parameters, so we can know those settings.
Angus Kong6607dae2014-06-10 16:07:45 -07002206 mCameraDevice.applySettings(mCameraSettings);
2207 mCameraSettings = mCameraDevice.getSettings();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002208 }
2209 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002210 mSceneMode = mCameraSettings.getCurrentSceneMode();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002211 if (mSceneMode == null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002212 mSceneMode = CameraCapabilities.SceneMode.AUTO;
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002213 }
2214 }
2215
Angus Kong6607dae2014-06-10 16:07:45 -07002216 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
Michael Kolb8872c232013-01-29 10:33:22 -08002217 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002218 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002219
Michael Kolb8872c232013-01-29 10:33:22 -08002220 // Set focus mode.
2221 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002222 mCameraSettings.setFocusMode(
2223 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Michael Kolb8872c232013-01-29 10:33:22 -08002224 } else {
Angus Kong831347d2014-06-16 16:07:28 -07002225 mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08002226 }
Michael Kolb8872c232013-01-29 10:33:22 -08002227 }
2228
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002229 private void updateParametersFlashMode() {
2230 SettingsManager settingsManager = mActivity.getSettingsManager();
2231
Angus Kong6607dae2014-06-10 16:07:45 -07002232 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
Erin Dahlgren6190c362014-06-13 14:12:08 -07002233 .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2234 Keys.KEY_FLASH_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002235 if (mCameraCapabilities.supports(flashMode)) {
2236 mCameraSettings.setFlashMode(flashMode);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002237 }
2238 }
2239
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002240 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002241 private void updateAutoFocusMoveCallback() {
Senpo Huee3123b2014-09-25 10:53:12 -07002242 if (mCameraDevice == null) {
2243 return;
2244 }
Angus Kong6607dae2014-06-10 16:07:45 -07002245 if (mCameraSettings.getCurrentFocusMode() ==
2246 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
Angus Kong9ef99252013-07-18 18:04:19 -07002247 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002248 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002249 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002250 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002251 }
2252 }
2253
Spike Spragueabf54e22014-03-27 15:41:28 -07002254 /**
2255 * Sets the exposure compensation to the given value and also updates settings.
2256 *
2257 * @param value exposure compensation value to be set
2258 */
2259 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002260 int max = mCameraCapabilities.getMaxExposureCompensation();
2261 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002262 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002263 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002264 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002265 settingsManager.set(mAppController.getCameraScope(),
2266 Keys.KEY_EXPOSURE, value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002267 } else {
2268 Log.w(TAG, "invalid exposure range: " + value);
2269 }
2270 }
2271
Michael Kolb8872c232013-01-29 10:33:22 -08002272 // We separate the parameters into several subsets, so we can update only
2273 // the subsets actually need updating. The PREFERENCE set needs extra
2274 // locking because the preference can be changed from GLThread as well.
2275 private void setCameraParameters(int updateSet) {
2276 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2277 updateCameraParametersInitialize();
2278 }
2279
2280 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2281 updateCameraParametersZoom();
2282 }
2283
2284 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002285 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002286 }
2287
Spike Sprague5cc48d62014-09-22 10:52:32 -07002288 if (mCameraDevice != null) {
2289 mCameraDevice.applySettings(mCameraSettings);
2290 }
Michael Kolb8872c232013-01-29 10:33:22 -08002291 }
2292
2293 // If the Camera is idle, update the parameters immediately, otherwise
2294 // accumulate them in mUpdateSet and update later.
2295 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2296 mUpdateSet |= additionalUpdateSet;
2297 if (mCameraDevice == null) {
2298 // We will update all the parameters when we open the device, so
2299 // we don't need to do anything now.
2300 mUpdateSet = 0;
2301 return;
2302 } else if (isCameraIdle()) {
2303 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002304 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002305 mUpdateSet = 0;
2306 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002307 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2308 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002309 }
2310 }
2311 }
2312
ztenghui7b265a62013-09-09 14:58:44 -07002313 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002314 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002315 return (mCameraState == IDLE) ||
2316 (mCameraState == PREVIEW_STOPPED) ||
2317 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002318 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002319 }
2320
ztenghui7b265a62013-09-09 14:58:44 -07002321 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002322 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002323 String action = mActivity.getIntent().getAction();
2324 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002325 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002326 }
2327
2328 private void setupCaptureParams() {
2329 Bundle myExtras = mActivity.getIntent().getExtras();
2330 if (myExtras != null) {
2331 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2332 mCropValue = myExtras.getString("crop");
2333 }
2334 }
2335
Michael Kolb8872c232013-01-29 10:33:22 -08002336 private void initializeCapabilities() {
Angus Kong88289042014-04-22 16:39:42 -07002337 mCameraCapabilities = mCameraDevice.getCapabilities();
2338 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2339 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2340 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2341 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kong6607dae2014-06-10 16:07:45 -07002342 mContinuousFocusSupported =
2343 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002344 }
2345
Michael Kolb8872c232013-01-29 10:33:22 -08002346 @Override
Sol Boucher2192fba2014-08-19 17:24:07 -07002347 public void onZoomChanged(float ratio) {
Michael Kolbd6954f32013-03-08 20:43:01 -08002348 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002349 if (mPaused) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002350 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002351 }
Sol Boucher2192fba2014-08-19 17:24:07 -07002352 mZoomValue = ratio;
Angus Kong6607dae2014-06-10 16:07:45 -07002353 if (mCameraSettings == null || mCameraDevice == null) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002354 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002355 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002356 // Set zoom parameters asynchronously
Sol Boucher2192fba2014-08-19 17:24:07 -07002357 mCameraSettings.setZoomRatio(mZoomValue);
Angus Kong6607dae2014-06-10 16:07:45 -07002358 mCameraDevice.applySettings(mCameraSettings);
Angus Kongce5480e2013-01-29 17:43:48 -08002359 }
2360
2361 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002362 public int getCameraState() {
2363 return mCameraState;
2364 }
2365
2366 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002367 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002368 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002369 }
Angus Kong86d36312013-01-31 18:22:44 -08002370
2371 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002372 public void onLowMemory() {
2373 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002374 }
Angus Kong0d00a892013-03-26 11:40:40 -07002375
2376 @Override
2377 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2378 }
2379
2380 @Override
2381 public void onSensorChanged(SensorEvent event) {
2382 int type = event.sensor.getType();
2383 float[] data;
2384 if (type == Sensor.TYPE_ACCELEROMETER) {
2385 data = mGData;
2386 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2387 data = mMData;
2388 } else {
2389 // we should not be here.
2390 return;
2391 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002392 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002393 data[i] = event.values[i];
2394 }
2395 float[] orientation = new float[3];
2396 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2397 SensorManager.getOrientation(mR, orientation);
2398 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2399 if (mHeading < 0) {
2400 mHeading += 360;
2401 }
Angus Kong0d00a892013-03-26 11:40:40 -07002402 }
Doris Liu6432cd62013-06-13 17:20:31 -07002403
Ruben Brunkd217ed02013-10-08 23:31:13 -07002404 // For debugging only.
2405 public void setDebugUri(Uri uri) {
2406 mDebugUri = uri;
2407 }
2408
2409 // For debugging only.
2410 private void saveToDebugUri(byte[] data) {
2411 if (mDebugUri != null) {
2412 OutputStream outputStream = null;
2413 try {
2414 outputStream = mContentResolver.openOutputStream(mDebugUri);
2415 outputStream.write(data);
2416 outputStream.close();
2417 } catch (IOException e) {
2418 Log.e(TAG, "Exception while writing debug jpeg file", e);
2419 } finally {
2420 CameraUtil.closeSilently(outputStream);
2421 }
2422 }
2423 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002424
2425 @Override
2426 public void onRemoteShutterPress() {
Sascha Haeberling57d4f742014-06-25 11:05:49 -07002427 mHandler.post(new Runnable() {
2428 @Override
2429 public void run() {
2430 focusAndCapture();
2431 }
2432 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002433 }
Michael Kolb8872c232013-01-29 10:33:22 -08002434}