blob: b729aa987068088399b00f8303f936ddb8863587 [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.annotation.TargetApi;
20import android.app.Activity;
Michael Kolb8872c232013-01-29 10:33:22 -080021import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070022import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080023import android.content.Intent;
Michael Kolb8872c232013-01-29 10:33:22 -080024import android.graphics.Bitmap;
Angus Kong454d63f2014-05-06 14:45:59 -070025import android.graphics.BitmapFactory;
Michael Kolb8872c232013-01-29 10:33:22 -080026import android.graphics.SurfaceTexture;
Angus Kong0d00a892013-03-26 11:40:40 -070027import android.hardware.Sensor;
28import android.hardware.SensorEvent;
29import android.hardware.SensorEventListener;
30import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080031import android.location.Location;
32import android.media.CameraProfile;
33import android.net.Uri;
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -070034import android.os.AsyncTask;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070035import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080036import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080037import android.os.Handler;
38import android.os.Looper;
39import android.os.Message;
40import android.os.MessageQueue;
41import android.os.SystemClock;
42import android.provider.MediaStore;
Michael Kolb8872c232013-01-29 10:33:22 -080043import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080044import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080045import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080046
Sameer Padala2c8cc452013-11-05 18:49:12 -080047import com.android.camera.PhotoModule.NamedImages.NamedEntity;
48import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080049import com.android.camera.app.CameraAppUI;
Angus Kong0b9eb5b2014-04-30 15:03:33 -070050import com.android.camera.app.CameraProvider;
Sascha Haeberlingf9cba4e2014-04-22 09:11:28 -070051import com.android.camera.app.MediaSaver;
52import com.android.camera.app.MemoryManager;
53import com.android.camera.app.MemoryManager.MemoryListener;
Kevin Gabayanfb333362014-06-02 14:48:20 -070054import com.android.camera.app.MotionManager;
Angus Kong2bca2102014-03-11 16:27:30 -070055import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070056import com.android.camera.exif.ExifInterface;
57import com.android.camera.exif.ExifTag;
58import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080059import com.android.camera.hardware.HardwareSpec;
60import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080061import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080062import com.android.camera.remote.RemoteCameraModule;
Andy Huibers90c7ad52014-05-20 22:13:54 -070063import com.android.camera.settings.CameraPictureSizesCacher;
Erin Dahlgren6190c362014-06-13 14:12:08 -070064import com.android.camera.settings.Keys;
Angus Kong454d63f2014-05-06 14:45:59 -070065import com.android.camera.settings.ResolutionUtil;
Erin Dahlgren357b7672013-11-20 17:38:14 -080066import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010067import com.android.camera.settings.SettingsUtil;
Doris Liu6c751642014-05-05 18:43:26 -070068import com.android.camera.ui.CountDownView;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070069import com.android.camera.ui.TouchCoordinate;
Angus Kongdcccc512013-08-08 17:06:03 -070070import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070071import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070072import com.android.camera.util.GcamHelper;
Sol Boucher44ce4b22014-08-04 23:41:38 -070073import com.android.camera.util.GservicesHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070074import com.android.camera.util.SessionStatsCollector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070075import com.android.camera.util.UsageStatistics;
Doris Liudb8f9752014-05-12 15:25:13 -070076import com.android.camera.widget.AspectRatioSelector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070077import com.android.camera2.R;
Spike Sprague3c3b31d2014-09-08 10:43:04 -070078import com.android.ex.camera2.portability.CameraAgent;
Sol Boucher5a344962014-06-17 14:05:08 -070079import com.android.ex.camera2.portability.CameraAgent.CameraAFCallback;
80import com.android.ex.camera2.portability.CameraAgent.CameraAFMoveCallback;
81import com.android.ex.camera2.portability.CameraAgent.CameraPictureCallback;
82import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
83import com.android.ex.camera2.portability.CameraAgent.CameraShutterCallback;
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -070084import com.android.ex.camera2.portability.CameraCapabilities;
Sol Boucher43e18132014-06-19 15:03:18 -070085import com.android.ex.camera2.portability.CameraDeviceInfo.Characteristics;
Sol Boucher5a344962014-06-17 14:05:08 -070086import com.android.ex.camera2.portability.CameraSettings;
87import com.android.ex.camera2.portability.Size;
Seth Raphael5e09d012013-12-18 13:45:03 -080088import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080089
Angus Kong454d63f2014-05-06 14:45:59 -070090import java.io.ByteArrayOutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070091import java.io.File;
92import java.io.FileNotFoundException;
93import java.io.FileOutputStream;
94import java.io.IOException;
95import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010096import java.lang.ref.WeakReference;
Angus Kong831347d2014-06-16 16:07:28 -070097import java.util.ArrayList;
Angus Kongdcccc512013-08-08 17:06:03 -070098import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070099import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -0700100
Michael Kolb8872c232013-01-29 10:33:22 -0800101public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800102 extends CameraModule
103 implements PhotoController,
104 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800105 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800106 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -0800107 SensorEventListener,
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800108 SettingsManager.OnSettingChangedListener,
Doris Liu6c751642014-05-05 18:43:26 -0700109 RemoteCameraModule,
110 CountDownView.OnCountDownStatusListener {
Michael Kolb8872c232013-01-29 10:33:22 -0800111
Alan Newberger00a390e2014-08-18 18:40:47 -0700112 public static final String PHOTO_MODULE_STRING_ID = "PhotoModule";
Erin Dahlgren6190c362014-06-13 14:12:08 -0700113
114 private static final Log.Tag TAG = new Log.Tag(PHOTO_MODULE_STRING_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800115
116 // We number the request code from 1000 to avoid collision with Gallery.
117 private static final int REQUEST_CROP = 1000;
118
Angus Kong13e87c42013-11-25 10:02:47 -0800119 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800120 private static final int MSG_FIRST_TIME_INIT = 1;
121 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800122
123 // The subset of parameters we need to update in setCameraParameters().
124 private static final int UPDATE_PARAM_INITIALIZE = 1;
125 private static final int UPDATE_PARAM_ZOOM = 2;
126 private static final int UPDATE_PARAM_PREFERENCE = 4;
127 private static final int UPDATE_PARAM_ALL = -1;
128
Ruben Brunkd7488272013-10-10 18:45:53 -0700129 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
130
Michael Kolb8872c232013-01-29 10:33:22 -0800131 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800132 private CameraProxy mCameraDevice;
133 private int mCameraId;
Angus Kong88289042014-04-22 16:39:42 -0700134 private CameraCapabilities mCameraCapabilities;
Angus Kong6607dae2014-06-10 16:07:45 -0700135 private CameraSettings mCameraSettings;
Michael Kolb8872c232013-01-29 10:33:22 -0800136 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800137
138 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800139
Michael Kolb8872c232013-01-29 10:33:22 -0800140 // The activity is going to switch to the specified camera id. This is
141 // needed because texture copy is done in GL thread. -1 means camera is not
142 // switching.
143 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800144
145 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
146 // needed to be updated in mUpdateSet.
147 private int mUpdateSet;
148
Sol Boucher2192fba2014-08-19 17:24:07 -0700149 private float mZoomValue; // The current zoom ratio.
Andy Huibers547d7c82014-05-20 23:03:18 -0700150 private int mTimerDuration;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700151 /** Set when a volume button is clicked to take photo */
152 private boolean mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800153
Michael Kolb8872c232013-01-29 10:33:22 -0800154 private boolean mFocusAreaSupported;
155 private boolean mMeteringAreaSupported;
156 private boolean mAeLockSupported;
157 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700158 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800159
160 // The degrees of the device rotated clockwise from its natural orientation.
161 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800162
163 private static final String sTempCropFilename = "crop-temp";
164
Michael Kolb8872c232013-01-29 10:33:22 -0800165 private boolean mFaceDetectionStarted = false;
166
Michael Kolb8872c232013-01-29 10:33:22 -0800167 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
168 private String mCropValue;
169 private Uri mSaveUri;
170
Ruben Brunkd217ed02013-10-08 23:31:13 -0700171 private Uri mDebugUri;
172
Angus Kongce5480e2013-01-29 17:43:48 -0800173 // We use a queue to generated names of the images to be used later
174 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800175 private NamedImages mNamedImages;
176
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800177 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800178 @Override
179 public void run() {
180 onShutterButtonClick();
181 }
182 };
183
Michael Kolb8872c232013-01-29 10:33:22 -0800184 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800185 * An unpublished intent flag requesting to return as soon as capturing is
186 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800187 */
188 private static final String EXTRA_QUICK_CAPTURE =
189 "android.intent.extra.quickCapture";
190
191 // The display rotation in degrees. This is only valid when mCameraState is
192 // not PREVIEW_STOPPED.
193 private int mDisplayRotation;
194 // The value for android.hardware.Camera.setDisplayOrientation.
195 private int mCameraDisplayOrientation;
196 // The value for UI components like indicators.
197 private int mDisplayOrientation;
Angus Kong831347d2014-06-16 16:07:28 -0700198 // The value for cameradevice.CameraSettings.setPhotoRotationDegrees.
Michael Kolb8872c232013-01-29 10:33:22 -0800199 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700200 // Indicates whether we are using front camera
201 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800202 private boolean mFirstTimeInitialized;
203 private boolean mIsImageCaptureIntent;
204
Michael Kolb8872c232013-01-29 10:33:22 -0800205 private int mCameraState = PREVIEW_STOPPED;
206 private boolean mSnapshotOnIdle = false;
207
208 private ContentResolver mContentResolver;
209
Doris Liu2b906b82013-12-10 16:34:08 -0800210 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800211
Michael Kolb8872c232013-01-29 10:33:22 -0800212 private final PostViewPictureCallback mPostViewPictureCallback =
213 new PostViewPictureCallback();
214 private final RawPictureCallback mRawPictureCallback =
215 new RawPictureCallback();
216 private final AutoFocusCallback mAutoFocusCallback =
217 new AutoFocusCallback();
218 private final Object mAutoFocusMoveCallback =
219 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700220 ? new AutoFocusMoveCallback()
221 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800222
Michael Kolb8872c232013-01-29 10:33:22 -0800223 private long mFocusStartTime;
224 private long mShutterCallbackTime;
225 private long mPostViewPictureCallbackTime;
226 private long mRawPictureCallbackTime;
227 private long mJpegPictureCallbackTime;
228 private long mOnResumeTime;
229 private byte[] mJpegImageData;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700230 /** Touch coordinate for shutter button press. */
231 private TouchCoordinate mShutterTouchCoordinate;
232
Michael Kolb8872c232013-01-29 10:33:22 -0800233
234 // These latency time are for the CameraLatency test.
235 public long mAutoFocusTime;
236 public long mShutterLag;
237 public long mShutterToPictureDisplayedTime;
238 public long mPictureDisplayedToJpegCallbackTime;
239 public long mJpegCallbackFinishTime;
240 public long mCaptureStartTime;
241
242 // This handles everything about focus.
243 private FocusOverlayManager mFocusManager;
244
Doris Liubd1b8f92014-01-03 17:59:51 -0800245 private final int mGcamModeIndex;
Andy Huibersa31162c2014-09-02 11:27:11 -0700246 private SoundPlayer mCountdownSoundPlayer;
Doris Liubd1b8f92014-01-03 17:59:51 -0800247
Angus Kong6607dae2014-06-10 16:07:45 -0700248 private CameraCapabilities.SceneMode mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800249
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100250 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800251
Michael Kolb8872c232013-01-29 10:33:22 -0800252 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700253 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800254 private final float[] mGData = new float[3];
255 private final float[] mMData = new float[3];
256 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700257 private int mHeading = -1;
258
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800259 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700260 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700261
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800262 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800263 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800264 @Override
265 public void onMediaSaved(Uri uri) {
266 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700267 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800268 }
269 }
270 };
Angus Kong454d63f2014-05-06 14:45:59 -0700271 private boolean mShouldResizeTo16x9 = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800272
Doris Liudb8f9752014-05-12 15:25:13 -0700273 /**
Angus Kong3699c412014-05-23 15:31:09 -0700274 * We keep the flash setting before entering scene modes (HDR)
275 * and restore it after HDR is off.
276 */
277 private String mFlashModeBeforeSceneMode;
278
279 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700280 * This callback gets called when user select whether or not to
281 * turn on geo-tagging.
282 */
283 public interface LocationDialogCallback {
284 /**
285 * Gets called after user selected/unselected geo-tagging feature.
286 *
287 * @param selected whether or not geo-tagging feature is selected
288 */
289 public void onLocationTaggingSelected(boolean selected);
290 }
291
292 /**
293 * This callback defines the text that is shown in the aspect ratio selection
294 * dialog, provides the current aspect ratio, and gets notified when user changes
295 * aspect ratio selection in the dialog.
296 */
297 public interface AspectRatioDialogCallback {
298 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700299 * Returns current aspect ratio that is being used to set as default.
300 */
301 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
302
303 /**
304 * Gets notified when user has made the aspect ratio selection.
305 *
306 * @param newAspectRatio aspect ratio that user has selected
Doris Liu08b0cdd2014-05-13 19:19:55 -0700307 * @param dialogHandlingFinishedRunnable runnable to run when the operations
308 * needed to handle changes from dialog
309 * are finished.
Doris Liudb8f9752014-05-12 15:25:13 -0700310 */
Doris Liu08b0cdd2014-05-13 19:19:55 -0700311 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
312 Runnable dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700313 }
314
Angus Kongdcccc512013-08-08 17:06:03 -0700315 private void checkDisplayRotation() {
Senpo Hu9746ebe2014-10-14 11:51:56 -0700316 // Need to just be a no-op for the quick resume-pause scenario.
317 if (mPaused) {
318 return;
319 }
Angus Kongdcccc512013-08-08 17:06:03 -0700320 // Set the display orientation if display rotation has changed.
321 // Sometimes this happens when the device is held upside
322 // down and camera app is opened. Rotation animation will
323 // take some time and the rotation value we have got may be
324 // wrong. Framework does not have a callback for this now.
325 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
326 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800327 }
Angus Kongdcccc512013-08-08 17:06:03 -0700328 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
329 mHandler.postDelayed(new Runnable() {
330 @Override
331 public void run() {
332 checkDisplayRotation();
333 }
334 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800335 }
336 }
337
338 /**
339 * This Handler is used to post message back onto the main thread of the
340 * application
341 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100342 private static class MainHandler extends Handler {
343 private final WeakReference<PhotoModule> mModule;
344
345 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800346 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100347 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800348 }
349
Michael Kolb8872c232013-01-29 10:33:22 -0800350 @Override
351 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100352 PhotoModule module = mModule.get();
353 if (module == null) {
354 return;
355 }
Michael Kolb8872c232013-01-29 10:33:22 -0800356 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800357 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100358 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800359 break;
360 }
361
Angus Kong13e87c42013-11-25 10:02:47 -0800362 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100363 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800364 break;
365 }
Michael Kolb8872c232013-01-29 10:33:22 -0800366 }
367 }
368 }
369
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700370 private void switchToGcamCapture() {
371 if (mActivity != null && mGcamModeIndex != 0) {
372 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700373 settingsManager.set(SettingsManager.SCOPE_GLOBAL,
374 Keys.KEY_CAMERA_HDR_PLUS, true);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700375
376 // Disable the HDR+ button to prevent callbacks from being
377 // queued before the correct callback is attached to the button
378 // in the new module. The new module will set the enabled/disabled
379 // of this button when the module's preferred camera becomes available.
380 ButtonManager buttonManager = mActivity.getButtonManager();
Spike Sprague66d3d0d2014-08-18 14:11:33 -0700381
382 buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700383
Seth Raphael274f6e92014-05-21 17:11:53 -0700384 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
385
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700386 // Do not post this to avoid this module switch getting interleaved with
387 // other button callbacks.
388 mActivity.onModeSelected(mGcamModeIndex);
Spike Sprague66d3d0d2014-08-18 14:11:33 -0700389
390 buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700391 }
392 }
393
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800394 /**
395 * Constructs a new photo module.
396 */
Angus Kongc4e66562013-11-22 23:03:21 -0800397 public PhotoModule(AppController app) {
398 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800399 mGcamModeIndex = app.getAndroidContext().getResources()
400 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800401 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700402
Michael Kolb8872c232013-01-29 10:33:22 -0800403 @Override
Spike Sprague159e6e92014-05-27 18:26:30 -0700404 public String getPeekAccessibilityString() {
405 return mAppController.getAndroidContext()
406 .getResources().getString(R.string.photo_accessibility_peek);
407 }
408
409 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -0700410 public String getModuleStringIdentifier() {
411 return PHOTO_MODULE_STRING_ID;
412 }
413
414 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100415 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
416 mActivity = activity;
417 // TODO: Need to look at the controller interface to see if we can get
418 // rid of passing in the activity directly.
419 mAppController = mActivity;
420
421 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
422 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800423
424 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700425 mCameraId = settingsManager.getInteger(mAppController.getModuleScope(),
426 Keys.KEY_CAMERA_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800427
Doris Liudb8f9752014-05-12 15:25:13 -0700428 // TODO: Move this to SettingsManager as a part of upgrade procedure.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700429 if (!settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
430 Keys.KEY_USER_SELECTED_ASPECT_RATIO)) {
Doris Liudb8f9752014-05-12 15:25:13 -0700431 // Switch to back camera to set aspect ratio.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700432 mCameraId = settingsManager.getIntegerDefault(Keys.KEY_CAMERA_ID);
Doris Liudb8f9752014-05-12 15:25:13 -0700433 }
434
Michael Kolb8872c232013-01-29 10:33:22 -0800435 mContentResolver = mActivity.getContentResolver();
436
Michael Kolb8872c232013-01-29 10:33:22 -0800437 // Surface texture is from camera screen nail and startPreview needs it.
438 // This must be done before startPreview.
439 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800440
Michael Kolb8872c232013-01-29 10:33:22 -0800441 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800442 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Doris Liu6c751642014-05-05 18:43:26 -0700443 mUI.setCountdownFinishedListener(this);
Andy Huibersa31162c2014-09-02 11:27:11 -0700444 mCountdownSoundPlayer = new SoundPlayer(mAppController.getAndroidContext());
Doris Liu6c751642014-05-05 18:43:26 -0700445
446 // TODO: Make this a part of app controller API.
447 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
448 cancelButton.setOnClickListener(new View.OnClickListener() {
449 @Override
450 public void onClick(View view) {
451 cancelCountDown();
452 }
453 });
454 }
455
456 private void cancelCountDown() {
457 if (mUI.isCountingDown()) {
458 // Cancel on-going countdown.
459 mUI.cancelCountDown();
460 }
461 mAppController.getCameraAppUI().transitionToCapture();
462 mAppController.getCameraAppUI().showModeOptions();
Michael Kolbd6954f32013-03-08 20:43:01 -0800463 }
464
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800465 @Override
466 public boolean isUsingBottomBar() {
467 return true;
468 }
469
Michael Kolbd6954f32013-03-08 20:43:01 -0800470 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800471 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800472 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800473 setupCaptureParams();
474 }
475 }
476
477 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800478 mAppController.onPreviewStarted();
Alan Newbergerffc395d2014-09-10 13:07:18 -0700479 mAppController.setShutterEnabled(true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800480 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800481 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700482 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800483 }
484
Doris Liudb8f9752014-05-12 15:25:13 -0700485 /**
486 * Prompt the user to pick to record location and choose aspect ratio for the
487 * very first run of camera only.
488 */
489 private void settingsFirstRun() {
490 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100491
Doris Liudb8f9752014-05-12 15:25:13 -0700492 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800493 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800494 }
Doris Liudb8f9752014-05-12 15:25:13 -0700495
Erin Dahlgren6190c362014-06-13 14:12:08 -0700496 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SCOPE_GLOBAL,
497 Keys.KEY_RECORD_LOCATION);
Doris Liudb8f9752014-05-12 15:25:13 -0700498 boolean aspectRatioPrompt = !settingsManager.getBoolean(
Erin Dahlgren6190c362014-06-13 14:12:08 -0700499 SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO);
Doris Liudb8f9752014-05-12 15:25:13 -0700500 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800501 return;
502 }
Doris Liudb8f9752014-05-12 15:25:13 -0700503
Michael Kolb8872c232013-01-29 10:33:22 -0800504 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800505 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800506 if (backCameraId == -1) {
507 // If there is no back camera, do not show the prompt.
508 return;
509 }
Doris Liudb8f9752014-05-12 15:25:13 -0700510
511 if (locationPrompt) {
512 // Show both location and aspect ratio selection dialog.
513 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700514 @Override
Doris Liudb8f9752014-05-12 15:25:13 -0700515 public void onLocationTaggingSelected(boolean selected) {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700516 Keys.setLocation(mActivity.getSettingsManager(), selected,
517 mActivity.getLocationManager());
Doris Liudb8f9752014-05-12 15:25:13 -0700518 }
519 }, createAspectRatioDialogCallback());
520 } else {
521 // App upgrade. Only show aspect ratio selection.
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700522 boolean wasShown = mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
523 if (!wasShown) {
524 // If the dialog was not shown, set this flag to true so that we
525 // never have to check for it again. It means that we don't need
526 // to show the dialog on this device.
527 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
528 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
529 }
Doris Liudb8f9752014-05-12 15:25:13 -0700530 }
531 }
532
533 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -0700534 Size currentSize = mCameraSettings.getCurrentPhotoSize();
Doris Liudb8f9752014-05-12 15:25:13 -0700535 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
536 if (aspectRatio < 1f) {
537 aspectRatio = 1 / aspectRatio;
538 }
539 final AspectRatioSelector.AspectRatio currentAspectRatio;
540 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
541 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
542 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
543 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
544 } else {
545 // TODO: Log error and not show dialog.
546 return null;
547 }
548
Angus Kong6607dae2014-06-10 16:07:45 -0700549 List<Size> sizes = mCameraCapabilities.getSupportedPhotoSizes();
Doris Liudb8f9752014-05-12 15:25:13 -0700550 List<Size> pictureSizes = ResolutionUtil
551 .getDisplayableSizesFromSupported(sizes, true);
552
553 // This logic below finds the largest resolution for each aspect ratio.
554 // TODO: Move this somewhere that can be shared with SettingsActivity
555 int aspectRatio4x3Resolution = 0;
556 int aspectRatio16x9Resolution = 0;
557 Size largestSize4x3 = new Size(0, 0);
558 Size largestSize16x9 = new Size(0, 0);
559 for (Size size : pictureSizes) {
560 float pictureAspectRatio = (float) size.width() / (float) size.height();
561 pictureAspectRatio = pictureAspectRatio < 1 ?
562 1f / pictureAspectRatio : pictureAspectRatio;
563 int resolution = size.width() * size.height();
564 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
565 if (resolution > aspectRatio4x3Resolution) {
566 aspectRatio4x3Resolution = resolution;
567 largestSize4x3 = size;
568 }
569 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
570 if (resolution > aspectRatio16x9Resolution) {
571 aspectRatio16x9Resolution = resolution;
572 largestSize16x9 = size;
573 }
574 }
575 }
Doris Liudb8f9752014-05-12 15:25:13 -0700576
Doris Liu08b0cdd2014-05-13 19:19:55 -0700577 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
578 final Size size4x3ToSelect = largestSize4x3;
579 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700580
Doris Liudb8f9752014-05-12 15:25:13 -0700581 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
Doris Liudb8f9752014-05-12 15:25:13 -0700582
583 @Override
584 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
585 return currentAspectRatio;
586 }
587
588 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700589 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
590 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700591 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700592 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700593 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
594 Keys.KEY_PICTURE_SIZE_BACK,
595 largestSize4x3Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700596 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700597 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700598 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
599 Keys.KEY_PICTURE_SIZE_BACK,
600 largestSize16x9Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700601 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700602 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
603 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
604 String aspectRatio = mActivity.getSettingsManager().getString(
605 SettingsManager.SCOPE_GLOBAL,
606 Keys.KEY_USER_SELECTED_ASPECT_RATIO);
607 Log.e(TAG, "aspect ratio after setting it to true=" + aspectRatio);
Doris Liudb8f9752014-05-12 15:25:13 -0700608 if (newAspectRatio != currentAspectRatio) {
Alan Newberger19597372014-10-03 18:27:50 -0700609 Log.i(TAG, "changing aspect ratio from dialog");
Doris Liudb8f9752014-05-12 15:25:13 -0700610 stopPreview();
611 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700612 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
613 } else {
614 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700615 }
616 }
617 };
618 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700619 }
Michael Kolb8872c232013-01-29 10:33:22 -0800620
ztenghui7b265a62013-09-09 14:58:44 -0700621 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700622 public void onPreviewUIReady() {
Alan Newberger19597372014-10-03 18:27:50 -0700623 Log.i(TAG, "onPreviewUIReady");
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800624 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700625 }
626
627 @Override
628 public void onPreviewUIDestroyed() {
629 if (mCameraDevice == null) {
630 return;
631 }
632 mCameraDevice.setPreviewTexture(null);
633 stopPreview();
634 }
635
Doris Liu1dfe7822013-12-12 00:02:08 -0800636 @Override
637 public void startPreCaptureAnimation() {
638 mAppController.startPreCaptureAnimation();
639 }
640
Michael Kolbd6954f32013-03-08 20:43:01 -0800641 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800642 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800643 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800644 }
645
Michael Kolbd6954f32013-03-08 20:43:01 -0800646 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800647 if (mPaused) {
648 return;
649 }
Doris Liu6c751642014-05-05 18:43:26 -0700650 cancelCountDown();
Doris Liu6809eb82014-05-21 13:16:59 -0700651
652 mAppController.freezeScreenUntilPreviewReady();
Erin Dahlgren357b7672013-11-20 17:38:14 -0800653 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800654
Alan Newbergerd41766f2014-04-09 18:25:34 -0700655 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800656 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800657 mCameraId = mPendingSwitchCameraId;
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700658
Erin Dahlgren6190c362014-06-13 14:12:08 -0700659 settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId);
Sol Boucher44ce4b22014-08-04 23:41:38 -0700660 requestCameraOpen();
Michael Kolbd6954f32013-03-08 20:43:01 -0800661 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800662 if (mFocusManager != null) {
663 mFocusManager.removeMessages();
664 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800665
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700666 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700667 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700668 // Start switch camera animation. Post a message because
669 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800670 }
671
Sol Boucher44ce4b22014-08-04 23:41:38 -0700672 /**
673 * Uses the {@link CameraProvider} to open the currently-selected camera
674 * device, using {@link GservicesHelper} to choose between API-1 and API-2.
675 */
676 private void requestCameraOpen() {
Alan Newberger29a009c2014-09-05 12:47:42 -0700677 Log.v(TAG, "requestCameraOpen");
Sol Boucher44ce4b22014-08-04 23:41:38 -0700678 mActivity.getCameraProvider().requestCamera(mCameraId,
679 GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity));
680 }
681
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800682 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100683 new ButtonManager.ButtonCallback() {
684 @Override
685 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800686 // At the time this callback is fired, the camera id
687 // has be set to the desired camera.
688
Angus Kong97e282a2014-03-04 18:44:49 -0800689 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100690 return;
691 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800692 // If switching to back camera, and HDR+ is still on,
693 // switch back to gcam, otherwise handle callback normally.
694 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700695 if (Keys.isCameraBackFacing(settingsManager,
696 mAppController.getModuleScope())) {
697 if (Keys.requestsReturnToHdrPlus(settingsManager,
698 mAppController.getModuleScope())) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700699 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800700 return;
701 }
702 }
703
Sascha Haeberlingde303232014-02-07 02:30:53 +0100704 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800705
Alan Newbergerd41766f2014-04-09 18:25:34 -0700706 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100707 // We need to keep a preview frame for the animation before
708 // releasing the camera. This will trigger
709 // onPreviewTextureCopied.
710 // TODO: Need to animate the camera switch
711 switchCamera();
712 }
713 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800714
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800715 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100716 new ButtonManager.ButtonCallback() {
717 @Override
718 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700719 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700720 if (GcamHelper.hasGcamAsSeparateModule()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100721 // Set the camera setting to default backfacing.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700722 settingsManager.setToDefault(mAppController.getModuleScope(),
723 Keys.KEY_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700724 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100725 } else {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700726 if (Keys.isHdrOn(settingsManager)) {
Angus Kong831347d2014-06-16 16:07:28 -0700727 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
728 mCameraCapabilities.getStringifier().stringify(
729 CameraCapabilities.SceneMode.HDR));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700730 } else {
Angus Kong831347d2014-06-16 16:07:28 -0700731 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
732 mCameraCapabilities.getStringifier().stringify(
733 CameraCapabilities.SceneMode.AUTO));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700734 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100735 updateParametersSceneMode();
Senpo Huee3123b2014-09-25 10:53:12 -0700736 if (mCameraDevice != null) {
737 mCameraDevice.applySettings(mCameraSettings);
738 }
Doris Liu8ad8ad42014-03-27 14:43:31 -0700739 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100740 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800741 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100742 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800743
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800744 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
745 @Override
746 public void onClick(View v) {
747 onCaptureCancelled();
748 }
749 };
750
751 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
752 @Override
753 public void onClick(View v) {
754 onCaptureDone();
755 }
756 };
757
758 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
759 @Override
760 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800761 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800762 onCaptureRetake();
763 }
764 };
765
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800766 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700767 public void hardResetSettings(SettingsManager settingsManager) {
Erin Dahlgrene1547932014-06-23 15:17:07 -0700768 // PhotoModule should hard reset HDR+ to off,
769 // and HDR to off if HDR+ is supported.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700770 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700771 if (GcamHelper.hasGcamAsSeparateModule()) {
Erin Dahlgren21a62362014-06-24 10:13:01 -0700772 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false);
Erin Dahlgrene1547932014-06-23 15:17:07 -0700773 }
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700774 }
775
776 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800777 public HardwareSpec getHardwareSpec() {
Angus Kong831347d2014-06-16 16:07:28 -0700778 return (mCameraSettings != null ?
779 new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800780 }
781
782 @Override
783 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
784 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
785
786 bottomBarSpec.enableCamera = true;
787 bottomBarSpec.cameraCallback = mCameraCallback;
Erin Dahlgren6190c362014-06-13 14:12:08 -0700788 bottomBarSpec.enableFlash = !mAppController.getSettingsManager()
789 .getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR);
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800790 bottomBarSpec.enableHdr = true;
791 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800792 bottomBarSpec.enableGridLines = true;
Spike Sprague8f7425c2014-05-12 11:08:34 -0700793 if (mCameraCapabilities != null) {
794 bottomBarSpec.enableExposureCompensation = true;
795 bottomBarSpec.exposureCompensationSetCallback =
796 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
797 @Override
798 public void setExposure(int value) {
799 setExposureCompensation(value);
800 }
801 };
802 bottomBarSpec.minExposureCompensation =
803 mCameraCapabilities.getMinExposureCompensation();
804 bottomBarSpec.maxExposureCompensation =
805 mCameraCapabilities.getMaxExposureCompensation();
806 bottomBarSpec.exposureCompensationStep =
807 mCameraCapabilities.getExposureCompensationStep();
808 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800809
Doris Liu6c751642014-05-05 18:43:26 -0700810 bottomBarSpec.enableSelfTimer = true;
Sascha Haeberling4333fac2014-05-20 16:28:11 -0700811 bottomBarSpec.showSelfTimer = true;
Doris Liu6c751642014-05-05 18:43:26 -0700812
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800813 if (isImageCaptureIntent()) {
814 bottomBarSpec.showCancel = true;
815 bottomBarSpec.cancelCallback = mCancelCallback;
816 bottomBarSpec.showDone = true;
817 bottomBarSpec.doneCallback = mDoneCallback;
818 bottomBarSpec.showRetake = true;
819 bottomBarSpec.retakeCallback = mRetakeCallback;
820 }
821
822 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800823 }
824
Michael Kolbd6954f32013-03-08 20:43:01 -0800825 // either open a new camera or switch cameras
826 private void openCameraCommon() {
Angus Kong6607dae2014-06-10 16:07:45 -0700827 mUI.onCameraOpened(mCameraCapabilities, mCameraSettings);
Angus Kong0fb819b2013-10-08 13:44:19 -0700828 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800829 // Set hdr plus to default: off.
830 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700831 settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL,
832 Keys.KEY_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700833 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800834 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800835 }
836
ztenghui7b265a62013-09-09 14:58:44 -0700837 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800838 public void updatePreviewAspectRatio(float aspectRatio) {
839 mAppController.updatePreviewAspectRatio(aspectRatio);
840 }
841
Michael Kolb8872c232013-01-29 10:33:22 -0800842 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800843 SettingsManager settingsManager = mActivity.getSettingsManager();
844 if (settingsManager == null) {
845 Log.e(TAG, "Settings manager is null!");
846 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800847 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700848 settingsManager.setToDefault(mAppController.getCameraScope(),
849 Keys.KEY_EXPOSURE);
Michael Kolb8872c232013-01-29 10:33:22 -0800850 }
851
Michael Kolb8872c232013-01-29 10:33:22 -0800852 // Snapshots can only be taken after this is called. It should be called
853 // once only. We could have done these things in onCreate() but we want to
854 // make preview screen appear as soon as possible.
855 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700856 if (mFirstTimeInitialized || mPaused) {
857 return;
858 }
Michael Kolb8872c232013-01-29 10:33:22 -0800859
Michael Kolbd6954f32013-03-08 20:43:01 -0800860 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800861
Angus Kong86d36312013-01-31 18:22:44 -0800862 // We set the listener only when both service and shutterbutton
863 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800864 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800865
Michael Kolb8872c232013-01-29 10:33:22 -0800866 mNamedImages = new NamedImages();
867
868 mFirstTimeInitialized = true;
869 addIdleHandler();
870
Spike Spraguee6374b72014-04-25 17:24:32 -0700871 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800872 }
873
Michael Kolbd6954f32013-03-08 20:43:01 -0800874 // If the activity is paused and resumed, this method will be called in
875 // onResume.
876 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800877 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800878 mNamedImages = new NamedImages();
Angus Kong6607dae2014-06-10 16:07:45 -0700879 mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings);
Michael Kolbd6954f32013-03-08 20:43:01 -0800880 }
881
Michael Kolb8872c232013-01-29 10:33:22 -0800882 private void addIdleHandler() {
883 MessageQueue queue = Looper.myQueue();
884 queue.addIdleHandler(new MessageQueue.IdleHandler() {
885 @Override
886 public boolean queueIdle() {
887 Storage.ensureOSXCompatible();
888 return false;
889 }
890 });
891 }
892
Michael Kolb8872c232013-01-29 10:33:22 -0800893 @Override
894 public void startFaceDetection() {
Senpo Huee3123b2014-09-25 10:53:12 -0700895 if (mFaceDetectionStarted || mCameraDevice == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800896 return;
897 }
Angus Kong88289042014-04-22 16:39:42 -0700898 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800899 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700900 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700901 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800902 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700903 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800904 }
905 }
906
Michael Kolb8872c232013-01-29 10:33:22 -0800907 @Override
908 public void stopFaceDetection() {
Senpo Huee3123b2014-09-25 10:53:12 -0700909 if (!mFaceDetectionStarted || mCameraDevice == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800910 return;
911 }
Angus Kong88289042014-04-22 16:39:42 -0700912 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800913 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700914 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800915 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800916 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700917 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800918 }
919 }
920
Michael Kolb8872c232013-01-29 10:33:22 -0800921 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700922 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700923
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800924 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700925
Sascha Haeberling37f36112013-08-06 14:31:52 -0700926 public ShutterCallback(boolean needsAnimation) {
927 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700928 }
929
Michael Kolb8872c232013-01-29 10:33:22 -0800930 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700931 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800932 mShutterCallbackTime = System.currentTimeMillis();
933 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
934 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700935 if (mNeedsAnimation) {
936 mActivity.runOnUiThread(new Runnable() {
937 @Override
938 public void run() {
939 animateAfterShutter();
940 }
941 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700942 }
Michael Kolb8872c232013-01-29 10:33:22 -0800943 }
944 }
945
Angus Kong9ef99252013-07-18 18:04:19 -0700946 private final class PostViewPictureCallback
947 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800948 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800949 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800950 mPostViewPictureCallbackTime = System.currentTimeMillis();
951 Log.v(TAG, "mShutterToPostViewCallbackTime = "
952 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
953 + "ms");
954 }
955 }
956
Angus Kong9ef99252013-07-18 18:04:19 -0700957 private final class RawPictureCallback
958 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800959 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800960 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800961 mRawPictureCallbackTime = System.currentTimeMillis();
962 Log.v(TAG, "mShutterToRawCallbackTime = "
963 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
964 }
965 }
966
Angus Kong454d63f2014-05-06 14:45:59 -0700967 private static class ResizeBundle {
968 byte[] jpegData;
969 float targetAspectRatio;
970 ExifInterface exif;
971 }
972
973 /**
974 * @return Cropped image if the target aspect ratio is larger than the jpeg
975 * aspect ratio on the long axis. The original jpeg otherwise.
976 */
977 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
978
979 final byte[] jpegData = dataBundle.jpegData;
980 final ExifInterface exif = dataBundle.exif;
981 float targetAspectRatio = dataBundle.targetAspectRatio;
982
983 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
984 int originalWidth = original.getWidth();
985 int originalHeight = original.getHeight();
986 int newWidth;
987 int newHeight;
988
989 if (originalWidth > originalHeight) {
990 newHeight = (int) (originalWidth / targetAspectRatio);
991 newWidth = originalWidth;
992 } else {
993 newWidth = (int) (originalHeight / targetAspectRatio);
994 newHeight = originalHeight;
995 }
996 int xOffset = (originalWidth - newWidth)/2;
997 int yOffset = (originalHeight - newHeight)/2;
998
999 if (xOffset < 0 || yOffset < 0) {
1000 return dataBundle;
1001 }
1002
1003 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
1004 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
1005 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
1006
1007 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1008
1009 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
1010 dataBundle.jpegData = stream.toByteArray();
1011 return dataBundle;
1012 }
1013
Angus Kong9ef99252013-07-18 18:04:19 -07001014 private final class JpegPictureCallback
1015 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001016 Location mLocation;
1017
1018 public JpegPictureCallback(Location loc) {
1019 mLocation = loc;
1020 }
1021
1022 @Override
Angus Kong454d63f2014-05-06 14:45:59 -07001023 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Alan Newberger19597372014-10-03 18:27:50 -07001024 Log.i(TAG, "onPictureTaken");
Erin Dahlgren667630d2014-04-01 14:03:25 -07001025 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001026 if (mPaused) {
1027 return;
1028 }
Doris Liu6432cd62013-06-13 17:20:31 -07001029 if (mIsImageCaptureIntent) {
1030 stopPreview();
1031 }
Angus Kong6607dae2014-06-10 16:07:45 -07001032 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001033 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001034 }
1035
1036 mJpegPictureCallbackTime = System.currentTimeMillis();
1037 // If postview callback has arrived, the captured image is displayed
1038 // in postview callback. If not, the captured image is displayed in
1039 // raw picture callback.
1040 if (mPostViewPictureCallbackTime != 0) {
1041 mShutterToPictureDisplayedTime =
1042 mPostViewPictureCallbackTime - mShutterCallbackTime;
1043 mPictureDisplayedToJpegCallbackTime =
1044 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
1045 } else {
1046 mShutterToPictureDisplayedTime =
1047 mRawPictureCallbackTime - mShutterCallbackTime;
1048 mPictureDisplayedToJpegCallbackTime =
1049 mJpegPictureCallbackTime - mRawPictureCallbackTime;
1050 }
1051 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
1052 + mPictureDisplayedToJpegCallbackTime + "ms");
1053
Michael Kolb8872c232013-01-29 10:33:22 -08001054 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
1055 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001056 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001057 }
1058
Angus Kong454d63f2014-05-06 14:45:59 -07001059 long now = System.currentTimeMillis();
1060 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1061 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1062 mJpegPictureCallbackTime = 0;
1063
1064 final ExifInterface exif = Exif.getExif(originalJpegData);
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001065 final NamedEntity name = mNamedImages.getNextNameEntity();
Angus Kong454d63f2014-05-06 14:45:59 -07001066 if (mShouldResizeTo16x9) {
1067 final ResizeBundle dataBundle = new ResizeBundle();
1068 dataBundle.jpegData = originalJpegData;
1069 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1070 dataBundle.exif = exif;
1071 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1072
1073 @Override
1074 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1075 return cropJpegDataToAspectRatio(resizeBundles[0]);
1076 }
1077
1078 @Override
1079 protected void onPostExecute(ResizeBundle result) {
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001080 saveFinalPhoto(result.jpegData, name, result.exif, camera);
Angus Kong454d63f2014-05-06 14:45:59 -07001081 }
1082 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1083
1084 } else {
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001085 saveFinalPhoto(originalJpegData, name, exif, camera);
Angus Kong454d63f2014-05-06 14:45:59 -07001086 }
1087 }
1088
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001089 void saveFinalPhoto(final byte[] jpegData, NamedEntity name, final ExifInterface exif,
1090 CameraProxy camera) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001091 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001092
Sol Boucher2192fba2014-08-19 17:24:07 -07001093 float zoomValue = 1.0f;
Angus Kong88289042014-04-22 16:39:42 -07001094 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07001095 zoomValue = mCameraSettings.getCurrentZoomRatio();
Andy Huiberse08bc042014-04-11 19:26:47 -07001096 }
Angus Kong6607dae2014-06-10 16:07:45 -07001097 boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
Andy Huibers203abe52014-05-19 13:59:01 -07001098 String flashSetting =
Erin Dahlgren6190c362014-06-13 14:12:08 -07001099 mActivity.getSettingsManager().getString(mAppController.getCameraScope(),
1100 Keys.KEY_FLASH_MODE);
1101 boolean gridLinesOn = Keys.areGridLinesOn(mActivity.getSettingsManager());
Andy Huibers10c58162014-03-29 14:06:54 -07001102 UsageStatistics.instance().photoCaptureDoneEvent(
1103 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001104 name.title + ".jpg", exif,
Andy Huibers547d7c82014-05-20 23:03:18 -07001105 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001106 (float) mTimerDuration, mShutterTouchCoordinate, mVolumeButtonClickedFlag);
1107 mShutterTouchCoordinate = null;
1108 mVolumeButtonClickedFlag = false;
Ruben Brunkd217ed02013-10-08 23:31:13 -07001109
Ruben Brunkd7488272013-10-10 18:45:53 -07001110 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001111 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001112 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1113 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001114 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001115 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1116 width = exifWidth;
1117 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001118 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001119 Size s;
Angus Kong6607dae2014-06-10 16:07:45 -07001120 s = mCameraSettings.getCurrentPhotoSize();
Angus Kong454d63f2014-05-06 14:45:59 -07001121 if ((mJpegRotation + orientation) % 180 == 0) {
1122 width = s.width();
1123 height = s.height();
1124 } else {
1125 width = s.height();
1126 height = s.width();
1127 }
Michael Kolb8872c232013-01-29 10:33:22 -08001128 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001129 String title = (name == null) ? null : name.title;
1130 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001131
1132 // Handle debug mode outputs
1133 if (mDebugUri != null) {
1134 // If using a debug uri, save jpeg there.
1135 saveToDebugUri(jpegData);
1136
1137 // Adjust the title of the debug image shown in mediastore.
1138 if (title != null) {
1139 title = DEBUG_IMAGE_PREFIX + title;
1140 }
1141 }
1142
Michael Kolb8872c232013-01-29 10:33:22 -08001143 if (title == null) {
1144 Log.e(TAG, "Unbalanced name/data pair");
1145 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001146 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001147 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001148 }
Angus Kong0d00a892013-03-26 11:40:40 -07001149 if (mHeading >= 0) {
1150 // heading direction has been updated by the sensor.
1151 ExifTag directionRefTag = exif.buildTag(
1152 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1153 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1154 ExifTag directionTag = exif.buildTag(
1155 ExifInterface.TAG_GPS_IMG_DIRECTION,
1156 new Rational(mHeading, 1));
1157 exif.setTag(directionRefTag);
1158 exif.setTag(directionTag);
1159 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001160 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001161 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001162 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001163 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001164 // Animate capture with real jpeg data instead of a preview
1165 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001166 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001167 } else {
1168 mJpegImageData = jpegData;
1169 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001170 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001171 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001172 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001173 }
1174 }
1175
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001176 // Send the taken photo to remote shutter listeners, if any are
1177 // registered.
Sascha Haeberling3c6de142014-07-14 12:23:36 -07001178 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001179
Michael Kolb8872c232013-01-29 10:33:22 -08001180 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001181 // latency. It's true that someone else could write to the SD card
1182 // in the mean time and fill it, but that could have happened
1183 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001184 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001185 }
1186 }
1187
Angus Kong9ef99252013-07-18 18:04:19 -07001188 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001189 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001190 public void onAutoFocus(boolean focused, CameraProxy camera) {
1191 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001192 if (mPaused) {
1193 return;
1194 }
Michael Kolb8872c232013-01-29 10:33:22 -08001195
1196 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001197 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001198 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001199 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001200 }
1201 }
1202
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001203 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001204 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001205 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001206 @Override
1207 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001208 boolean moving, CameraProxy camera) {
1209 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001210 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001211 }
1212 }
1213
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001214 /**
1215 * This class is just a thread-safe queue for name,date holder objects.
1216 */
1217 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001218 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001219
1220 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001221 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001222 }
1223
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001224 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001225 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001226 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001227 r.date = date;
1228 mQueue.add(r);
1229 }
1230
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001231 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001232 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001233 if (!mQueue.isEmpty()) {
1234 return mQueue.remove(0);
1235 }
Michael Kolb8872c232013-01-29 10:33:22 -08001236 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001237 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001238 }
1239
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001240 public static class NamedEntity {
1241 public String title;
1242 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001243 }
1244 }
1245
1246 private void setCameraState(int state) {
1247 mCameraState = state;
1248 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001249 case PREVIEW_STOPPED:
1250 case SNAPSHOT_IN_PROGRESS:
1251 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001252 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001253 break;
1254 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001255 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001256 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001257 }
1258 }
1259
Sascha Haeberling37f36112013-08-06 14:31:52 -07001260 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001261 // Only animate when in full screen capture mode
1262 // i.e. If monkey/a user swipes to the gallery during picture taking,
1263 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001264 if (!mIsImageCaptureIntent) {
1265 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001266 }
Michael Kolb8872c232013-01-29 10:33:22 -08001267 }
1268
1269 @Override
1270 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001271 // If we are already in the middle of taking a snapshot or the image
1272 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001273 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -07001274 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -08001275 return false;
1276 }
1277 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.)
1295 int orientation = mActivity.isAutoRotateScreen() ? mDisplayRotation : mOrientation;
1296 Characteristics info = mActivity.getCameraProvider().getCharacteristics(mCameraId);
1297 mJpegRotation = info.getJpegOrientation(orientation);
1298 mCameraDevice.setJpegOrientation(mJpegRotation);
1299
Sascha Haeberling88901942013-08-28 17:49:00 -07001300 // We don't want user to press the button again while taking a
1301 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -07001302 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -07001303 mCameraDevice.takePicture(mHandler,
1304 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001305 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001306 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001307
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001308 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001309
1310 mFaceDetectionStarted = false;
1311 setCameraState(SNAPSHOT_IN_PROGRESS);
1312 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
1322 // focus mode, instead, we read it from driver
Angus Kong6607dae2014-06-10 16:07:45 -07001323 if (CameraCapabilities.SceneMode.AUTO != mSceneMode) {
1324 overrideCameraSettings(mCameraSettings.getCurrentFlashMode(),
1325 mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001326 }
1327 }
1328
Angus Kong6607dae2014-06-10 16:07:45 -07001329 private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode,
1330 CameraCapabilities.FocusMode focusMode) {
1331 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrene419b192013-12-03 13:10:27 -08001332 SettingsManager settingsManager = mActivity.getSettingsManager();
Alan Newberger347dccb2014-09-24 17:19:25 -07001333 if (!CameraCapabilities.FlashMode.NO_FLASH.equals(flashMode)) {
1334 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE,
1335 stringifier.stringify(flashMode));
1336 }
Erin Dahlgren6190c362014-06-13 14:12:08 -07001337 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE,
Alan Newberger347dccb2014-09-24 17:19:25 -07001338 stringifier.stringify(focusMode));
Michael Kolb8872c232013-01-29 10:33:22 -08001339 }
1340
1341 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001342 public void onOrientationChanged(int orientation) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001343 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1344 return;
1345 }
Senpo Hubb1c72f2014-09-08 01:07:27 -07001346
1347 // TODO: Document orientation compute logic and unify them in OrientationManagerImpl.
1348 // b/17443789
1349 // Flip to counter-clockwise orientation.
1350 mOrientation = (360 - orientation) % 360;
Michael Kolb8872c232013-01-29 10:33:22 -08001351 }
1352
1353 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001354 public void onCameraAvailable(CameraProxy cameraProxy) {
Alan Newberger19597372014-10-03 18:27:50 -07001355 Log.i(TAG, "onCameraAvailable");
Angus Kong20fad242013-11-11 18:23:46 -08001356 if (mPaused) {
1357 return;
1358 }
1359 mCameraDevice = cameraProxy;
1360
1361 initializeCapabilities();
1362
1363 // Reset zoom value index.
Sol Boucher2192fba2014-08-19 17:24:07 -07001364 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001365 if (mFocusManager == null) {
1366 initializeFocusManager();
1367 }
Angus Kong831347d2014-06-16 16:07:28 -07001368 mFocusManager.updateCapabilities(mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001369
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001370 // Do camera parameter dependent initialization.
Angus Kong6607dae2014-06-10 16:07:45 -07001371 mCameraSettings = mCameraDevice.getSettings();
Alan Newberger19597372014-10-03 18:27:50 -07001372
1373 setCameraParameters(UPDATE_PARAM_ALL);
1374 // Set a listener which updates camera parameters based
1375 // on changed settings.
1376 SettingsManager settingsManager = mActivity.getSettingsManager();
1377 settingsManager.addListener(this);
1378 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001379
Angus Kong20fad242013-11-11 18:23:46 -08001380 startPreview();
1381
1382 onCameraOpened();
1383 }
1384
1385 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001386 public void onCaptureCancelled() {
1387 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1388 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001389 }
1390
Michael Kolbd6954f32013-03-08 20:43:01 -08001391 @Override
1392 public void onCaptureRetake() {
Alan Newberger19597372014-10-03 18:27:50 -07001393 Log.i(TAG, "onCaptureRetake");
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001394 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001395 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001396 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001397 mUI.hidePostCaptureAlert();
Spike Spragueb4a22222014-05-22 14:40:53 -07001398 mUI.hideIntentReviewImageView();
Michael Kolb8872c232013-01-29 10:33:22 -08001399 setupPreview();
1400 }
1401
Michael Kolbd6954f32013-03-08 20:43:01 -08001402 @Override
1403 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001404 if (mPaused) {
1405 return;
1406 }
1407
1408 byte[] data = mJpegImageData;
1409
1410 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001411 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001412 // caller specifies a "save uri" then write the data to its
1413 // stream. Otherwise, pass back a scaled down version of the bitmap
1414 // directly in the extras.
1415 if (mSaveUri != null) {
1416 OutputStream outputStream = null;
1417 try {
1418 outputStream = mContentResolver.openOutputStream(mSaveUri);
1419 outputStream.write(data);
1420 outputStream.close();
1421
Alan Newbergerf974ba72014-09-08 14:44:30 -07001422 Log.v(TAG, "saved result to URI: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001423 mActivity.setResultEx(Activity.RESULT_OK);
1424 mActivity.finish();
1425 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001426 Log.w(TAG, "exception saving result to URI: " + mSaveUri, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001427 // ignore exception
1428 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001429 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001430 }
1431 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001432 ExifInterface exif = Exif.getExif(data);
1433 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001434 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1435 bitmap = CameraUtil.rotate(bitmap, orientation);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001436 Log.v(TAG, "inlined bitmap into capture intent result");
Michael Kolb8872c232013-01-29 10:33:22 -08001437 mActivity.setResultEx(Activity.RESULT_OK,
1438 new Intent("inline-data").putExtra("data", bitmap));
1439 mActivity.finish();
1440 }
1441 } else {
1442 // Save the image to a temp file and invoke the cropper
1443 Uri tempUri = null;
1444 FileOutputStream tempStream = null;
1445 try {
1446 File path = mActivity.getFileStreamPath(sTempCropFilename);
1447 path.delete();
1448 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1449 tempStream.write(data);
1450 tempStream.close();
1451 tempUri = Uri.fromFile(path);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001452 Log.v(TAG, "wrote temp file for cropping to: " + sTempCropFilename);
Michael Kolb8872c232013-01-29 10:33:22 -08001453 } catch (FileNotFoundException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001454 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001455 mActivity.setResultEx(Activity.RESULT_CANCELED);
1456 mActivity.finish();
1457 return;
1458 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001459 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001460 mActivity.setResultEx(Activity.RESULT_CANCELED);
1461 mActivity.finish();
1462 return;
1463 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001464 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001465 }
1466
1467 Bundle newExtras = new Bundle();
1468 if (mCropValue.equals("circle")) {
1469 newExtras.putString("circleCrop", "true");
1470 }
1471 if (mSaveUri != null) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001472 Log.v(TAG, "setting output of cropped file to: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001473 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1474 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001475 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001476 }
1477 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001478 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001479 }
1480
Sascha Haeberling37f36112013-08-06 14:31:52 -07001481 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001482 final String CROP_ACTION = "com.android.camera.action.CROP";
1483 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001484
1485 cropIntent.setData(tempUri);
1486 cropIntent.putExtras(newExtras);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001487 Log.v(TAG, "starting CROP intent for capture");
Michael Kolb8872c232013-01-29 10:33:22 -08001488 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1489 }
1490 }
1491
Michael Kolb8872c232013-01-29 10:33:22 -08001492 @Override
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001493 public void onShutterCoordinate(TouchCoordinate coord) {
1494 mShutterTouchCoordinate = coord;
1495 }
1496
1497 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001498 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001499 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001500 }
1501
1502 @Override
1503 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001504 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001505 || (mCameraState == PREVIEW_STOPPED)) {
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001506 mVolumeButtonClickedFlag = false;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001507 return;
1508 }
Michael Kolb8872c232013-01-29 10:33:22 -08001509
1510 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001511 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001512 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001513 + mActivity.getStorageSpaceBytes());
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001514 mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001515 return;
1516 }
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001517 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
1518 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
Michael Kolb8872c232013-01-29 10:33:22 -08001519
Erin Dahlgren6190c362014-06-13 14:12:08 -07001520 int countDownDuration = mActivity.getSettingsManager()
1521 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
Andy Huibers547d7c82014-05-20 23:03:18 -07001522 mTimerDuration = countDownDuration;
Doris Liu6c751642014-05-05 18:43:26 -07001523 if (countDownDuration > 0) {
1524 // Start count down.
1525 mAppController.getCameraAppUI().transitionToCancel();
1526 mAppController.getCameraAppUI().hideModeOptions();
1527 mUI.startCountdown(countDownDuration);
1528 return;
1529 } else {
1530 focusAndCapture();
1531 }
1532 }
1533
1534 private void focusAndCapture() {
Angus Kong6607dae2014-06-10 16:07:45 -07001535 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001536 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001537 }
Michael Kolb8872c232013-01-29 10:33:22 -08001538 // If the user wants to do a snapshot while the previous one is still
1539 // in progress, remember the fact and do it after we finish the previous
1540 // one and re-start the preview. Snapshot in progress also includes the
1541 // state that autofocus is focusing and a picture will be taken when
1542 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001543 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1544 if (!mIsImageCaptureIntent) {
1545 mSnapshotOnIdle = true;
1546 }
Michael Kolb8872c232013-01-29 10:33:22 -08001547 return;
1548 }
1549
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001550 mSnapshotOnIdle = false;
Angus Kong831347d2014-06-16 16:07:28 -07001551 mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001552 }
1553
Doris Liu6c751642014-05-05 18:43:26 -07001554 @Override
1555 public void onRemainingSecondsChanged(int remainingSeconds) {
Andy Huibersa31162c2014-09-02 11:27:11 -07001556 if (remainingSeconds == 1) {
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001557 mCountdownSoundPlayer.play(R.raw.timer_final_second, 0.6f);
Andy Huibersa31162c2014-09-02 11:27:11 -07001558 } else if (remainingSeconds == 2 || remainingSeconds == 3) {
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001559 mCountdownSoundPlayer.play(R.raw.timer_increment, 0.6f);
Andy Huibersa31162c2014-09-02 11:27:11 -07001560 }
Doris Liu6c751642014-05-05 18:43:26 -07001561 }
1562
1563 @Override
1564 public void onCountDownFinished() {
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001565 if (mIsImageCaptureIntent) {
1566 mAppController.getCameraAppUI().transitionToIntentReviewLayout();
1567 } else {
1568 mAppController.getCameraAppUI().transitionToCapture();
1569 }
Doris Liu6c751642014-05-05 18:43:26 -07001570 mAppController.getCameraAppUI().showModeOptions();
1571 if (mPaused) {
1572 return;
1573 }
1574 focusAndCapture();
1575 }
1576
Puneet Lall4de9b722014-09-25 15:05:47 -07001577 @Override
1578 public void resume() {
1579 mPaused = false;
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001580
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001581 mCountdownSoundPlayer.loadSound(R.raw.timer_final_second);
1582 mCountdownSoundPlayer.loadSound(R.raw.timer_increment);
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001583 if (mFocusManager != null) {
1584 // If camera is not open when resume is called, focus manager will
1585 // not be initialized yet, in which case it will start listening to
1586 // preview area size change later in the initialization.
1587 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1588 }
1589 mAppController.addPreviewAreaSizeChangedListener(mUI);
1590
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001591 CameraProvider camProvider = mActivity.getCameraProvider();
1592 if (camProvider == null) {
1593 // No camera provider, the Activity is destroyed already.
1594 return;
1595 }
Sol Boucher44ce4b22014-08-04 23:41:38 -07001596 requestCameraOpen();
Angus Kong20fad242013-11-11 18:23:46 -08001597
Michael Kolb8872c232013-01-29 10:33:22 -08001598 mJpegPictureCallbackTime = 0;
Sol Boucher2192fba2014-08-19 17:24:07 -07001599 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001600
1601 mOnResumeTime = SystemClock.uptimeMillis();
1602 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001603
1604 // If first time initialization is not finished, put it in the
1605 // message queue.
1606 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001607 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001608 } else {
1609 initializeSecondTime();
1610 }
Michael Kolb8872c232013-01-29 10:33:22 -08001611
Angus Kong0d00a892013-03-26 11:40:40 -07001612 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1613 if (gsensor != null) {
1614 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1615 }
1616
1617 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1618 if (msensor != null) {
1619 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1620 }
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001621
1622 getServices().getRemoteShutterListener().onModuleReady(this);
1623 SessionStatsCollector.instance().sessionActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001624 }
1625
Angus Kongc4e66562013-11-22 23:03:21 -08001626 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001627 * @return Whether the currently active camera is front-facing.
1628 */
1629 private boolean isCameraFrontFacing() {
Sol Boucher43e18132014-06-19 15:03:18 -07001630 return mAppController.getCameraProvider().getCharacteristics(mCameraId)
1631 .isFacingFront();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001632 }
1633
1634 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001635 * The focus manager is the first UI related element to get initialized, and
1636 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001637 */
1638 private void initializeFocusManager() {
1639 // Create FocusManager object. startPreview needs it.
1640 // if mFocusManager not null, reuse it
1641 // otherwise create a new instance
1642 if (mFocusManager != null) {
1643 mFocusManager.removeMessages();
1644 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001645 mMirror = isCameraFrontFacing();
Angus Kong831347d2014-06-16 16:07:28 -07001646 String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
Angus Kongc4e66562013-11-22 23:03:21 -08001647 R.array.pref_camera_focusmode_default_array);
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -07001648 ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
1649 new ArrayList<CameraCapabilities.FocusMode>();
Angus Kong831347d2014-06-16 16:07:28 -07001650 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
1651 for (String modeString : defaultFocusModesStrings) {
1652 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
1653 if (mode != null) {
1654 defaultFocusModes.add(mode);
1655 }
1656 }
1657 mFocusManager =
1658 new FocusOverlayManager(mAppController, defaultFocusModes,
1659 mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
1660 mUI.getFocusUI());
Kevin Gabayanfb333362014-06-02 14:48:20 -07001661 MotionManager motionManager = getServices().getMotionManager();
1662 if (motionManager != null) {
1663 motionManager.addListener(mFocusManager);
1664 }
Angus Kongc4e66562013-11-22 23:03:21 -08001665 }
Doris Liu482de022013-12-18 19:18:16 -08001666 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001667 }
1668
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001669 /**
1670 * @return Whether we are resuming from within the lockscreen.
1671 */
1672 private boolean isResumeFromLockscreen() {
1673 String action = mActivity.getIntent().getAction();
1674 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1675 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
1676 }
1677
Angus Kong20fad242013-11-11 18:23:46 -08001678 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001679 public void pause() {
Michael Kolb8872c232013-01-29 10:33:22 -08001680 mPaused = true;
Sascha Haeberling1bf08892014-06-18 09:38:06 -07001681 getServices().getRemoteShutterListener().onModuleExit();
Andy Huibers10c58162014-03-29 14:06:54 -07001682 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001683
Angus Kong0d00a892013-03-26 11:40:40 -07001684 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1685 if (gsensor != null) {
1686 mSensorManager.unregisterListener(this, gsensor);
1687 }
1688
1689 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1690 if (msensor != null) {
1691 mSensorManager.unregisterListener(this, msensor);
1692 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001693
Michael Kolb8872c232013-01-29 10:33:22 -08001694 // Reset the focus first. Camera CTS does not guarantee that
1695 // cancelAutoFocus is allowed after preview stops.
1696 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1697 mCameraDevice.cancelAutoFocus();
1698 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001699
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001700 // If the camera has not been opened asynchronously yet,
1701 // and startPreview hasn't been called, then this is a no-op.
1702 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001703 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001704 cancelCountDown();
Andy Huibers877c2b62014-10-03 15:22:47 -07001705 mCountdownSoundPlayer.unloadSound(R.raw.timer_final_second);
1706 mCountdownSoundPlayer.unloadSound(R.raw.timer_increment);
Michael Kolb8872c232013-01-29 10:33:22 -08001707
Angus Kongce5480e2013-01-29 17:43:48 -08001708 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001709 // If we are in an image capture intent and has taken
1710 // a picture, we just clear it in onPause.
1711 mJpegImageData = null;
1712
Angus Kongdcccc512013-08-08 17:06:03 -07001713 // Remove the messages and runnables in the queue.
1714 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001715
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001716 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001717 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001718 mUI.onPause();
1719
Michael Kolb8872c232013-01-29 10:33:22 -08001720 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001721 if (mFocusManager != null) {
1722 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001723 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001724 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001725 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001726 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001727
1728 SettingsManager settingsManager = mActivity.getSettingsManager();
1729 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001730 }
1731
Angus Kong20fad242013-11-11 18:23:46 -08001732 @Override
1733 public void destroy() {
Andy Huibers877c2b62014-10-03 15:22:47 -07001734 mCountdownSoundPlayer.release();
Angus Kong20fad242013-11-11 18:23:46 -08001735 }
1736
1737 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001738 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001739 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001740 }
1741
1742 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001743 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001744 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001745 setDisplayOrientation();
1746 }
1747 }
1748
Michael Kolb8872c232013-01-29 10:33:22 -08001749 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001750 return isCameraIdle()
1751 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001752 }
1753
1754 @Override
1755 public void autoFocus() {
Senpo Hu4ba28292014-09-26 11:05:08 -07001756 if (mCameraDevice == null) {
1757 return;
1758 }
Andy Huibers10c58162014-03-29 14:06:54 -07001759 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001760 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001761 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001762 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001763 setCameraState(FOCUSING);
1764 }
1765
1766 @Override
1767 public void cancelAutoFocus() {
Senpo Hu4ba28292014-09-26 11:05:08 -07001768 if (mCameraDevice == null) {
1769 return;
1770 }
Michael Kolb8872c232013-01-29 10:33:22 -08001771 mCameraDevice.cancelAutoFocus();
1772 setCameraState(IDLE);
1773 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1774 }
1775
Michael Kolb8872c232013-01-29 10:33:22 -08001776 @Override
1777 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001778 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1779 || mCameraState == SNAPSHOT_IN_PROGRESS
1780 || mCameraState == SWITCHING_CAMERA
1781 || mCameraState == PREVIEW_STOPPED) {
1782 return;
1783 }
1784
1785 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001786 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1787 return;
1788 }
Doris Liu482de022013-12-18 19:18:16 -08001789 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001790 }
1791
1792 @Override
1793 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001794 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001795 }
1796
1797 @Override
1798 public boolean onKeyDown(int keyCode, KeyEvent event) {
1799 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001800 case KeyEvent.KEYCODE_VOLUME_UP:
1801 case KeyEvent.KEYCODE_VOLUME_DOWN:
1802 case KeyEvent.KEYCODE_FOCUS:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001803 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1804 !mActivity.getCameraAppUI().isInIntentReview()) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001805 if (event.getRepeatCount() == 0) {
1806 onShutterButtonFocus(true);
1807 }
1808 return true;
1809 }
1810 return false;
1811 case KeyEvent.KEYCODE_CAMERA:
1812 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1813 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001814 }
1815 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001816 case KeyEvent.KEYCODE_DPAD_CENTER:
1817 // If we get a dpad center event without any focused view, move
1818 // the focus to the shutter button and press it.
1819 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1820 // Start auto-focus immediately to reduce shutter lag. After
1821 // the shutter button gets the focus, onShutterButtonFocus()
1822 // will be called again but it is fine.
1823 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001824 }
1825 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001826 }
1827 return false;
1828 }
1829
1830 @Override
1831 public boolean onKeyUp(int keyCode, KeyEvent event) {
1832 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001833 case KeyEvent.KEYCODE_VOLUME_UP:
1834 case KeyEvent.KEYCODE_VOLUME_DOWN:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001835 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1836 !mActivity.getCameraAppUI().isInIntentReview()) {
1837 if (mUI.isCountingDown()) {
1838 cancelCountDown();
1839 } else {
1840 mVolumeButtonClickedFlag = true;
1841 onShutterButtonClick();
1842 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001843 return true;
1844 }
1845 return false;
1846 case KeyEvent.KEYCODE_FOCUS:
1847 if (mFirstTimeInitialized) {
1848 onShutterButtonFocus(false);
1849 }
Michael Kolb8872c232013-01-29 10:33:22 -08001850 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001851 }
1852 return false;
1853 }
1854
Michael Kolb8872c232013-01-29 10:33:22 -08001855 private void closeCamera() {
1856 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001857 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001858 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001859 mCameraDevice.setFaceDetectionCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001860
Michael Kolb8872c232013-01-29 10:33:22 -08001861 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001862 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001863 mCameraDevice = null;
1864 setCameraState(PREVIEW_STOPPED);
1865 mFocusManager.onCameraReleased();
1866 }
1867 }
1868
1869 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001870 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
Sol Boucher43e18132014-06-19 15:03:18 -07001871 Characteristics info =
1872 mActivity.getCameraProvider().getCharacteristics(mCameraId);
Sol Boucher73ec9852014-07-24 23:59:21 -07001873 mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001874 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001875 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001876 if (mFocusManager != null) {
1877 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1878 }
Doris Liu6432cd62013-06-13 17:20:31 -07001879 // Change the camera display orientation
1880 if (mCameraDevice != null) {
Sol Boucher73ec9852014-07-24 23:59:21 -07001881 mCameraDevice.setDisplayOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001882 }
Michael Kolb8872c232013-01-29 10:33:22 -08001883 }
1884
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001885 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001886 private void setupPreview() {
Alan Newberger19597372014-10-03 18:27:50 -07001887 Log.i(TAG, "setupPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001888 mFocusManager.resetTouchFocus();
1889 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001890 }
1891
Angus Kong20fad242013-11-11 18:23:46 -08001892 /**
1893 * Returns whether we can/should start the preview or not.
1894 */
1895 private boolean checkPreviewPreconditions() {
1896 if (mPaused) {
1897 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001898 }
Michael Kolb8872c232013-01-29 10:33:22 -08001899
Angus Kong20fad242013-11-11 18:23:46 -08001900 if (mCameraDevice == null) {
1901 Log.w(TAG, "startPreview: camera device not ready yet.");
1902 return false;
1903 }
1904
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001905 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001906 if (st == null) {
1907 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001908 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001909 }
1910
1911 if (!mCameraPreviewParamsReady) {
1912 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001913 return false;
1914 }
1915 return true;
1916 }
1917
1918 /**
1919 * The start/stop preview should only run on the UI thread.
1920 */
1921 private void startPreview() {
Alan Newberger19597372014-10-03 18:27:50 -07001922 if (mCameraDevice == null) {
1923 Log.i(TAG, "attempted to start preview before camera device");
Puneet Lall70a96522014-09-11 17:33:21 -07001924 // do nothing
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001925 return;
1926 }
Alan Newberger19597372014-10-03 18:27:50 -07001927
1928 if (!checkPreviewPreconditions()) {
1929 return;
1930 }
1931
Alan Newberger19597372014-10-03 18:27:50 -07001932 setDisplayOrientation();
1933
1934 if (!mSnapshotOnIdle) {
1935 // If the focus mode is continuous autofocus, call cancelAutoFocus
1936 // to resume it because it may have been paused by autoFocus call.
1937 if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
1938 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
1939 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001940 }
Alan Newberger19597372014-10-03 18:27:50 -07001941 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1942 }
1943 setCameraParameters(UPDATE_PARAM_ALL);
Michael Kolb8872c232013-01-29 10:33:22 -08001944
Alan Newberger19597372014-10-03 18:27:50 -07001945 updateParametersPictureSize();
Angus Kong20fad242013-11-11 18:23:46 -08001946
Alan Newberger19597372014-10-03 18:27:50 -07001947 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Puneet Lall70a96522014-09-11 17:33:21 -07001948
Alan Newberger19597372014-10-03 18:27:50 -07001949 Log.i(TAG, "startPreview");
1950 // If we're using API2 in portability layers, don't use startPreviewWithCallback()
1951 // b/17576554
1952 CameraAgent.CameraStartPreviewCallback startPreviewCallback =
1953 new CameraAgent.CameraStartPreviewCallback() {
1954 @Override
1955 public void onPreviewStarted() {
1956 mFocusManager.onPreviewStarted();
1957 PhotoModule.this.onPreviewStarted();
1958 SessionStatsCollector.instance().previewActive(true);
1959 if (mSnapshotOnIdle) {
1960 mHandler.post(mDoSnapRunnable);
Spike Sprague3c3b31d2014-09-08 10:43:04 -07001961 }
Alan Newberger19597372014-10-03 18:27:50 -07001962 }
1963 };
1964 if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity)) {
1965 mCameraDevice.startPreview();
1966 startPreviewCallback.onPreviewStarted();
1967 } else {
1968 mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
1969 startPreviewCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001970 }
1971 }
1972
Michael Kolbd6954f32013-03-08 20:43:01 -08001973 @Override
1974 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001975 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001976 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001977 mCameraDevice.stopPreview();
1978 mFaceDetectionStarted = false;
1979 }
1980 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001981 if (mFocusManager != null) {
1982 mFocusManager.onPreviewStopped();
1983 }
Andy Huibers10c58162014-03-29 14:06:54 -07001984 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001985 }
1986
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001987 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -07001988 public void onSettingChanged(SettingsManager settingsManager, String key) {
1989 if (key.equals(Keys.KEY_FLASH_MODE)) {
1990 updateParametersFlashMode();
1991 }
1992 if (key.equals(Keys.KEY_CAMERA_HDR)) {
1993 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
1994 Keys.KEY_CAMERA_HDR)) {
1995 // HDR is on.
1996 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
1997 mFlashModeBeforeSceneMode = settingsManager.getString(
1998 mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
1999 } else {
2000 if (mFlashModeBeforeSceneMode != null) {
2001 settingsManager.set(mAppController.getCameraScope(),
2002 Keys.KEY_FLASH_MODE,
2003 mFlashModeBeforeSceneMode);
2004 updateParametersFlashMode();
2005 mFlashModeBeforeSceneMode = null;
Angus Kong8c07fe92014-05-22 14:42:45 -07002006 }
Erin Dahlgren6190c362014-06-13 14:12:08 -07002007 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002008 }
2009 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08002010
2011 if (mCameraDevice != null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002012 mCameraDevice.applySettings(mCameraSettings);
Erin Dahlgren1648c362014-01-06 15:06:04 -08002013 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002014 }
2015
Michael Kolb8872c232013-01-29 10:33:22 -08002016 private void updateCameraParametersInitialize() {
2017 // Reset preview frame rate to the maximum because it may be lowered by
2018 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07002019 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07002020 if (fpsRange != null && fpsRange.length > 0) {
Angus Kong831347d2014-06-16 16:07:28 -07002021 mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
Michael Kolb8872c232013-01-29 10:33:22 -08002022 }
2023
Angus Kong831347d2014-06-16 16:07:28 -07002024 mCameraSettings.setRecordingHintEnabled(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002025
Angus Kong6607dae2014-06-10 16:07:45 -07002026 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
2027 mCameraSettings.setVideoStabilization(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002028 }
2029 }
2030
2031 private void updateCameraParametersZoom() {
2032 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07002033 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002034 mCameraSettings.setZoomRatio(mZoomValue);
Michael Kolb8872c232013-01-29 10:33:22 -08002035 }
2036 }
2037
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002038 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002039 private void setAutoExposureLockIfSupported() {
2040 if (mAeLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002041 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002042 }
2043 }
2044
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002045 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002046 private void setAutoWhiteBalanceLockIfSupported() {
2047 if (mAwbLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002048 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002049 }
2050 }
2051
Michael Kolb8872c232013-01-29 10:33:22 -08002052 private void setFocusAreasIfSupported() {
2053 if (mFocusAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002054 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002055 }
2056 }
2057
Michael Kolb8872c232013-01-29 10:33:22 -08002058 private void setMeteringAreasIfSupported() {
2059 if (mMeteringAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002060 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002061 }
2062 }
2063
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002064 private void updateCameraParametersPreference() {
Spike Spragueae56c122014-09-19 11:18:46 -07002065 // some monkey tests can get here when shutting the app down
2066 // make sure mCameraDevice is still valid, b/17580046
2067 if (mCameraDevice == null) {
2068 return;
2069 }
2070
Michael Kolb8872c232013-01-29 10:33:22 -08002071 setAutoExposureLockIfSupported();
2072 setAutoWhiteBalanceLockIfSupported();
2073 setFocusAreasIfSupported();
2074 setMeteringAreasIfSupported();
2075
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002076 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07002077 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002078 mCameraSettings
2079 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Andy Huibers10c58162014-03-29 14:06:54 -07002080 SessionStatsCollector.instance().autofocusActive(
Angus Kong831347d2014-06-16 16:07:28 -07002081 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
2082 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
2083 );
Michael Kolbd3253f22013-07-12 11:36:47 -07002084
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002085 // Set JPEG quality.
2086 updateParametersPictureQuality();
2087
2088 // For the following settings, we need to check if the settings are
2089 // still supported by latest driver, if not, ignore the settings.
2090
2091 // Set exposure compensation
2092 updateParametersExposureCompensation();
2093
2094 // Set the scene mode: also sets flash and white balance.
2095 updateParametersSceneMode();
2096
2097 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2098 updateAutoFocusMoveCallback();
2099 }
2100 }
2101
Alan Newberger19597372014-10-03 18:27:50 -07002102 /**
2103 * This method sets picture size parameters. Size parameters should only be
2104 * set when the preview is stopped, and so this method is only invoked in
2105 * {@link #startPreview()} just before starting the preview.
2106 */
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002107 private void updateParametersPictureSize() {
Senpo Huee3123b2014-09-25 10:53:12 -07002108 if (mCameraDevice == null) {
Alan Newberger19597372014-10-03 18:27:50 -07002109 Log.w(TAG, "attempting to set picture size without caemra device");
Senpo Huee3123b2014-09-25 10:53:12 -07002110 return;
2111 }
2112
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002113 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002114 String pictureSizeKey = isCameraFrontFacing() ? Keys.KEY_PICTURE_SIZE_FRONT
2115 : Keys.KEY_PICTURE_SIZE_BACK;
2116 String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL,
2117 pictureSizeKey);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01002118
Angus Kong6607dae2014-06-10 16:07:45 -07002119 List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes();
Andy Huibers90c7ad52014-05-20 22:13:54 -07002120 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2121 mCameraDevice.getCameraId(), supported);
Angus Kong6607dae2014-06-10 16:07:45 -07002122 SettingsUtil.setCameraPictureSize(pictureSize, supported, mCameraSettings,
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002123 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07002124
2125 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2126 mCameraDevice.getCameraId());
2127 if (ApiHelper.IS_NEXUS_5) {
2128 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2129 mShouldResizeTo16x9 = true;
2130 } else {
2131 mShouldResizeTo16x9 = false;
2132 }
2133 }
Michael Kolb8872c232013-01-29 10:33:22 -08002134
2135 // Set a preview size that is closest to the viewfinder height and has
2136 // the right aspect ratio.
Angus Kong6607dae2014-06-10 16:07:45 -07002137 List<Size> sizes = mCameraCapabilities.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07002138 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002139 (double) size.width() / size.height());
Angus Kong6607dae2014-06-10 16:07:45 -07002140 Size original = mCameraSettings.getCurrentPreviewSize();
2141 if (!optimalSize.equals(original)) {
Alan Newberger2631a152014-09-24 14:23:30 -07002142 Log.v(TAG, "setting preview size. optimal: " + optimalSize + "original: " + original);
Angus Kong6607dae2014-06-10 16:07:45 -07002143 mCameraSettings.setPreviewSize(optimalSize);
Doris Liu6432cd62013-06-13 17:20:31 -07002144
Alan Newberger19597372014-10-03 18:27:50 -07002145 mCameraDevice.applySettings(mCameraSettings);
Angus Kong6607dae2014-06-10 16:07:45 -07002146 mCameraSettings = mCameraDevice.getSettings();
Michael Kolb8872c232013-01-29 10:33:22 -08002147 }
Doris Liu95405742013-11-05 15:25:26 -08002148
Angus Kong00b7b102014-04-24 15:46:52 -07002149 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
Alan Newberger73514152014-09-10 15:03:27 -07002150 Log.v(TAG, "updating aspect ratio");
Angus Kong00b7b102014-04-24 15:46:52 -07002151 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2152 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002153 }
Alan Newberger73514152014-09-10 15:03:27 -07002154 Log.d(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002155 }
Michael Kolb8872c232013-01-29 10:33:22 -08002156
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002157 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002158 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2159 CameraProfile.QUALITY_HIGH);
Angus Kong6607dae2014-06-10 16:07:45 -07002160 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002161 }
Michael Kolb8872c232013-01-29 10:33:22 -08002162
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002163 private void updateParametersExposureCompensation() {
2164 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002165 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2166 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2167 int value = settingsManager.getInteger(mAppController.getCameraScope(),
2168 Keys.KEY_EXPOSURE);
Angus Kong88289042014-04-22 16:39:42 -07002169 int max = mCameraCapabilities.getMaxExposureCompensation();
2170 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002171 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002172 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002173 } else {
2174 Log.w(TAG, "invalid exposure range: " + value);
2175 }
Michael Kolb8872c232013-01-29 10:33:22 -08002176 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002177 // If exposure compensation is not enabled, reset the exposure compensation value.
2178 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002179 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002180
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002181 }
2182
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002183 private void updateParametersSceneMode() {
Angus Kong6607dae2014-06-10 16:07:45 -07002184 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002185 SettingsManager settingsManager = mActivity.getSettingsManager();
2186
Erin Dahlgren6190c362014-06-13 14:12:08 -07002187 mSceneMode = stringifier.
2188 sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2189 Keys.KEY_SCENE_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002190 if (mCameraCapabilities.supports(mSceneMode)) {
2191 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
2192 mCameraSettings.setSceneMode(mSceneMode);
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002193
2194 // Setting scene mode will change the settings of flash mode,
2195 // white balance, and focus mode. Here we read back the
2196 // parameters, so we can know those settings.
Angus Kong6607dae2014-06-10 16:07:45 -07002197 mCameraDevice.applySettings(mCameraSettings);
2198 mCameraSettings = mCameraDevice.getSettings();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002199 }
2200 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002201 mSceneMode = mCameraSettings.getCurrentSceneMode();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002202 if (mSceneMode == null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002203 mSceneMode = CameraCapabilities.SceneMode.AUTO;
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002204 }
2205 }
2206
Angus Kong6607dae2014-06-10 16:07:45 -07002207 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
Michael Kolb8872c232013-01-29 10:33:22 -08002208 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002209 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002210
Michael Kolb8872c232013-01-29 10:33:22 -08002211 // Set focus mode.
2212 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002213 mCameraSettings.setFocusMode(
2214 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Michael Kolb8872c232013-01-29 10:33:22 -08002215 } else {
Angus Kong831347d2014-06-16 16:07:28 -07002216 mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08002217 }
Michael Kolb8872c232013-01-29 10:33:22 -08002218 }
2219
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002220 private void updateParametersFlashMode() {
2221 SettingsManager settingsManager = mActivity.getSettingsManager();
2222
Angus Kong6607dae2014-06-10 16:07:45 -07002223 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
Erin Dahlgren6190c362014-06-13 14:12:08 -07002224 .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2225 Keys.KEY_FLASH_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002226 if (mCameraCapabilities.supports(flashMode)) {
2227 mCameraSettings.setFlashMode(flashMode);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002228 }
2229 }
2230
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002231 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002232 private void updateAutoFocusMoveCallback() {
Senpo Huee3123b2014-09-25 10:53:12 -07002233 if (mCameraDevice == null) {
2234 return;
2235 }
Angus Kong6607dae2014-06-10 16:07:45 -07002236 if (mCameraSettings.getCurrentFocusMode() ==
2237 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
Angus Kong9ef99252013-07-18 18:04:19 -07002238 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002239 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002240 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002241 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002242 }
2243 }
2244
Spike Spragueabf54e22014-03-27 15:41:28 -07002245 /**
2246 * Sets the exposure compensation to the given value and also updates settings.
2247 *
2248 * @param value exposure compensation value to be set
2249 */
2250 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002251 int max = mCameraCapabilities.getMaxExposureCompensation();
2252 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002253 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002254 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002255 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002256 settingsManager.set(mAppController.getCameraScope(),
2257 Keys.KEY_EXPOSURE, value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002258 } else {
2259 Log.w(TAG, "invalid exposure range: " + value);
2260 }
2261 }
2262
Michael Kolb8872c232013-01-29 10:33:22 -08002263 // We separate the parameters into several subsets, so we can update only
2264 // the subsets actually need updating. The PREFERENCE set needs extra
2265 // locking because the preference can be changed from GLThread as well.
2266 private void setCameraParameters(int updateSet) {
2267 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2268 updateCameraParametersInitialize();
2269 }
2270
2271 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2272 updateCameraParametersZoom();
2273 }
2274
2275 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002276 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002277 }
2278
Spike Sprague5cc48d62014-09-22 10:52:32 -07002279 if (mCameraDevice != null) {
2280 mCameraDevice.applySettings(mCameraSettings);
2281 }
Michael Kolb8872c232013-01-29 10:33:22 -08002282 }
2283
2284 // If the Camera is idle, update the parameters immediately, otherwise
2285 // accumulate them in mUpdateSet and update later.
2286 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2287 mUpdateSet |= additionalUpdateSet;
2288 if (mCameraDevice == null) {
2289 // We will update all the parameters when we open the device, so
2290 // we don't need to do anything now.
2291 mUpdateSet = 0;
2292 return;
2293 } else if (isCameraIdle()) {
2294 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002295 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002296 mUpdateSet = 0;
2297 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002298 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2299 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002300 }
2301 }
2302 }
2303
ztenghui7b265a62013-09-09 14:58:44 -07002304 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002305 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002306 return (mCameraState == IDLE) ||
2307 (mCameraState == PREVIEW_STOPPED) ||
2308 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002309 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002310 }
2311
ztenghui7b265a62013-09-09 14:58:44 -07002312 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002313 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002314 String action = mActivity.getIntent().getAction();
2315 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002316 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002317 }
2318
2319 private void setupCaptureParams() {
2320 Bundle myExtras = mActivity.getIntent().getExtras();
2321 if (myExtras != null) {
2322 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2323 mCropValue = myExtras.getString("crop");
2324 }
2325 }
2326
Michael Kolb8872c232013-01-29 10:33:22 -08002327 private void initializeCapabilities() {
Angus Kong88289042014-04-22 16:39:42 -07002328 mCameraCapabilities = mCameraDevice.getCapabilities();
2329 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2330 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2331 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2332 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kong6607dae2014-06-10 16:07:45 -07002333 mContinuousFocusSupported =
2334 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002335 }
2336
Michael Kolb8872c232013-01-29 10:33:22 -08002337 @Override
Sol Boucher2192fba2014-08-19 17:24:07 -07002338 public void onZoomChanged(float ratio) {
Michael Kolbd6954f32013-03-08 20:43:01 -08002339 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002340 if (mPaused) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002341 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002342 }
Sol Boucher2192fba2014-08-19 17:24:07 -07002343 mZoomValue = ratio;
Angus Kong6607dae2014-06-10 16:07:45 -07002344 if (mCameraSettings == null || mCameraDevice == null) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002345 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002346 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002347 // Set zoom parameters asynchronously
Sol Boucher2192fba2014-08-19 17:24:07 -07002348 mCameraSettings.setZoomRatio(mZoomValue);
Angus Kong6607dae2014-06-10 16:07:45 -07002349 mCameraDevice.applySettings(mCameraSettings);
Angus Kongce5480e2013-01-29 17:43:48 -08002350 }
2351
2352 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002353 public int getCameraState() {
2354 return mCameraState;
2355 }
2356
2357 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002358 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002359 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002360 }
Angus Kong86d36312013-01-31 18:22:44 -08002361
2362 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002363 public void onLowMemory() {
2364 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002365 }
Angus Kong0d00a892013-03-26 11:40:40 -07002366
2367 @Override
2368 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2369 }
2370
2371 @Override
2372 public void onSensorChanged(SensorEvent event) {
2373 int type = event.sensor.getType();
2374 float[] data;
2375 if (type == Sensor.TYPE_ACCELEROMETER) {
2376 data = mGData;
2377 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2378 data = mMData;
2379 } else {
2380 // we should not be here.
2381 return;
2382 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002383 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002384 data[i] = event.values[i];
2385 }
2386 float[] orientation = new float[3];
2387 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2388 SensorManager.getOrientation(mR, orientation);
2389 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2390 if (mHeading < 0) {
2391 mHeading += 360;
2392 }
Angus Kong0d00a892013-03-26 11:40:40 -07002393 }
Doris Liu6432cd62013-06-13 17:20:31 -07002394
Ruben Brunkd217ed02013-10-08 23:31:13 -07002395 // For debugging only.
2396 public void setDebugUri(Uri uri) {
2397 mDebugUri = uri;
2398 }
2399
2400 // For debugging only.
2401 private void saveToDebugUri(byte[] data) {
2402 if (mDebugUri != null) {
2403 OutputStream outputStream = null;
2404 try {
2405 outputStream = mContentResolver.openOutputStream(mDebugUri);
2406 outputStream.write(data);
2407 outputStream.close();
2408 } catch (IOException e) {
2409 Log.e(TAG, "Exception while writing debug jpeg file", e);
2410 } finally {
2411 CameraUtil.closeSilently(outputStream);
2412 }
2413 }
2414 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002415
2416 @Override
2417 public void onRemoteShutterPress() {
Sascha Haeberling57d4f742014-06-25 11:05:49 -07002418 mHandler.post(new Runnable() {
2419 @Override
2420 public void run() {
2421 focusAndCapture();
2422 }
2423 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002424 }
Michael Kolb8872c232013-01-29 10:33:22 -08002425}