blob: b4b5ee759d1cc3428296e27fadd19b93ae2c20b7 [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;
Michael Kolb8872c232013-01-29 10:33:22 -0800194 // The value for UI components like indicators.
195 private int mDisplayOrientation;
Angus Kong831347d2014-06-16 16:07:28 -0700196 // The value for cameradevice.CameraSettings.setPhotoRotationDegrees.
Michael Kolb8872c232013-01-29 10:33:22 -0800197 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700198 // Indicates whether we are using front camera
199 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800200 private boolean mFirstTimeInitialized;
201 private boolean mIsImageCaptureIntent;
202
Michael Kolb8872c232013-01-29 10:33:22 -0800203 private int mCameraState = PREVIEW_STOPPED;
204 private boolean mSnapshotOnIdle = false;
205
206 private ContentResolver mContentResolver;
207
Doris Liu2b906b82013-12-10 16:34:08 -0800208 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800209
Michael Kolb8872c232013-01-29 10:33:22 -0800210 private final PostViewPictureCallback mPostViewPictureCallback =
211 new PostViewPictureCallback();
212 private final RawPictureCallback mRawPictureCallback =
213 new RawPictureCallback();
214 private final AutoFocusCallback mAutoFocusCallback =
215 new AutoFocusCallback();
216 private final Object mAutoFocusMoveCallback =
217 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700218 ? new AutoFocusMoveCallback()
219 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800220
Michael Kolb8872c232013-01-29 10:33:22 -0800221 private long mFocusStartTime;
222 private long mShutterCallbackTime;
223 private long mPostViewPictureCallbackTime;
224 private long mRawPictureCallbackTime;
225 private long mJpegPictureCallbackTime;
226 private long mOnResumeTime;
227 private byte[] mJpegImageData;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700228 /** Touch coordinate for shutter button press. */
229 private TouchCoordinate mShutterTouchCoordinate;
230
Michael Kolb8872c232013-01-29 10:33:22 -0800231
232 // These latency time are for the CameraLatency test.
233 public long mAutoFocusTime;
234 public long mShutterLag;
235 public long mShutterToPictureDisplayedTime;
236 public long mPictureDisplayedToJpegCallbackTime;
237 public long mJpegCallbackFinishTime;
238 public long mCaptureStartTime;
239
240 // This handles everything about focus.
241 private FocusOverlayManager mFocusManager;
242
Doris Liubd1b8f92014-01-03 17:59:51 -0800243 private final int mGcamModeIndex;
Andy Huibersa31162c2014-09-02 11:27:11 -0700244 private SoundPlayer mCountdownSoundPlayer;
Doris Liubd1b8f92014-01-03 17:59:51 -0800245
Angus Kong6607dae2014-06-10 16:07:45 -0700246 private CameraCapabilities.SceneMode mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800247
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100248 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800249
Michael Kolb8872c232013-01-29 10:33:22 -0800250 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700251 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800252 private final float[] mGData = new float[3];
253 private final float[] mMData = new float[3];
254 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700255 private int mHeading = -1;
256
Sascha Haeberling375f9d12014-10-17 19:05:12 -0700257 /** Used to detect motion. We use this to release focus lock early. */
258 private MotionManager mMotionManager;
259
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800260 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700261 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700262
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800263 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800264 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800265 @Override
266 public void onMediaSaved(Uri uri) {
267 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700268 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800269 }
270 }
271 };
Angus Kong454d63f2014-05-06 14:45:59 -0700272 private boolean mShouldResizeTo16x9 = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800273
Doris Liudb8f9752014-05-12 15:25:13 -0700274 /**
Angus Kong3699c412014-05-23 15:31:09 -0700275 * We keep the flash setting before entering scene modes (HDR)
276 * and restore it after HDR is off.
277 */
278 private String mFlashModeBeforeSceneMode;
279
280 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700281 * This callback gets called when user select whether or not to
282 * turn on geo-tagging.
283 */
284 public interface LocationDialogCallback {
285 /**
286 * Gets called after user selected/unselected geo-tagging feature.
287 *
288 * @param selected whether or not geo-tagging feature is selected
289 */
290 public void onLocationTaggingSelected(boolean selected);
291 }
292
293 /**
294 * This callback defines the text that is shown in the aspect ratio selection
295 * dialog, provides the current aspect ratio, and gets notified when user changes
296 * aspect ratio selection in the dialog.
297 */
298 public interface AspectRatioDialogCallback {
299 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700300 * Returns current aspect ratio that is being used to set as default.
301 */
302 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
303
304 /**
305 * Gets notified when user has made the aspect ratio selection.
306 *
307 * @param newAspectRatio aspect ratio that user has selected
Doris Liu08b0cdd2014-05-13 19:19:55 -0700308 * @param dialogHandlingFinishedRunnable runnable to run when the operations
309 * needed to handle changes from dialog
310 * are finished.
Doris Liudb8f9752014-05-12 15:25:13 -0700311 */
Doris Liu08b0cdd2014-05-13 19:19:55 -0700312 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
313 Runnable dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700314 }
315
Angus Kongdcccc512013-08-08 17:06:03 -0700316 private void checkDisplayRotation() {
Senpo Hu9746ebe2014-10-14 11:51:56 -0700317 // Need to just be a no-op for the quick resume-pause scenario.
318 if (mPaused) {
319 return;
320 }
Angus Kongdcccc512013-08-08 17:06:03 -0700321 // Set the display orientation if display rotation has changed.
322 // Sometimes this happens when the device is held upside
323 // down and camera app is opened. Rotation animation will
324 // take some time and the rotation value we have got may be
325 // wrong. Framework does not have a callback for this now.
326 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
327 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800328 }
Angus Kongdcccc512013-08-08 17:06:03 -0700329 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
330 mHandler.postDelayed(new Runnable() {
331 @Override
332 public void run() {
333 checkDisplayRotation();
334 }
335 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800336 }
337 }
338
339 /**
340 * This Handler is used to post message back onto the main thread of the
341 * application
342 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100343 private static class MainHandler extends Handler {
344 private final WeakReference<PhotoModule> mModule;
345
346 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800347 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100348 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800349 }
350
Michael Kolb8872c232013-01-29 10:33:22 -0800351 @Override
352 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100353 PhotoModule module = mModule.get();
354 if (module == null) {
355 return;
356 }
Michael Kolb8872c232013-01-29 10:33:22 -0800357 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800358 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100359 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800360 break;
361 }
362
Angus Kong13e87c42013-11-25 10:02:47 -0800363 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100364 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800365 break;
366 }
Michael Kolb8872c232013-01-29 10:33:22 -0800367 }
368 }
369 }
370
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700371 private void switchToGcamCapture() {
372 if (mActivity != null && mGcamModeIndex != 0) {
373 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700374 settingsManager.set(SettingsManager.SCOPE_GLOBAL,
375 Keys.KEY_CAMERA_HDR_PLUS, true);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700376
377 // Disable the HDR+ button to prevent callbacks from being
378 // queued before the correct callback is attached to the button
379 // in the new module. The new module will set the enabled/disabled
380 // of this button when the module's preferred camera becomes available.
381 ButtonManager buttonManager = mActivity.getButtonManager();
Spike Sprague66d3d0d2014-08-18 14:11:33 -0700382
383 buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700384
Seth Raphael274f6e92014-05-21 17:11:53 -0700385 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
386
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700387 // Do not post this to avoid this module switch getting interleaved with
388 // other button callbacks.
389 mActivity.onModeSelected(mGcamModeIndex);
Spike Sprague66d3d0d2014-08-18 14:11:33 -0700390
391 buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700392 }
393 }
394
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800395 /**
396 * Constructs a new photo module.
397 */
Angus Kongc4e66562013-11-22 23:03:21 -0800398 public PhotoModule(AppController app) {
399 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800400 mGcamModeIndex = app.getAndroidContext().getResources()
401 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800402 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700403
Michael Kolb8872c232013-01-29 10:33:22 -0800404 @Override
Spike Sprague159e6e92014-05-27 18:26:30 -0700405 public String getPeekAccessibilityString() {
406 return mAppController.getAndroidContext()
407 .getResources().getString(R.string.photo_accessibility_peek);
408 }
409
410 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -0700411 public String getModuleStringIdentifier() {
412 return PHOTO_MODULE_STRING_ID;
413 }
414
415 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100416 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
417 mActivity = activity;
418 // TODO: Need to look at the controller interface to see if we can get
419 // rid of passing in the activity directly.
420 mAppController = mActivity;
421
422 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
423 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800424
425 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700426 mCameraId = settingsManager.getInteger(mAppController.getModuleScope(),
427 Keys.KEY_CAMERA_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800428
Doris Liudb8f9752014-05-12 15:25:13 -0700429 // TODO: Move this to SettingsManager as a part of upgrade procedure.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700430 if (!settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
431 Keys.KEY_USER_SELECTED_ASPECT_RATIO)) {
Doris Liudb8f9752014-05-12 15:25:13 -0700432 // Switch to back camera to set aspect ratio.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700433 mCameraId = settingsManager.getIntegerDefault(Keys.KEY_CAMERA_ID);
Doris Liudb8f9752014-05-12 15:25:13 -0700434 }
435
Michael Kolb8872c232013-01-29 10:33:22 -0800436 mContentResolver = mActivity.getContentResolver();
437
Michael Kolb8872c232013-01-29 10:33:22 -0800438 // Surface texture is from camera screen nail and startPreview needs it.
439 // This must be done before startPreview.
440 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800441
Michael Kolb8872c232013-01-29 10:33:22 -0800442 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800443 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Doris Liu6c751642014-05-05 18:43:26 -0700444 mUI.setCountdownFinishedListener(this);
Andy Huibersa31162c2014-09-02 11:27:11 -0700445 mCountdownSoundPlayer = new SoundPlayer(mAppController.getAndroidContext());
Doris Liu6c751642014-05-05 18:43:26 -0700446
447 // TODO: Make this a part of app controller API.
448 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
449 cancelButton.setOnClickListener(new View.OnClickListener() {
450 @Override
451 public void onClick(View view) {
452 cancelCountDown();
453 }
454 });
455 }
456
457 private void cancelCountDown() {
458 if (mUI.isCountingDown()) {
459 // Cancel on-going countdown.
460 mUI.cancelCountDown();
461 }
462 mAppController.getCameraAppUI().transitionToCapture();
463 mAppController.getCameraAppUI().showModeOptions();
Spike Sprague8d1ac992014-10-13 14:49:06 -0700464 mAppController.setShutterEnabled(true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800465 }
466
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800467 @Override
468 public boolean isUsingBottomBar() {
469 return true;
470 }
471
Michael Kolbd6954f32013-03-08 20:43:01 -0800472 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800473 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800474 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800475 setupCaptureParams();
476 }
477 }
478
479 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800480 mAppController.onPreviewStarted();
Alan Newbergerffc395d2014-09-10 13:07:18 -0700481 mAppController.setShutterEnabled(true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800482 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800483 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700484 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800485 }
486
Doris Liudb8f9752014-05-12 15:25:13 -0700487 /**
488 * Prompt the user to pick to record location and choose aspect ratio for the
489 * very first run of camera only.
490 */
491 private void settingsFirstRun() {
492 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100493
Doris Liudb8f9752014-05-12 15:25:13 -0700494 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800495 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800496 }
Doris Liudb8f9752014-05-12 15:25:13 -0700497
Erin Dahlgren6190c362014-06-13 14:12:08 -0700498 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SCOPE_GLOBAL,
499 Keys.KEY_RECORD_LOCATION);
Doris Liudb8f9752014-05-12 15:25:13 -0700500 boolean aspectRatioPrompt = !settingsManager.getBoolean(
Erin Dahlgren6190c362014-06-13 14:12:08 -0700501 SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO);
Doris Liudb8f9752014-05-12 15:25:13 -0700502 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800503 return;
504 }
Doris Liudb8f9752014-05-12 15:25:13 -0700505
Michael Kolb8872c232013-01-29 10:33:22 -0800506 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800507 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800508 if (backCameraId == -1) {
509 // If there is no back camera, do not show the prompt.
510 return;
511 }
Doris Liudb8f9752014-05-12 15:25:13 -0700512
513 if (locationPrompt) {
514 // Show both location and aspect ratio selection dialog.
515 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700516 @Override
Doris Liudb8f9752014-05-12 15:25:13 -0700517 public void onLocationTaggingSelected(boolean selected) {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700518 Keys.setLocation(mActivity.getSettingsManager(), selected,
519 mActivity.getLocationManager());
Doris Liudb8f9752014-05-12 15:25:13 -0700520 }
521 }, createAspectRatioDialogCallback());
522 } else {
523 // App upgrade. Only show aspect ratio selection.
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700524 boolean wasShown = mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
525 if (!wasShown) {
526 // If the dialog was not shown, set this flag to true so that we
527 // never have to check for it again. It means that we don't need
528 // to show the dialog on this device.
529 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
530 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
531 }
Doris Liudb8f9752014-05-12 15:25:13 -0700532 }
533 }
534
535 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -0700536 Size currentSize = mCameraSettings.getCurrentPhotoSize();
Doris Liudb8f9752014-05-12 15:25:13 -0700537 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
538 if (aspectRatio < 1f) {
539 aspectRatio = 1 / aspectRatio;
540 }
541 final AspectRatioSelector.AspectRatio currentAspectRatio;
542 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
543 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
544 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
545 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
546 } else {
547 // TODO: Log error and not show dialog.
548 return null;
549 }
550
Angus Kong6607dae2014-06-10 16:07:45 -0700551 List<Size> sizes = mCameraCapabilities.getSupportedPhotoSizes();
Doris Liudb8f9752014-05-12 15:25:13 -0700552 List<Size> pictureSizes = ResolutionUtil
553 .getDisplayableSizesFromSupported(sizes, true);
554
555 // This logic below finds the largest resolution for each aspect ratio.
556 // TODO: Move this somewhere that can be shared with SettingsActivity
557 int aspectRatio4x3Resolution = 0;
558 int aspectRatio16x9Resolution = 0;
559 Size largestSize4x3 = new Size(0, 0);
560 Size largestSize16x9 = new Size(0, 0);
561 for (Size size : pictureSizes) {
562 float pictureAspectRatio = (float) size.width() / (float) size.height();
563 pictureAspectRatio = pictureAspectRatio < 1 ?
564 1f / pictureAspectRatio : pictureAspectRatio;
565 int resolution = size.width() * size.height();
566 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
567 if (resolution > aspectRatio4x3Resolution) {
568 aspectRatio4x3Resolution = resolution;
569 largestSize4x3 = size;
570 }
571 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
572 if (resolution > aspectRatio16x9Resolution) {
573 aspectRatio16x9Resolution = resolution;
574 largestSize16x9 = size;
575 }
576 }
577 }
Doris Liudb8f9752014-05-12 15:25:13 -0700578
Doris Liu08b0cdd2014-05-13 19:19:55 -0700579 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
580 final Size size4x3ToSelect = largestSize4x3;
581 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700582
Doris Liudb8f9752014-05-12 15:25:13 -0700583 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
Doris Liudb8f9752014-05-12 15:25:13 -0700584
585 @Override
586 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
587 return currentAspectRatio;
588 }
589
590 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700591 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
592 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700593 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700594 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700595 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
596 Keys.KEY_PICTURE_SIZE_BACK,
597 largestSize4x3Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700598 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700599 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700600 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
601 Keys.KEY_PICTURE_SIZE_BACK,
602 largestSize16x9Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700603 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700604 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
605 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
606 String aspectRatio = mActivity.getSettingsManager().getString(
607 SettingsManager.SCOPE_GLOBAL,
608 Keys.KEY_USER_SELECTED_ASPECT_RATIO);
609 Log.e(TAG, "aspect ratio after setting it to true=" + aspectRatio);
Doris Liudb8f9752014-05-12 15:25:13 -0700610 if (newAspectRatio != currentAspectRatio) {
Alan Newberger19597372014-10-03 18:27:50 -0700611 Log.i(TAG, "changing aspect ratio from dialog");
Doris Liudb8f9752014-05-12 15:25:13 -0700612 stopPreview();
613 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700614 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
615 } else {
616 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700617 }
618 }
619 };
620 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700621 }
Michael Kolb8872c232013-01-29 10:33:22 -0800622
ztenghui7b265a62013-09-09 14:58:44 -0700623 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700624 public void onPreviewUIReady() {
Alan Newberger19597372014-10-03 18:27:50 -0700625 Log.i(TAG, "onPreviewUIReady");
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800626 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700627 }
628
629 @Override
630 public void onPreviewUIDestroyed() {
631 if (mCameraDevice == null) {
632 return;
633 }
634 mCameraDevice.setPreviewTexture(null);
635 stopPreview();
636 }
637
Doris Liu1dfe7822013-12-12 00:02:08 -0800638 @Override
639 public void startPreCaptureAnimation() {
640 mAppController.startPreCaptureAnimation();
641 }
642
Michael Kolbd6954f32013-03-08 20:43:01 -0800643 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800644 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800645 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800646 }
647
Michael Kolbd6954f32013-03-08 20:43:01 -0800648 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800649 if (mPaused) {
650 return;
651 }
Doris Liu6c751642014-05-05 18:43:26 -0700652 cancelCountDown();
Doris Liu6809eb82014-05-21 13:16:59 -0700653
654 mAppController.freezeScreenUntilPreviewReady();
Erin Dahlgren357b7672013-11-20 17:38:14 -0800655 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800656
Alan Newbergerd41766f2014-04-09 18:25:34 -0700657 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800658 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800659 mCameraId = mPendingSwitchCameraId;
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700660
Erin Dahlgren6190c362014-06-13 14:12:08 -0700661 settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId);
Sol Boucher44ce4b22014-08-04 23:41:38 -0700662 requestCameraOpen();
Michael Kolbd6954f32013-03-08 20:43:01 -0800663 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800664 if (mFocusManager != null) {
665 mFocusManager.removeMessages();
666 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800667
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700668 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700669 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700670 // Start switch camera animation. Post a message because
671 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800672 }
673
Sol Boucher44ce4b22014-08-04 23:41:38 -0700674 /**
675 * Uses the {@link CameraProvider} to open the currently-selected camera
676 * device, using {@link GservicesHelper} to choose between API-1 and API-2.
677 */
678 private void requestCameraOpen() {
Alan Newberger29a009c2014-09-05 12:47:42 -0700679 Log.v(TAG, "requestCameraOpen");
Sol Boucher44ce4b22014-08-04 23:41:38 -0700680 mActivity.getCameraProvider().requestCamera(mCameraId,
681 GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity));
682 }
683
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800684 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100685 new ButtonManager.ButtonCallback() {
686 @Override
687 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800688 // At the time this callback is fired, the camera id
689 // has be set to the desired camera.
690
Angus Kong97e282a2014-03-04 18:44:49 -0800691 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100692 return;
693 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800694 // If switching to back camera, and HDR+ is still on,
695 // switch back to gcam, otherwise handle callback normally.
696 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700697 if (Keys.isCameraBackFacing(settingsManager,
698 mAppController.getModuleScope())) {
699 if (Keys.requestsReturnToHdrPlus(settingsManager,
700 mAppController.getModuleScope())) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700701 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800702 return;
703 }
704 }
705
Sascha Haeberlingde303232014-02-07 02:30:53 +0100706 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800707
Alan Newbergerd41766f2014-04-09 18:25:34 -0700708 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100709 // We need to keep a preview frame for the animation before
710 // releasing the camera. This will trigger
711 // onPreviewTextureCopied.
712 // TODO: Need to animate the camera switch
713 switchCamera();
714 }
715 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800716
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800717 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100718 new ButtonManager.ButtonCallback() {
719 @Override
720 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700721 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700722 if (GcamHelper.hasGcamAsSeparateModule()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100723 // Set the camera setting to default backfacing.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700724 settingsManager.setToDefault(mAppController.getModuleScope(),
725 Keys.KEY_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700726 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100727 } else {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700728 if (Keys.isHdrOn(settingsManager)) {
Angus Kong831347d2014-06-16 16:07:28 -0700729 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
730 mCameraCapabilities.getStringifier().stringify(
731 CameraCapabilities.SceneMode.HDR));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700732 } else {
Angus Kong831347d2014-06-16 16:07:28 -0700733 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
734 mCameraCapabilities.getStringifier().stringify(
735 CameraCapabilities.SceneMode.AUTO));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700736 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100737 updateParametersSceneMode();
Senpo Huee3123b2014-09-25 10:53:12 -0700738 if (mCameraDevice != null) {
739 mCameraDevice.applySettings(mCameraSettings);
740 }
Doris Liu8ad8ad42014-03-27 14:43:31 -0700741 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100742 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800743 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100744 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800745
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800746 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
747 @Override
748 public void onClick(View v) {
749 onCaptureCancelled();
750 }
751 };
752
753 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
754 @Override
755 public void onClick(View v) {
756 onCaptureDone();
757 }
758 };
759
760 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
761 @Override
762 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800763 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800764 onCaptureRetake();
765 }
766 };
767
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800768 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700769 public void hardResetSettings(SettingsManager settingsManager) {
Erin Dahlgrene1547932014-06-23 15:17:07 -0700770 // PhotoModule should hard reset HDR+ to off,
771 // and HDR to off if HDR+ is supported.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700772 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700773 if (GcamHelper.hasGcamAsSeparateModule()) {
Erin Dahlgren21a62362014-06-24 10:13:01 -0700774 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false);
Erin Dahlgrene1547932014-06-23 15:17:07 -0700775 }
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700776 }
777
778 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800779 public HardwareSpec getHardwareSpec() {
Angus Kong831347d2014-06-16 16:07:28 -0700780 return (mCameraSettings != null ?
781 new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800782 }
783
784 @Override
785 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
786 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
787
788 bottomBarSpec.enableCamera = true;
789 bottomBarSpec.cameraCallback = mCameraCallback;
Erin Dahlgren6190c362014-06-13 14:12:08 -0700790 bottomBarSpec.enableFlash = !mAppController.getSettingsManager()
791 .getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR);
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800792 bottomBarSpec.enableHdr = true;
793 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800794 bottomBarSpec.enableGridLines = true;
Spike Sprague8f7425c2014-05-12 11:08:34 -0700795 if (mCameraCapabilities != null) {
796 bottomBarSpec.enableExposureCompensation = true;
797 bottomBarSpec.exposureCompensationSetCallback =
798 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
799 @Override
800 public void setExposure(int value) {
801 setExposureCompensation(value);
802 }
803 };
804 bottomBarSpec.minExposureCompensation =
805 mCameraCapabilities.getMinExposureCompensation();
806 bottomBarSpec.maxExposureCompensation =
807 mCameraCapabilities.getMaxExposureCompensation();
808 bottomBarSpec.exposureCompensationStep =
809 mCameraCapabilities.getExposureCompensationStep();
810 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800811
Doris Liu6c751642014-05-05 18:43:26 -0700812 bottomBarSpec.enableSelfTimer = true;
Sascha Haeberling4333fac2014-05-20 16:28:11 -0700813 bottomBarSpec.showSelfTimer = true;
Doris Liu6c751642014-05-05 18:43:26 -0700814
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800815 if (isImageCaptureIntent()) {
816 bottomBarSpec.showCancel = true;
817 bottomBarSpec.cancelCallback = mCancelCallback;
818 bottomBarSpec.showDone = true;
819 bottomBarSpec.doneCallback = mDoneCallback;
820 bottomBarSpec.showRetake = true;
821 bottomBarSpec.retakeCallback = mRetakeCallback;
822 }
823
824 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800825 }
826
Michael Kolbd6954f32013-03-08 20:43:01 -0800827 // either open a new camera or switch cameras
828 private void openCameraCommon() {
Angus Kong6607dae2014-06-10 16:07:45 -0700829 mUI.onCameraOpened(mCameraCapabilities, mCameraSettings);
Angus Kong0fb819b2013-10-08 13:44:19 -0700830 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800831 // Set hdr plus to default: off.
832 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700833 settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL,
834 Keys.KEY_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700835 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800836 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800837 }
838
ztenghui7b265a62013-09-09 14:58:44 -0700839 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800840 public void updatePreviewAspectRatio(float aspectRatio) {
841 mAppController.updatePreviewAspectRatio(aspectRatio);
842 }
843
Michael Kolb8872c232013-01-29 10:33:22 -0800844 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800845 SettingsManager settingsManager = mActivity.getSettingsManager();
846 if (settingsManager == null) {
847 Log.e(TAG, "Settings manager is null!");
848 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800849 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700850 settingsManager.setToDefault(mAppController.getCameraScope(),
851 Keys.KEY_EXPOSURE);
Michael Kolb8872c232013-01-29 10:33:22 -0800852 }
853
Michael Kolb8872c232013-01-29 10:33:22 -0800854 // Snapshots can only be taken after this is called. It should be called
855 // once only. We could have done these things in onCreate() but we want to
856 // make preview screen appear as soon as possible.
857 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700858 if (mFirstTimeInitialized || mPaused) {
859 return;
860 }
Michael Kolb8872c232013-01-29 10:33:22 -0800861
Michael Kolbd6954f32013-03-08 20:43:01 -0800862 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800863
Angus Kong86d36312013-01-31 18:22:44 -0800864 // We set the listener only when both service and shutterbutton
865 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800866 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800867
Michael Kolb8872c232013-01-29 10:33:22 -0800868 mNamedImages = new NamedImages();
869
870 mFirstTimeInitialized = true;
871 addIdleHandler();
872
Spike Spraguee6374b72014-04-25 17:24:32 -0700873 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800874 }
875
Michael Kolbd6954f32013-03-08 20:43:01 -0800876 // If the activity is paused and resumed, this method will be called in
877 // onResume.
878 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800879 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800880 mNamedImages = new NamedImages();
Angus Kong6607dae2014-06-10 16:07:45 -0700881 mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings);
Michael Kolbd6954f32013-03-08 20:43:01 -0800882 }
883
Michael Kolb8872c232013-01-29 10:33:22 -0800884 private void addIdleHandler() {
885 MessageQueue queue = Looper.myQueue();
886 queue.addIdleHandler(new MessageQueue.IdleHandler() {
887 @Override
888 public boolean queueIdle() {
889 Storage.ensureOSXCompatible();
890 return false;
891 }
892 });
893 }
894
Michael Kolb8872c232013-01-29 10:33:22 -0800895 @Override
896 public void startFaceDetection() {
Senpo Huee3123b2014-09-25 10:53:12 -0700897 if (mFaceDetectionStarted || mCameraDevice == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800898 return;
899 }
Angus Kong88289042014-04-22 16:39:42 -0700900 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800901 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700902 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700903 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800904 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700905 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800906 }
907 }
908
Michael Kolb8872c232013-01-29 10:33:22 -0800909 @Override
910 public void stopFaceDetection() {
Senpo Huee3123b2014-09-25 10:53:12 -0700911 if (!mFaceDetectionStarted || mCameraDevice == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800912 return;
913 }
Angus Kong88289042014-04-22 16:39:42 -0700914 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800915 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700916 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800917 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800918 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700919 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800920 }
921 }
922
Michael Kolb8872c232013-01-29 10:33:22 -0800923 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700924 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700925
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800926 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700927
Sascha Haeberling37f36112013-08-06 14:31:52 -0700928 public ShutterCallback(boolean needsAnimation) {
929 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700930 }
931
Michael Kolb8872c232013-01-29 10:33:22 -0800932 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700933 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800934 mShutterCallbackTime = System.currentTimeMillis();
935 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
936 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700937 if (mNeedsAnimation) {
938 mActivity.runOnUiThread(new Runnable() {
939 @Override
940 public void run() {
941 animateAfterShutter();
942 }
943 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700944 }
Michael Kolb8872c232013-01-29 10:33:22 -0800945 }
946 }
947
Angus Kong9ef99252013-07-18 18:04:19 -0700948 private final class PostViewPictureCallback
949 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800950 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800951 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800952 mPostViewPictureCallbackTime = System.currentTimeMillis();
953 Log.v(TAG, "mShutterToPostViewCallbackTime = "
954 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
955 + "ms");
956 }
957 }
958
Angus Kong9ef99252013-07-18 18:04:19 -0700959 private final class RawPictureCallback
960 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800961 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800962 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800963 mRawPictureCallbackTime = System.currentTimeMillis();
964 Log.v(TAG, "mShutterToRawCallbackTime = "
965 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
966 }
967 }
968
Angus Kong454d63f2014-05-06 14:45:59 -0700969 private static class ResizeBundle {
970 byte[] jpegData;
971 float targetAspectRatio;
972 ExifInterface exif;
973 }
974
975 /**
976 * @return Cropped image if the target aspect ratio is larger than the jpeg
977 * aspect ratio on the long axis. The original jpeg otherwise.
978 */
979 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
980
981 final byte[] jpegData = dataBundle.jpegData;
982 final ExifInterface exif = dataBundle.exif;
983 float targetAspectRatio = dataBundle.targetAspectRatio;
984
985 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
986 int originalWidth = original.getWidth();
987 int originalHeight = original.getHeight();
988 int newWidth;
989 int newHeight;
990
991 if (originalWidth > originalHeight) {
992 newHeight = (int) (originalWidth / targetAspectRatio);
993 newWidth = originalWidth;
994 } else {
995 newWidth = (int) (originalHeight / targetAspectRatio);
996 newHeight = originalHeight;
997 }
998 int xOffset = (originalWidth - newWidth)/2;
999 int yOffset = (originalHeight - newHeight)/2;
1000
1001 if (xOffset < 0 || yOffset < 0) {
1002 return dataBundle;
1003 }
1004
1005 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
1006 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
1007 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
1008
1009 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1010
1011 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
1012 dataBundle.jpegData = stream.toByteArray();
1013 return dataBundle;
1014 }
1015
Angus Kong9ef99252013-07-18 18:04:19 -07001016 private final class JpegPictureCallback
1017 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001018 Location mLocation;
1019
1020 public JpegPictureCallback(Location loc) {
1021 mLocation = loc;
1022 }
1023
1024 @Override
Angus Kong454d63f2014-05-06 14:45:59 -07001025 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Alan Newberger19597372014-10-03 18:27:50 -07001026 Log.i(TAG, "onPictureTaken");
Erin Dahlgren667630d2014-04-01 14:03:25 -07001027 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001028 if (mPaused) {
1029 return;
1030 }
Doris Liu6432cd62013-06-13 17:20:31 -07001031 if (mIsImageCaptureIntent) {
1032 stopPreview();
1033 }
Angus Kong6607dae2014-06-10 16:07:45 -07001034 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001035 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001036 }
1037
1038 mJpegPictureCallbackTime = System.currentTimeMillis();
1039 // If postview callback has arrived, the captured image is displayed
1040 // in postview callback. If not, the captured image is displayed in
1041 // raw picture callback.
1042 if (mPostViewPictureCallbackTime != 0) {
1043 mShutterToPictureDisplayedTime =
1044 mPostViewPictureCallbackTime - mShutterCallbackTime;
1045 mPictureDisplayedToJpegCallbackTime =
1046 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
1047 } else {
1048 mShutterToPictureDisplayedTime =
1049 mRawPictureCallbackTime - mShutterCallbackTime;
1050 mPictureDisplayedToJpegCallbackTime =
1051 mJpegPictureCallbackTime - mRawPictureCallbackTime;
1052 }
1053 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
1054 + mPictureDisplayedToJpegCallbackTime + "ms");
1055
Michael Kolb8872c232013-01-29 10:33:22 -08001056 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
1057 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001058 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001059 }
1060
Angus Kong454d63f2014-05-06 14:45:59 -07001061 long now = System.currentTimeMillis();
1062 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1063 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1064 mJpegPictureCallbackTime = 0;
1065
1066 final ExifInterface exif = Exif.getExif(originalJpegData);
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001067 final NamedEntity name = mNamedImages.getNextNameEntity();
Angus Kong454d63f2014-05-06 14:45:59 -07001068 if (mShouldResizeTo16x9) {
1069 final ResizeBundle dataBundle = new ResizeBundle();
1070 dataBundle.jpegData = originalJpegData;
1071 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1072 dataBundle.exif = exif;
1073 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1074
1075 @Override
1076 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1077 return cropJpegDataToAspectRatio(resizeBundles[0]);
1078 }
1079
1080 @Override
1081 protected void onPostExecute(ResizeBundle result) {
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001082 saveFinalPhoto(result.jpegData, name, result.exif, camera);
Angus Kong454d63f2014-05-06 14:45:59 -07001083 }
1084 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1085
1086 } else {
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001087 saveFinalPhoto(originalJpegData, name, exif, camera);
Angus Kong454d63f2014-05-06 14:45:59 -07001088 }
1089 }
1090
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001091 void saveFinalPhoto(final byte[] jpegData, NamedEntity name, final ExifInterface exif,
1092 CameraProxy camera) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001093 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001094
Sol Boucher2192fba2014-08-19 17:24:07 -07001095 float zoomValue = 1.0f;
Angus Kong88289042014-04-22 16:39:42 -07001096 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07001097 zoomValue = mCameraSettings.getCurrentZoomRatio();
Andy Huiberse08bc042014-04-11 19:26:47 -07001098 }
Angus Kong6607dae2014-06-10 16:07:45 -07001099 boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
Andy Huibers203abe52014-05-19 13:59:01 -07001100 String flashSetting =
Erin Dahlgren6190c362014-06-13 14:12:08 -07001101 mActivity.getSettingsManager().getString(mAppController.getCameraScope(),
1102 Keys.KEY_FLASH_MODE);
1103 boolean gridLinesOn = Keys.areGridLinesOn(mActivity.getSettingsManager());
Andy Huibers10c58162014-03-29 14:06:54 -07001104 UsageStatistics.instance().photoCaptureDoneEvent(
1105 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
Sascha Haeberlingce90f7b2014-10-10 15:20:35 -07001106 name.title + ".jpg", exif,
Andy Huibers547d7c82014-05-20 23:03:18 -07001107 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001108 (float) mTimerDuration, mShutterTouchCoordinate, mVolumeButtonClickedFlag);
1109 mShutterTouchCoordinate = null;
1110 mVolumeButtonClickedFlag = false;
Ruben Brunkd217ed02013-10-08 23:31:13 -07001111
Ruben Brunkd7488272013-10-10 18:45:53 -07001112 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001113 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001114 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1115 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001116 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001117 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1118 width = exifWidth;
1119 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001120 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001121 Size s;
Angus Kong6607dae2014-06-10 16:07:45 -07001122 s = mCameraSettings.getCurrentPhotoSize();
Angus Kong454d63f2014-05-06 14:45:59 -07001123 if ((mJpegRotation + orientation) % 180 == 0) {
1124 width = s.width();
1125 height = s.height();
1126 } else {
1127 width = s.height();
1128 height = s.width();
1129 }
Michael Kolb8872c232013-01-29 10:33:22 -08001130 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001131 String title = (name == null) ? null : name.title;
1132 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001133
1134 // Handle debug mode outputs
1135 if (mDebugUri != null) {
1136 // If using a debug uri, save jpeg there.
1137 saveToDebugUri(jpegData);
1138
1139 // Adjust the title of the debug image shown in mediastore.
1140 if (title != null) {
1141 title = DEBUG_IMAGE_PREFIX + title;
1142 }
1143 }
1144
Michael Kolb8872c232013-01-29 10:33:22 -08001145 if (title == null) {
1146 Log.e(TAG, "Unbalanced name/data pair");
1147 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001148 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001149 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001150 }
Angus Kong0d00a892013-03-26 11:40:40 -07001151 if (mHeading >= 0) {
1152 // heading direction has been updated by the sensor.
1153 ExifTag directionRefTag = exif.buildTag(
1154 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1155 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1156 ExifTag directionTag = exif.buildTag(
1157 ExifInterface.TAG_GPS_IMG_DIRECTION,
1158 new Rational(mHeading, 1));
1159 exif.setTag(directionRefTag);
1160 exif.setTag(directionTag);
1161 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001162 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001163 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001164 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001165 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001166 // Animate capture with real jpeg data instead of a preview
1167 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001168 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001169 } else {
1170 mJpegImageData = jpegData;
1171 if (!mQuickCapture) {
Alan Newberger7dabda62014-10-15 15:28:01 -07001172 Log.v(TAG, "showing UI");
Doris Liu36e56fb2013-09-11 17:38:08 -07001173 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001174 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001175 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001176 }
1177 }
1178
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001179 // Send the taken photo to remote shutter listeners, if any are
1180 // registered.
Sascha Haeberling3c6de142014-07-14 12:23:36 -07001181 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001182
Michael Kolb8872c232013-01-29 10:33:22 -08001183 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001184 // latency. It's true that someone else could write to the SD card
1185 // in the mean time and fill it, but that could have happened
1186 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001187 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001188 }
1189 }
1190
Angus Kong9ef99252013-07-18 18:04:19 -07001191 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001192 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001193 public void onAutoFocus(boolean focused, CameraProxy camera) {
1194 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001195 if (mPaused) {
1196 return;
1197 }
Michael Kolb8872c232013-01-29 10:33:22 -08001198
1199 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001200 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001201 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001202 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001203 }
1204 }
1205
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001206 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001207 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001208 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001209 @Override
1210 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001211 boolean moving, CameraProxy camera) {
1212 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001213 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001214 }
1215 }
1216
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001217 /**
1218 * This class is just a thread-safe queue for name,date holder objects.
1219 */
1220 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001221 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001222
1223 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001224 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001225 }
1226
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001227 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001228 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001229 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001230 r.date = date;
1231 mQueue.add(r);
1232 }
1233
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001234 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001235 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001236 if (!mQueue.isEmpty()) {
1237 return mQueue.remove(0);
1238 }
Michael Kolb8872c232013-01-29 10:33:22 -08001239 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001240 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001241 }
1242
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001243 public static class NamedEntity {
1244 public String title;
1245 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001246 }
1247 }
1248
1249 private void setCameraState(int state) {
1250 mCameraState = state;
1251 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001252 case PREVIEW_STOPPED:
1253 case SNAPSHOT_IN_PROGRESS:
1254 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001255 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001256 break;
1257 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001258 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001259 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001260 }
1261 }
1262
Sascha Haeberling37f36112013-08-06 14:31:52 -07001263 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001264 // Only animate when in full screen capture mode
1265 // i.e. If monkey/a user swipes to the gallery during picture taking,
1266 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001267 if (!mIsImageCaptureIntent) {
1268 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001269 }
Michael Kolb8872c232013-01-29 10:33:22 -08001270 }
1271
1272 @Override
1273 public boolean capture() {
Alan Newberger7dabda62014-10-15 15:28:01 -07001274 Log.i(TAG, "capture");
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001275 // If we are already in the middle of taking a snapshot or the image
1276 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001277 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Spike Sprague8d1ac992014-10-13 14:49:06 -07001278 || mCameraState == SWITCHING_CAMERA) {
Michael Kolb8872c232013-01-29 10:33:22 -08001279 return false;
1280 }
Spike Sprague8d1ac992014-10-13 14:49:06 -07001281 setCameraState(SNAPSHOT_IN_PROGRESS);
1282
Michael Kolb8872c232013-01-29 10:33:22 -08001283 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001284
Michael Kolb8872c232013-01-29 10:33:22 -08001285 mPostViewPictureCallbackTime = 0;
1286 mJpegImageData = null;
1287
Angus Kong6607dae2014-06-10 16:07:45 -07001288 final boolean animateBefore = (mSceneMode == CameraCapabilities.SceneMode.HDR);
Michael Kolb8872c232013-01-29 10:33:22 -08001289
1290 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001291 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -08001292 }
1293
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -08001294 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kong6607dae2014-06-10 16:07:45 -07001295 CameraUtil.setGpsParameters(mCameraSettings, loc);
1296 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb8872c232013-01-29 10:33:22 -08001297
Senpo Hubb1c72f2014-09-08 01:07:27 -07001298 // Set JPEG orientation. Even if screen UI is locked in portrait, camera orientation should
1299 // still match device orientation (e.g., users should always get landscape photos while
1300 // capturing by putting device in landscape.)
1301 int orientation = mActivity.isAutoRotateScreen() ? mDisplayRotation : mOrientation;
1302 Characteristics info = mActivity.getCameraProvider().getCharacteristics(mCameraId);
1303 mJpegRotation = info.getJpegOrientation(orientation);
1304 mCameraDevice.setJpegOrientation(mJpegRotation);
1305
Alan Newbergere9f7b2d2014-10-22 17:42:47 -07001306 Log.v(TAG, "capture orientation (screen:device:used:jpeg) " +
1307 mDisplayRotation + ":" + mOrientation + ":" +
Alan Newberger05edd2e2014-10-20 17:31:33 -07001308 orientation + ":" + mJpegRotation);
1309
Angus Kong9ef99252013-07-18 18:04:19 -07001310 mCameraDevice.takePicture(mHandler,
1311 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001312 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001313 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001314
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001315 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001316
1317 mFaceDetectionStarted = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001318 return true;
1319 }
1320
1321 @Override
1322 public void setFocusParameters() {
1323 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1324 }
1325
Michael Kolbd6954f32013-03-08 20:43:01 -08001326 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001327 // If scene mode is set, we cannot set flash mode, white balance, and
1328 // focus mode, instead, we read it from driver
Angus Kong6607dae2014-06-10 16:07:45 -07001329 if (CameraCapabilities.SceneMode.AUTO != mSceneMode) {
1330 overrideCameraSettings(mCameraSettings.getCurrentFlashMode(),
1331 mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001332 }
1333 }
1334
Angus Kong6607dae2014-06-10 16:07:45 -07001335 private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode,
1336 CameraCapabilities.FocusMode focusMode) {
1337 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrene419b192013-12-03 13:10:27 -08001338 SettingsManager settingsManager = mActivity.getSettingsManager();
Alan Newberger347dccb2014-09-24 17:19:25 -07001339 if (!CameraCapabilities.FlashMode.NO_FLASH.equals(flashMode)) {
1340 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE,
1341 stringifier.stringify(flashMode));
1342 }
Erin Dahlgren6190c362014-06-13 14:12:08 -07001343 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE,
Alan Newberger347dccb2014-09-24 17:19:25 -07001344 stringifier.stringify(focusMode));
Michael Kolb8872c232013-01-29 10:33:22 -08001345 }
1346
1347 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001348 public void onOrientationChanged(int orientation) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001349 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1350 return;
1351 }
Senpo Hubb1c72f2014-09-08 01:07:27 -07001352
1353 // TODO: Document orientation compute logic and unify them in OrientationManagerImpl.
1354 // b/17443789
1355 // Flip to counter-clockwise orientation.
1356 mOrientation = (360 - orientation) % 360;
Michael Kolb8872c232013-01-29 10:33:22 -08001357 }
1358
1359 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001360 public void onCameraAvailable(CameraProxy cameraProxy) {
Alan Newberger19597372014-10-03 18:27:50 -07001361 Log.i(TAG, "onCameraAvailable");
Angus Kong20fad242013-11-11 18:23:46 -08001362 if (mPaused) {
1363 return;
1364 }
1365 mCameraDevice = cameraProxy;
1366
1367 initializeCapabilities();
1368
1369 // Reset zoom value index.
Sol Boucher2192fba2014-08-19 17:24:07 -07001370 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001371 if (mFocusManager == null) {
1372 initializeFocusManager();
1373 }
Angus Kong831347d2014-06-16 16:07:28 -07001374 mFocusManager.updateCapabilities(mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001375
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001376 // Do camera parameter dependent initialization.
Angus Kong6607dae2014-06-10 16:07:45 -07001377 mCameraSettings = mCameraDevice.getSettings();
Alan Newberger19597372014-10-03 18:27:50 -07001378
1379 setCameraParameters(UPDATE_PARAM_ALL);
1380 // Set a listener which updates camera parameters based
1381 // on changed settings.
1382 SettingsManager settingsManager = mActivity.getSettingsManager();
1383 settingsManager.addListener(this);
1384 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001385
Angus Kong20fad242013-11-11 18:23:46 -08001386 startPreview();
1387
1388 onCameraOpened();
1389 }
1390
1391 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001392 public void onCaptureCancelled() {
1393 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1394 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001395 }
1396
Michael Kolbd6954f32013-03-08 20:43:01 -08001397 @Override
1398 public void onCaptureRetake() {
Alan Newberger19597372014-10-03 18:27:50 -07001399 Log.i(TAG, "onCaptureRetake");
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001400 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001401 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001402 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001403 mUI.hidePostCaptureAlert();
Spike Spragueb4a22222014-05-22 14:40:53 -07001404 mUI.hideIntentReviewImageView();
Michael Kolb8872c232013-01-29 10:33:22 -08001405 setupPreview();
1406 }
1407
Michael Kolbd6954f32013-03-08 20:43:01 -08001408 @Override
1409 public void onCaptureDone() {
Alan Newberger7dabda62014-10-15 15:28:01 -07001410 Log.i(TAG, "onCaptureDone");
Michael Kolb8872c232013-01-29 10:33:22 -08001411 if (mPaused) {
1412 return;
1413 }
1414
1415 byte[] data = mJpegImageData;
1416
1417 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001418 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001419 // caller specifies a "save uri" then write the data to its
1420 // stream. Otherwise, pass back a scaled down version of the bitmap
1421 // directly in the extras.
1422 if (mSaveUri != null) {
1423 OutputStream outputStream = null;
1424 try {
1425 outputStream = mContentResolver.openOutputStream(mSaveUri);
1426 outputStream.write(data);
1427 outputStream.close();
1428
Alan Newbergerf974ba72014-09-08 14:44:30 -07001429 Log.v(TAG, "saved result to URI: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001430 mActivity.setResultEx(Activity.RESULT_OK);
1431 mActivity.finish();
1432 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001433 Log.w(TAG, "exception saving result to URI: " + mSaveUri, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001434 // ignore exception
1435 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001436 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001437 }
1438 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001439 ExifInterface exif = Exif.getExif(data);
1440 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001441 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1442 bitmap = CameraUtil.rotate(bitmap, orientation);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001443 Log.v(TAG, "inlined bitmap into capture intent result");
Michael Kolb8872c232013-01-29 10:33:22 -08001444 mActivity.setResultEx(Activity.RESULT_OK,
1445 new Intent("inline-data").putExtra("data", bitmap));
1446 mActivity.finish();
1447 }
1448 } else {
1449 // Save the image to a temp file and invoke the cropper
1450 Uri tempUri = null;
1451 FileOutputStream tempStream = null;
1452 try {
1453 File path = mActivity.getFileStreamPath(sTempCropFilename);
1454 path.delete();
1455 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1456 tempStream.write(data);
1457 tempStream.close();
1458 tempUri = Uri.fromFile(path);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001459 Log.v(TAG, "wrote temp file for cropping to: " + sTempCropFilename);
Michael Kolb8872c232013-01-29 10:33:22 -08001460 } catch (FileNotFoundException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001461 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001462 mActivity.setResultEx(Activity.RESULT_CANCELED);
1463 mActivity.finish();
1464 return;
1465 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001466 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001467 mActivity.setResultEx(Activity.RESULT_CANCELED);
1468 mActivity.finish();
1469 return;
1470 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001471 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001472 }
1473
1474 Bundle newExtras = new Bundle();
1475 if (mCropValue.equals("circle")) {
1476 newExtras.putString("circleCrop", "true");
1477 }
1478 if (mSaveUri != null) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001479 Log.v(TAG, "setting output of cropped file to: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001480 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1481 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001482 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001483 }
1484 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001485 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001486 }
1487
Sascha Haeberling37f36112013-08-06 14:31:52 -07001488 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001489 final String CROP_ACTION = "com.android.camera.action.CROP";
1490 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001491
1492 cropIntent.setData(tempUri);
1493 cropIntent.putExtras(newExtras);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001494 Log.v(TAG, "starting CROP intent for capture");
Michael Kolb8872c232013-01-29 10:33:22 -08001495 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1496 }
1497 }
1498
Michael Kolb8872c232013-01-29 10:33:22 -08001499 @Override
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001500 public void onShutterCoordinate(TouchCoordinate coord) {
1501 mShutterTouchCoordinate = coord;
1502 }
1503
1504 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001505 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001506 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001507 }
1508
1509 @Override
1510 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001511 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Spike Sprague8d1ac992014-10-13 14:49:06 -07001512 || (mCameraState == PREVIEW_STOPPED)
1513 || !mAppController.isShutterEnabled()) {
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001514 mVolumeButtonClickedFlag = false;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001515 return;
1516 }
Michael Kolb8872c232013-01-29 10:33:22 -08001517
1518 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001519 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001520 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001521 + mActivity.getStorageSpaceBytes());
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001522 mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001523 return;
1524 }
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001525 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
1526 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
Michael Kolb8872c232013-01-29 10:33:22 -08001527
Spike Sprague8d1ac992014-10-13 14:49:06 -07001528 mAppController.setShutterEnabled(false);
1529
Erin Dahlgren6190c362014-06-13 14:12:08 -07001530 int countDownDuration = mActivity.getSettingsManager()
1531 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
Andy Huibers547d7c82014-05-20 23:03:18 -07001532 mTimerDuration = countDownDuration;
Doris Liu6c751642014-05-05 18:43:26 -07001533 if (countDownDuration > 0) {
1534 // Start count down.
1535 mAppController.getCameraAppUI().transitionToCancel();
1536 mAppController.getCameraAppUI().hideModeOptions();
1537 mUI.startCountdown(countDownDuration);
1538 return;
1539 } else {
1540 focusAndCapture();
1541 }
1542 }
1543
1544 private void focusAndCapture() {
Angus Kong6607dae2014-06-10 16:07:45 -07001545 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001546 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001547 }
Michael Kolb8872c232013-01-29 10:33:22 -08001548 // If the user wants to do a snapshot while the previous one is still
1549 // in progress, remember the fact and do it after we finish the previous
1550 // one and re-start the preview. Snapshot in progress also includes the
1551 // state that autofocus is focusing and a picture will be taken when
1552 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001553 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1554 if (!mIsImageCaptureIntent) {
1555 mSnapshotOnIdle = true;
1556 }
Michael Kolb8872c232013-01-29 10:33:22 -08001557 return;
1558 }
1559
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001560 mSnapshotOnIdle = false;
Angus Kong831347d2014-06-16 16:07:28 -07001561 mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001562 }
1563
Doris Liu6c751642014-05-05 18:43:26 -07001564 @Override
1565 public void onRemainingSecondsChanged(int remainingSeconds) {
Andy Huibersa31162c2014-09-02 11:27:11 -07001566 if (remainingSeconds == 1) {
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001567 mCountdownSoundPlayer.play(R.raw.timer_final_second, 0.6f);
Andy Huibersa31162c2014-09-02 11:27:11 -07001568 } else if (remainingSeconds == 2 || remainingSeconds == 3) {
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001569 mCountdownSoundPlayer.play(R.raw.timer_increment, 0.6f);
Andy Huibersa31162c2014-09-02 11:27:11 -07001570 }
Doris Liu6c751642014-05-05 18:43:26 -07001571 }
1572
1573 @Override
1574 public void onCountDownFinished() {
Alan Newberger7dabda62014-10-15 15:28:01 -07001575 mAppController.getCameraAppUI().transitionToCapture();
Doris Liu6c751642014-05-05 18:43:26 -07001576 mAppController.getCameraAppUI().showModeOptions();
1577 if (mPaused) {
1578 return;
1579 }
1580 focusAndCapture();
1581 }
1582
Puneet Lall4de9b722014-09-25 15:05:47 -07001583 @Override
1584 public void resume() {
1585 mPaused = false;
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001586
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001587 mCountdownSoundPlayer.loadSound(R.raw.timer_final_second);
1588 mCountdownSoundPlayer.loadSound(R.raw.timer_increment);
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001589 if (mFocusManager != null) {
1590 // If camera is not open when resume is called, focus manager will
1591 // not be initialized yet, in which case it will start listening to
1592 // preview area size change later in the initialization.
1593 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1594 }
1595 mAppController.addPreviewAreaSizeChangedListener(mUI);
1596
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001597 CameraProvider camProvider = mActivity.getCameraProvider();
1598 if (camProvider == null) {
1599 // No camera provider, the Activity is destroyed already.
1600 return;
1601 }
Sol Boucher44ce4b22014-08-04 23:41:38 -07001602 requestCameraOpen();
Angus Kong20fad242013-11-11 18:23:46 -08001603
Michael Kolb8872c232013-01-29 10:33:22 -08001604 mJpegPictureCallbackTime = 0;
Sol Boucher2192fba2014-08-19 17:24:07 -07001605 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001606
1607 mOnResumeTime = SystemClock.uptimeMillis();
1608 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001609
1610 // If first time initialization is not finished, put it in the
1611 // message queue.
1612 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001613 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001614 } else {
1615 initializeSecondTime();
1616 }
Michael Kolb8872c232013-01-29 10:33:22 -08001617
Angus Kong0d00a892013-03-26 11:40:40 -07001618 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1619 if (gsensor != null) {
1620 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1621 }
1622
1623 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1624 if (msensor != null) {
1625 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1626 }
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001627
1628 getServices().getRemoteShutterListener().onModuleReady(this);
1629 SessionStatsCollector.instance().sessionActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001630 }
1631
Angus Kongc4e66562013-11-22 23:03:21 -08001632 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001633 * @return Whether the currently active camera is front-facing.
1634 */
1635 private boolean isCameraFrontFacing() {
Sol Boucher43e18132014-06-19 15:03:18 -07001636 return mAppController.getCameraProvider().getCharacteristics(mCameraId)
1637 .isFacingFront();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001638 }
1639
1640 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001641 * The focus manager is the first UI related element to get initialized, and
1642 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001643 */
1644 private void initializeFocusManager() {
1645 // Create FocusManager object. startPreview needs it.
1646 // if mFocusManager not null, reuse it
1647 // otherwise create a new instance
1648 if (mFocusManager != null) {
1649 mFocusManager.removeMessages();
1650 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001651 mMirror = isCameraFrontFacing();
Angus Kong831347d2014-06-16 16:07:28 -07001652 String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
Angus Kongc4e66562013-11-22 23:03:21 -08001653 R.array.pref_camera_focusmode_default_array);
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -07001654 ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
1655 new ArrayList<CameraCapabilities.FocusMode>();
Angus Kong831347d2014-06-16 16:07:28 -07001656 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
1657 for (String modeString : defaultFocusModesStrings) {
1658 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
1659 if (mode != null) {
1660 defaultFocusModes.add(mode);
1661 }
1662 }
1663 mFocusManager =
1664 new FocusOverlayManager(mAppController, defaultFocusModes,
1665 mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
1666 mUI.getFocusUI());
Sascha Haeberling375f9d12014-10-17 19:05:12 -07001667 mMotionManager = getServices().getMotionManager();
1668 if (mMotionManager != null) {
1669 mMotionManager.addListener(mFocusManager);
Kevin Gabayanfb333362014-06-02 14:48:20 -07001670 }
Angus Kongc4e66562013-11-22 23:03:21 -08001671 }
Doris Liu482de022013-12-18 19:18:16 -08001672 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001673 }
1674
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001675 /**
1676 * @return Whether we are resuming from within the lockscreen.
1677 */
1678 private boolean isResumeFromLockscreen() {
1679 String action = mActivity.getIntent().getAction();
1680 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1681 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
1682 }
1683
Angus Kong20fad242013-11-11 18:23:46 -08001684 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001685 public void pause() {
Alan Newberger7dabda62014-10-15 15:28:01 -07001686 Log.v(TAG, "pause");
Michael Kolb8872c232013-01-29 10:33:22 -08001687 mPaused = true;
Sascha Haeberling1bf08892014-06-18 09:38:06 -07001688 getServices().getRemoteShutterListener().onModuleExit();
Andy Huibers10c58162014-03-29 14:06:54 -07001689 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001690
Angus Kong0d00a892013-03-26 11:40:40 -07001691 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1692 if (gsensor != null) {
1693 mSensorManager.unregisterListener(this, gsensor);
1694 }
1695
1696 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1697 if (msensor != null) {
1698 mSensorManager.unregisterListener(this, msensor);
1699 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001700
Michael Kolb8872c232013-01-29 10:33:22 -08001701 // Reset the focus first. Camera CTS does not guarantee that
1702 // cancelAutoFocus is allowed after preview stops.
1703 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1704 mCameraDevice.cancelAutoFocus();
1705 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001706
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001707 // If the camera has not been opened asynchronously yet,
1708 // and startPreview hasn't been called, then this is a no-op.
1709 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001710 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001711 cancelCountDown();
Andy Huibers877c2b62014-10-03 15:22:47 -07001712 mCountdownSoundPlayer.unloadSound(R.raw.timer_final_second);
1713 mCountdownSoundPlayer.unloadSound(R.raw.timer_increment);
Michael Kolb8872c232013-01-29 10:33:22 -08001714
Angus Kongce5480e2013-01-29 17:43:48 -08001715 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001716 // If we are in an image capture intent and has taken
1717 // a picture, we just clear it in onPause.
1718 mJpegImageData = null;
1719
Angus Kongdcccc512013-08-08 17:06:03 -07001720 // Remove the messages and runnables in the queue.
1721 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001722
Sascha Haeberling375f9d12014-10-17 19:05:12 -07001723 if (mMotionManager != null) {
1724 mMotionManager.removeListener(mFocusManager);
1725 mMotionManager = null;
1726 }
1727
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001728 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001729 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001730 mUI.onPause();
1731
Michael Kolb8872c232013-01-29 10:33:22 -08001732 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001733 if (mFocusManager != null) {
1734 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001735 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001736 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001737 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001738 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001739
1740 SettingsManager settingsManager = mActivity.getSettingsManager();
1741 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001742 }
1743
Angus Kong20fad242013-11-11 18:23:46 -08001744 @Override
1745 public void destroy() {
Andy Huibers877c2b62014-10-03 15:22:47 -07001746 mCountdownSoundPlayer.release();
Angus Kong20fad242013-11-11 18:23:46 -08001747 }
1748
1749 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001750 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001751 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001752 }
1753
1754 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001755 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001756 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001757 setDisplayOrientation();
1758 }
1759 }
1760
Michael Kolb8872c232013-01-29 10:33:22 -08001761 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001762 return isCameraIdle()
1763 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001764 }
1765
1766 @Override
1767 public void autoFocus() {
Senpo Hu4ba28292014-09-26 11:05:08 -07001768 if (mCameraDevice == null) {
1769 return;
1770 }
Andy Huibers10c58162014-03-29 14:06:54 -07001771 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001772 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001773 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001774 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001775 setCameraState(FOCUSING);
1776 }
1777
1778 @Override
1779 public void cancelAutoFocus() {
Senpo Hu4ba28292014-09-26 11:05:08 -07001780 if (mCameraDevice == null) {
1781 return;
1782 }
Michael Kolb8872c232013-01-29 10:33:22 -08001783 mCameraDevice.cancelAutoFocus();
1784 setCameraState(IDLE);
1785 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1786 }
1787
Michael Kolb8872c232013-01-29 10:33:22 -08001788 @Override
1789 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001790 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1791 || mCameraState == SNAPSHOT_IN_PROGRESS
1792 || mCameraState == SWITCHING_CAMERA
1793 || mCameraState == PREVIEW_STOPPED) {
1794 return;
1795 }
1796
1797 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001798 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1799 return;
1800 }
Doris Liu482de022013-12-18 19:18:16 -08001801 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001802 }
1803
1804 @Override
1805 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001806 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001807 }
1808
1809 @Override
1810 public boolean onKeyDown(int keyCode, KeyEvent event) {
1811 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001812 case KeyEvent.KEYCODE_VOLUME_UP:
1813 case KeyEvent.KEYCODE_VOLUME_DOWN:
1814 case KeyEvent.KEYCODE_FOCUS:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001815 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1816 !mActivity.getCameraAppUI().isInIntentReview()) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001817 if (event.getRepeatCount() == 0) {
1818 onShutterButtonFocus(true);
1819 }
1820 return true;
1821 }
1822 return false;
1823 case KeyEvent.KEYCODE_CAMERA:
1824 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1825 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001826 }
1827 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001828 case KeyEvent.KEYCODE_DPAD_CENTER:
1829 // If we get a dpad center event without any focused view, move
1830 // the focus to the shutter button and press it.
1831 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1832 // Start auto-focus immediately to reduce shutter lag. After
1833 // the shutter button gets the focus, onShutterButtonFocus()
1834 // will be called again but it is fine.
1835 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001836 }
1837 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001838 }
1839 return false;
1840 }
1841
1842 @Override
1843 public boolean onKeyUp(int keyCode, KeyEvent event) {
1844 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001845 case KeyEvent.KEYCODE_VOLUME_UP:
1846 case KeyEvent.KEYCODE_VOLUME_DOWN:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001847 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1848 !mActivity.getCameraAppUI().isInIntentReview()) {
1849 if (mUI.isCountingDown()) {
1850 cancelCountDown();
1851 } else {
1852 mVolumeButtonClickedFlag = true;
1853 onShutterButtonClick();
1854 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001855 return true;
1856 }
1857 return false;
1858 case KeyEvent.KEYCODE_FOCUS:
1859 if (mFirstTimeInitialized) {
1860 onShutterButtonFocus(false);
1861 }
Michael Kolb8872c232013-01-29 10:33:22 -08001862 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001863 }
1864 return false;
1865 }
1866
Michael Kolb8872c232013-01-29 10:33:22 -08001867 private void closeCamera() {
1868 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001869 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001870 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001871 mCameraDevice.setFaceDetectionCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001872
Michael Kolb8872c232013-01-29 10:33:22 -08001873 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001874 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001875 mCameraDevice = null;
1876 setCameraState(PREVIEW_STOPPED);
1877 mFocusManager.onCameraReleased();
1878 }
1879 }
1880
1881 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001882 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
Sol Boucher43e18132014-06-19 15:03:18 -07001883 Characteristics info =
1884 mActivity.getCameraProvider().getCharacteristics(mCameraId);
Sol Boucher73ec9852014-07-24 23:59:21 -07001885 mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
Michael Kolbd6954f32013-03-08 20:43:01 -08001886 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001887 if (mFocusManager != null) {
1888 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1889 }
Doris Liu6432cd62013-06-13 17:20:31 -07001890 // Change the camera display orientation
1891 if (mCameraDevice != null) {
Sol Boucher73ec9852014-07-24 23:59:21 -07001892 mCameraDevice.setDisplayOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001893 }
Alan Newbergere9f7b2d2014-10-22 17:42:47 -07001894 Log.v(TAG, "setDisplayOrientation (screen:preview) " +
1895 mDisplayRotation + ":" + mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001896 }
1897
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001898 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001899 private void setupPreview() {
Alan Newberger19597372014-10-03 18:27:50 -07001900 Log.i(TAG, "setupPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001901 mFocusManager.resetTouchFocus();
1902 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001903 }
1904
Angus Kong20fad242013-11-11 18:23:46 -08001905 /**
1906 * Returns whether we can/should start the preview or not.
1907 */
1908 private boolean checkPreviewPreconditions() {
1909 if (mPaused) {
1910 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001911 }
Michael Kolb8872c232013-01-29 10:33:22 -08001912
Angus Kong20fad242013-11-11 18:23:46 -08001913 if (mCameraDevice == null) {
1914 Log.w(TAG, "startPreview: camera device not ready yet.");
1915 return false;
1916 }
1917
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001918 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001919 if (st == null) {
1920 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001921 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001922 }
1923
1924 if (!mCameraPreviewParamsReady) {
1925 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001926 return false;
1927 }
1928 return true;
1929 }
1930
1931 /**
1932 * The start/stop preview should only run on the UI thread.
1933 */
1934 private void startPreview() {
Alan Newberger19597372014-10-03 18:27:50 -07001935 if (mCameraDevice == null) {
1936 Log.i(TAG, "attempted to start preview before camera device");
Puneet Lall70a96522014-09-11 17:33:21 -07001937 // do nothing
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001938 return;
1939 }
Alan Newberger19597372014-10-03 18:27:50 -07001940
1941 if (!checkPreviewPreconditions()) {
1942 return;
1943 }
1944
Alan Newberger19597372014-10-03 18:27:50 -07001945 setDisplayOrientation();
1946
1947 if (!mSnapshotOnIdle) {
1948 // If the focus mode is continuous autofocus, call cancelAutoFocus
1949 // to resume it because it may have been paused by autoFocus call.
1950 if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
1951 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
1952 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001953 }
Alan Newberger19597372014-10-03 18:27:50 -07001954 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1955 }
Michael Kolb8872c232013-01-29 10:33:22 -08001956
Alan Newberger5a64d112014-11-03 22:44:14 -08001957 // Nexus 4 must have picture size set to > 640x480 before other
1958 // parameters are set in setCameraParameters, b/18227551. This call to
1959 // updateParametersPictureSize should occur before setCameraParameters
1960 // to address the issue.
Alan Newberger19597372014-10-03 18:27:50 -07001961 updateParametersPictureSize();
Angus Kong20fad242013-11-11 18:23:46 -08001962
Alan Newberger5a64d112014-11-03 22:44:14 -08001963 setCameraParameters(UPDATE_PARAM_ALL);
1964
Alan Newberger19597372014-10-03 18:27:50 -07001965 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Puneet Lall70a96522014-09-11 17:33:21 -07001966
Alan Newberger19597372014-10-03 18:27:50 -07001967 Log.i(TAG, "startPreview");
1968 // If we're using API2 in portability layers, don't use startPreviewWithCallback()
1969 // b/17576554
1970 CameraAgent.CameraStartPreviewCallback startPreviewCallback =
1971 new CameraAgent.CameraStartPreviewCallback() {
1972 @Override
1973 public void onPreviewStarted() {
1974 mFocusManager.onPreviewStarted();
1975 PhotoModule.this.onPreviewStarted();
1976 SessionStatsCollector.instance().previewActive(true);
1977 if (mSnapshotOnIdle) {
1978 mHandler.post(mDoSnapRunnable);
Spike Sprague3c3b31d2014-09-08 10:43:04 -07001979 }
Alan Newberger19597372014-10-03 18:27:50 -07001980 }
1981 };
1982 if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity)) {
1983 mCameraDevice.startPreview();
1984 startPreviewCallback.onPreviewStarted();
1985 } else {
1986 mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
1987 startPreviewCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001988 }
1989 }
1990
Michael Kolbd6954f32013-03-08 20:43:01 -08001991 @Override
1992 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001993 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001994 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001995 mCameraDevice.stopPreview();
1996 mFaceDetectionStarted = false;
1997 }
1998 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001999 if (mFocusManager != null) {
2000 mFocusManager.onPreviewStopped();
2001 }
Andy Huibers10c58162014-03-29 14:06:54 -07002002 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002003 }
2004
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002005 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -07002006 public void onSettingChanged(SettingsManager settingsManager, String key) {
2007 if (key.equals(Keys.KEY_FLASH_MODE)) {
2008 updateParametersFlashMode();
2009 }
2010 if (key.equals(Keys.KEY_CAMERA_HDR)) {
2011 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2012 Keys.KEY_CAMERA_HDR)) {
2013 // HDR is on.
2014 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
2015 mFlashModeBeforeSceneMode = settingsManager.getString(
2016 mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
2017 } else {
2018 if (mFlashModeBeforeSceneMode != null) {
2019 settingsManager.set(mAppController.getCameraScope(),
2020 Keys.KEY_FLASH_MODE,
2021 mFlashModeBeforeSceneMode);
2022 updateParametersFlashMode();
2023 mFlashModeBeforeSceneMode = null;
Angus Kong8c07fe92014-05-22 14:42:45 -07002024 }
Erin Dahlgren6190c362014-06-13 14:12:08 -07002025 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002026 }
2027 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08002028
2029 if (mCameraDevice != null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002030 mCameraDevice.applySettings(mCameraSettings);
Erin Dahlgren1648c362014-01-06 15:06:04 -08002031 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002032 }
2033
Michael Kolb8872c232013-01-29 10:33:22 -08002034 private void updateCameraParametersInitialize() {
2035 // Reset preview frame rate to the maximum because it may be lowered by
2036 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07002037 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07002038 if (fpsRange != null && fpsRange.length > 0) {
Angus Kong831347d2014-06-16 16:07:28 -07002039 mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
Michael Kolb8872c232013-01-29 10:33:22 -08002040 }
2041
Angus Kong831347d2014-06-16 16:07:28 -07002042 mCameraSettings.setRecordingHintEnabled(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002043
Angus Kong6607dae2014-06-10 16:07:45 -07002044 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
2045 mCameraSettings.setVideoStabilization(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002046 }
2047 }
2048
2049 private void updateCameraParametersZoom() {
2050 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07002051 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002052 mCameraSettings.setZoomRatio(mZoomValue);
Michael Kolb8872c232013-01-29 10:33:22 -08002053 }
2054 }
2055
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002056 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002057 private void setAutoExposureLockIfSupported() {
2058 if (mAeLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002059 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002060 }
2061 }
2062
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002063 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002064 private void setAutoWhiteBalanceLockIfSupported() {
2065 if (mAwbLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002066 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002067 }
2068 }
2069
Michael Kolb8872c232013-01-29 10:33:22 -08002070 private void setFocusAreasIfSupported() {
2071 if (mFocusAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002072 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002073 }
2074 }
2075
Michael Kolb8872c232013-01-29 10:33:22 -08002076 private void setMeteringAreasIfSupported() {
2077 if (mMeteringAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002078 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002079 }
2080 }
2081
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002082 private void updateCameraParametersPreference() {
Spike Spragueae56c122014-09-19 11:18:46 -07002083 // some monkey tests can get here when shutting the app down
2084 // make sure mCameraDevice is still valid, b/17580046
2085 if (mCameraDevice == null) {
2086 return;
2087 }
2088
Michael Kolb8872c232013-01-29 10:33:22 -08002089 setAutoExposureLockIfSupported();
2090 setAutoWhiteBalanceLockIfSupported();
2091 setFocusAreasIfSupported();
2092 setMeteringAreasIfSupported();
2093
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002094 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07002095 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002096 mCameraSettings
2097 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Andy Huibers10c58162014-03-29 14:06:54 -07002098 SessionStatsCollector.instance().autofocusActive(
Angus Kong831347d2014-06-16 16:07:28 -07002099 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
2100 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
2101 );
Michael Kolbd3253f22013-07-12 11:36:47 -07002102
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002103 // Set JPEG quality.
2104 updateParametersPictureQuality();
2105
2106 // For the following settings, we need to check if the settings are
2107 // still supported by latest driver, if not, ignore the settings.
2108
2109 // Set exposure compensation
2110 updateParametersExposureCompensation();
2111
2112 // Set the scene mode: also sets flash and white balance.
2113 updateParametersSceneMode();
2114
2115 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2116 updateAutoFocusMoveCallback();
2117 }
2118 }
2119
Alan Newberger19597372014-10-03 18:27:50 -07002120 /**
2121 * This method sets picture size parameters. Size parameters should only be
2122 * set when the preview is stopped, and so this method is only invoked in
2123 * {@link #startPreview()} just before starting the preview.
2124 */
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002125 private void updateParametersPictureSize() {
Senpo Huee3123b2014-09-25 10:53:12 -07002126 if (mCameraDevice == null) {
Alan Newberger19597372014-10-03 18:27:50 -07002127 Log.w(TAG, "attempting to set picture size without caemra device");
Senpo Huee3123b2014-09-25 10:53:12 -07002128 return;
2129 }
2130
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002131 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002132 String pictureSizeKey = isCameraFrontFacing() ? Keys.KEY_PICTURE_SIZE_FRONT
2133 : Keys.KEY_PICTURE_SIZE_BACK;
2134 String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL,
2135 pictureSizeKey);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01002136
Angus Kong6607dae2014-06-10 16:07:45 -07002137 List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes();
Andy Huibers90c7ad52014-05-20 22:13:54 -07002138 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2139 mCameraDevice.getCameraId(), supported);
Angus Kong6607dae2014-06-10 16:07:45 -07002140 SettingsUtil.setCameraPictureSize(pictureSize, supported, mCameraSettings,
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002141 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07002142
2143 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2144 mCameraDevice.getCameraId());
2145 if (ApiHelper.IS_NEXUS_5) {
2146 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2147 mShouldResizeTo16x9 = true;
2148 } else {
2149 mShouldResizeTo16x9 = false;
2150 }
2151 }
Michael Kolb8872c232013-01-29 10:33:22 -08002152
2153 // Set a preview size that is closest to the viewfinder height and has
2154 // the right aspect ratio.
Angus Kong6607dae2014-06-10 16:07:45 -07002155 List<Size> sizes = mCameraCapabilities.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07002156 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002157 (double) size.width() / size.height());
Angus Kong6607dae2014-06-10 16:07:45 -07002158 Size original = mCameraSettings.getCurrentPreviewSize();
2159 if (!optimalSize.equals(original)) {
Alan Newberger2631a152014-09-24 14:23:30 -07002160 Log.v(TAG, "setting preview size. optimal: " + optimalSize + "original: " + original);
Angus Kong6607dae2014-06-10 16:07:45 -07002161 mCameraSettings.setPreviewSize(optimalSize);
Doris Liu6432cd62013-06-13 17:20:31 -07002162
Alan Newberger19597372014-10-03 18:27:50 -07002163 mCameraDevice.applySettings(mCameraSettings);
Angus Kong6607dae2014-06-10 16:07:45 -07002164 mCameraSettings = mCameraDevice.getSettings();
Michael Kolb8872c232013-01-29 10:33:22 -08002165 }
Doris Liu95405742013-11-05 15:25:26 -08002166
Angus Kong00b7b102014-04-24 15:46:52 -07002167 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
Alan Newberger73514152014-09-10 15:03:27 -07002168 Log.v(TAG, "updating aspect ratio");
Angus Kong00b7b102014-04-24 15:46:52 -07002169 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2170 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002171 }
Alan Newberger73514152014-09-10 15:03:27 -07002172 Log.d(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002173 }
Michael Kolb8872c232013-01-29 10:33:22 -08002174
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002175 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002176 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2177 CameraProfile.QUALITY_HIGH);
Angus Kong6607dae2014-06-10 16:07:45 -07002178 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002179 }
Michael Kolb8872c232013-01-29 10:33:22 -08002180
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002181 private void updateParametersExposureCompensation() {
2182 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002183 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2184 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2185 int value = settingsManager.getInteger(mAppController.getCameraScope(),
2186 Keys.KEY_EXPOSURE);
Angus Kong88289042014-04-22 16:39:42 -07002187 int max = mCameraCapabilities.getMaxExposureCompensation();
2188 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002189 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002190 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002191 } else {
2192 Log.w(TAG, "invalid exposure range: " + value);
2193 }
Michael Kolb8872c232013-01-29 10:33:22 -08002194 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002195 // If exposure compensation is not enabled, reset the exposure compensation value.
2196 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002197 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002198
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002199 }
2200
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002201 private void updateParametersSceneMode() {
Angus Kong6607dae2014-06-10 16:07:45 -07002202 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002203 SettingsManager settingsManager = mActivity.getSettingsManager();
2204
Erin Dahlgren6190c362014-06-13 14:12:08 -07002205 mSceneMode = stringifier.
2206 sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2207 Keys.KEY_SCENE_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002208 if (mCameraCapabilities.supports(mSceneMode)) {
2209 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
2210 mCameraSettings.setSceneMode(mSceneMode);
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002211
2212 // Setting scene mode will change the settings of flash mode,
2213 // white balance, and focus mode. Here we read back the
2214 // parameters, so we can know those settings.
Angus Kong6607dae2014-06-10 16:07:45 -07002215 mCameraDevice.applySettings(mCameraSettings);
2216 mCameraSettings = mCameraDevice.getSettings();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002217 }
2218 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002219 mSceneMode = mCameraSettings.getCurrentSceneMode();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002220 if (mSceneMode == null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002221 mSceneMode = CameraCapabilities.SceneMode.AUTO;
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002222 }
2223 }
2224
Angus Kong6607dae2014-06-10 16:07:45 -07002225 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
Michael Kolb8872c232013-01-29 10:33:22 -08002226 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002227 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002228
Michael Kolb8872c232013-01-29 10:33:22 -08002229 // Set focus mode.
2230 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002231 mCameraSettings.setFocusMode(
2232 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Michael Kolb8872c232013-01-29 10:33:22 -08002233 } else {
Angus Kong831347d2014-06-16 16:07:28 -07002234 mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08002235 }
Michael Kolb8872c232013-01-29 10:33:22 -08002236 }
2237
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002238 private void updateParametersFlashMode() {
2239 SettingsManager settingsManager = mActivity.getSettingsManager();
2240
Angus Kong6607dae2014-06-10 16:07:45 -07002241 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
Erin Dahlgren6190c362014-06-13 14:12:08 -07002242 .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2243 Keys.KEY_FLASH_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002244 if (mCameraCapabilities.supports(flashMode)) {
2245 mCameraSettings.setFlashMode(flashMode);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002246 }
2247 }
2248
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002249 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002250 private void updateAutoFocusMoveCallback() {
Senpo Huee3123b2014-09-25 10:53:12 -07002251 if (mCameraDevice == null) {
2252 return;
2253 }
Angus Kong6607dae2014-06-10 16:07:45 -07002254 if (mCameraSettings.getCurrentFocusMode() ==
2255 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
Angus Kong9ef99252013-07-18 18:04:19 -07002256 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002257 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002258 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002259 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002260 }
2261 }
2262
Spike Spragueabf54e22014-03-27 15:41:28 -07002263 /**
2264 * Sets the exposure compensation to the given value and also updates settings.
2265 *
2266 * @param value exposure compensation value to be set
2267 */
2268 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002269 int max = mCameraCapabilities.getMaxExposureCompensation();
2270 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002271 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002272 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002273 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002274 settingsManager.set(mAppController.getCameraScope(),
2275 Keys.KEY_EXPOSURE, value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002276 } else {
2277 Log.w(TAG, "invalid exposure range: " + value);
2278 }
2279 }
2280
Michael Kolb8872c232013-01-29 10:33:22 -08002281 // We separate the parameters into several subsets, so we can update only
2282 // the subsets actually need updating. The PREFERENCE set needs extra
2283 // locking because the preference can be changed from GLThread as well.
2284 private void setCameraParameters(int updateSet) {
2285 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2286 updateCameraParametersInitialize();
2287 }
2288
2289 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2290 updateCameraParametersZoom();
2291 }
2292
2293 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002294 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002295 }
2296
Spike Sprague5cc48d62014-09-22 10:52:32 -07002297 if (mCameraDevice != null) {
2298 mCameraDevice.applySettings(mCameraSettings);
2299 }
Michael Kolb8872c232013-01-29 10:33:22 -08002300 }
2301
2302 // If the Camera is idle, update the parameters immediately, otherwise
2303 // accumulate them in mUpdateSet and update later.
2304 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2305 mUpdateSet |= additionalUpdateSet;
2306 if (mCameraDevice == null) {
2307 // We will update all the parameters when we open the device, so
2308 // we don't need to do anything now.
2309 mUpdateSet = 0;
2310 return;
2311 } else if (isCameraIdle()) {
2312 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002313 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002314 mUpdateSet = 0;
2315 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002316 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2317 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002318 }
2319 }
2320 }
2321
ztenghui7b265a62013-09-09 14:58:44 -07002322 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002323 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002324 return (mCameraState == IDLE) ||
2325 (mCameraState == PREVIEW_STOPPED) ||
2326 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002327 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002328 }
2329
ztenghui7b265a62013-09-09 14:58:44 -07002330 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002331 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002332 String action = mActivity.getIntent().getAction();
2333 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002334 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002335 }
2336
2337 private void setupCaptureParams() {
2338 Bundle myExtras = mActivity.getIntent().getExtras();
2339 if (myExtras != null) {
2340 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2341 mCropValue = myExtras.getString("crop");
2342 }
2343 }
2344
Michael Kolb8872c232013-01-29 10:33:22 -08002345 private void initializeCapabilities() {
Angus Kong88289042014-04-22 16:39:42 -07002346 mCameraCapabilities = mCameraDevice.getCapabilities();
2347 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2348 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2349 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2350 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kong6607dae2014-06-10 16:07:45 -07002351 mContinuousFocusSupported =
2352 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002353 }
2354
Michael Kolb8872c232013-01-29 10:33:22 -08002355 @Override
Sol Boucher2192fba2014-08-19 17:24:07 -07002356 public void onZoomChanged(float ratio) {
Michael Kolbd6954f32013-03-08 20:43:01 -08002357 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002358 if (mPaused) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002359 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002360 }
Sol Boucher2192fba2014-08-19 17:24:07 -07002361 mZoomValue = ratio;
Angus Kong6607dae2014-06-10 16:07:45 -07002362 if (mCameraSettings == null || mCameraDevice == null) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002363 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002364 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002365 // Set zoom parameters asynchronously
Sol Boucher2192fba2014-08-19 17:24:07 -07002366 mCameraSettings.setZoomRatio(mZoomValue);
Angus Kong6607dae2014-06-10 16:07:45 -07002367 mCameraDevice.applySettings(mCameraSettings);
Angus Kongce5480e2013-01-29 17:43:48 -08002368 }
2369
2370 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002371 public int getCameraState() {
2372 return mCameraState;
2373 }
2374
2375 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002376 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002377 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002378 }
Angus Kong86d36312013-01-31 18:22:44 -08002379
2380 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002381 public void onLowMemory() {
2382 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002383 }
Angus Kong0d00a892013-03-26 11:40:40 -07002384
2385 @Override
2386 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2387 }
2388
2389 @Override
2390 public void onSensorChanged(SensorEvent event) {
2391 int type = event.sensor.getType();
2392 float[] data;
2393 if (type == Sensor.TYPE_ACCELEROMETER) {
2394 data = mGData;
2395 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2396 data = mMData;
2397 } else {
2398 // we should not be here.
2399 return;
2400 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002401 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002402 data[i] = event.values[i];
2403 }
2404 float[] orientation = new float[3];
2405 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2406 SensorManager.getOrientation(mR, orientation);
2407 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2408 if (mHeading < 0) {
2409 mHeading += 360;
2410 }
Angus Kong0d00a892013-03-26 11:40:40 -07002411 }
Doris Liu6432cd62013-06-13 17:20:31 -07002412
Ruben Brunkd217ed02013-10-08 23:31:13 -07002413 // For debugging only.
2414 public void setDebugUri(Uri uri) {
2415 mDebugUri = uri;
2416 }
2417
2418 // For debugging only.
2419 private void saveToDebugUri(byte[] data) {
2420 if (mDebugUri != null) {
2421 OutputStream outputStream = null;
2422 try {
2423 outputStream = mContentResolver.openOutputStream(mDebugUri);
2424 outputStream.write(data);
2425 outputStream.close();
2426 } catch (IOException e) {
2427 Log.e(TAG, "Exception while writing debug jpeg file", e);
2428 } finally {
2429 CameraUtil.closeSilently(outputStream);
2430 }
2431 }
2432 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002433
2434 @Override
2435 public void onRemoteShutterPress() {
Sascha Haeberling57d4f742014-06-25 11:05:49 -07002436 mHandler.post(new Runnable() {
2437 @Override
2438 public void run() {
2439 focusAndCapture();
2440 }
2441 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002442 }
Michael Kolb8872c232013-01-29 10:33:22 -08002443}